fileio.cpp

Go to the documentation of this file.
00001 /* $Id$ */
00002 
00003 /*
00004  * This file is part of OpenTTD.
00005  * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
00006  * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00007  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
00008  */
00009 
00012 #include "stdafx.h"
00013 #include "fileio_func.h"
00014 #include "debug.h"
00015 #include "fios.h"
00016 #include "string_func.h"
00017 #include "tar_type.h"
00018 #ifdef WIN32
00019 #include <windows.h>
00020 # define access _taccess
00021 #elif defined(__HAIKU__)
00022 #include <Path.h>
00023 #include <storage/FindDirectory.h>
00024 #else
00025 #include <unistd.h>
00026 #include <pwd.h>
00027 #endif
00028 #include <sys/stat.h>
00029 #include <algorithm>
00030 
00032 #define FIO_BUFFER_SIZE 512
00033 
00035 struct Fio {
00036   byte *buffer, *buffer_end;             
00037   size_t pos;                            
00038   FILE *cur_fh;                          
00039   const char *filename;                  
00040   FILE *handles[MAX_FILE_SLOTS];         
00041   byte buffer_start[FIO_BUFFER_SIZE];    
00042   const char *filenames[MAX_FILE_SLOTS]; 
00043   char *shortnames[MAX_FILE_SLOTS];      
00044 #if defined(LIMITED_FDS)
00045   uint open_handles;                     
00046   uint usage_count[MAX_FILE_SLOTS];      
00047 #endif /* LIMITED_FDS */
00048 };
00049 
00050 static Fio _fio; 
00051 
00053 static bool _do_scan_working_directory = true;
00054 
00055 extern char *_config_file;
00056 extern char *_highscore_file;
00057 
00062 size_t FioGetPos()
00063 {
00064   return _fio.pos + (_fio.buffer - _fio.buffer_end);
00065 }
00066 
00072 const char *FioGetFilename(uint8 slot)
00073 {
00074   return _fio.shortnames[slot];
00075 }
00076 
00082 void FioSeekTo(size_t pos, int mode)
00083 {
00084   if (mode == SEEK_CUR) pos += FioGetPos();
00085   _fio.buffer = _fio.buffer_end = _fio.buffer_start + FIO_BUFFER_SIZE;
00086   _fio.pos = pos;
00087   fseek(_fio.cur_fh, _fio.pos, SEEK_SET);
00088 }
00089 
00090 #if defined(LIMITED_FDS)
00091 static void FioRestoreFile(int slot)
00092 {
00093   /* Do we still have the file open, or should we reopen it? */
00094   if (_fio.handles[slot] == NULL) {
00095     DEBUG(misc, 6, "Restoring file '%s' in slot '%d' from disk", _fio.filenames[slot], slot);
00096     FioOpenFile(slot, _fio.filenames[slot]);
00097   }
00098   _fio.usage_count[slot]++;
00099 }
00100 #endif /* LIMITED_FDS */
00101 
00107 void FioSeekToFile(uint8 slot, size_t pos)
00108 {
00109   FILE *f;
00110 #if defined(LIMITED_FDS)
00111   /* Make sure we have this file open */
00112   FioRestoreFile(slot);
00113 #endif /* LIMITED_FDS */
00114   f = _fio.handles[slot];
00115   assert(f != NULL);
00116   _fio.cur_fh = f;
00117   _fio.filename = _fio.filenames[slot];
00118   FioSeekTo(pos, SEEK_SET);
00119 }
00120 
00125 byte FioReadByte()
00126 {
00127   if (_fio.buffer == _fio.buffer_end) {
00128     _fio.buffer = _fio.buffer_start;
00129     size_t size = fread(_fio.buffer, 1, FIO_BUFFER_SIZE, _fio.cur_fh);
00130     _fio.pos += size;
00131     _fio.buffer_end = _fio.buffer_start + size;
00132 
00133     if (size == 0) return 0;
00134   }
00135   return *_fio.buffer++;
00136 }
00137 
00142 void FioSkipBytes(int n)
00143 {
00144   for (;;) {
00145     int m = min(_fio.buffer_end - _fio.buffer, n);
00146     _fio.buffer += m;
00147     n -= m;
00148     if (n == 0) break;
00149     FioReadByte();
00150     n--;
00151   }
00152 }
00153 
00158 uint16 FioReadWord()
00159 {
00160   byte b = FioReadByte();
00161   return (FioReadByte() << 8) | b;
00162 }
00163 
00168 uint32 FioReadDword()
00169 {
00170   uint b = FioReadWord();
00171   return (FioReadWord() << 16) | b;
00172 }
00173 
00179 void FioReadBlock(void *ptr, size_t size)
00180 {
00181   FioSeekTo(FioGetPos(), SEEK_SET);
00182   _fio.pos += fread(ptr, 1, size, _fio.cur_fh);
00183 }
00184 
00189 static inline void FioCloseFile(int slot)
00190 {
00191   if (_fio.handles[slot] != NULL) {
00192     fclose(_fio.handles[slot]);
00193 
00194     free(_fio.shortnames[slot]);
00195     _fio.shortnames[slot] = NULL;
00196 
00197     _fio.handles[slot] = NULL;
00198 #if defined(LIMITED_FDS)
00199     _fio.open_handles--;
00200 #endif /* LIMITED_FDS */
00201   }
00202 }
00203 
00205 void FioCloseAll()
00206 {
00207   for (int i = 0; i != lengthof(_fio.handles); i++) {
00208     FioCloseFile(i);
00209   }
00210 }
00211 
00212 #if defined(LIMITED_FDS)
00213 static void FioFreeHandle()
00214 {
00215   /* If we are about to open a file that will exceed the limit, close a file */
00216   if (_fio.open_handles + 1 == LIMITED_FDS) {
00217     uint i, count;
00218     int slot;
00219 
00220     count = UINT_MAX;
00221     slot = -1;
00222     /* Find the file that is used the least */
00223     for (i = 0; i < lengthof(_fio.handles); i++) {
00224       if (_fio.handles[i] != NULL && _fio.usage_count[i] < count) {
00225         count = _fio.usage_count[i];
00226         slot  = i;
00227       }
00228     }
00229     assert(slot != -1);
00230     DEBUG(misc, 6, "Closing filehandler '%s' in slot '%d' because of fd-limit", _fio.filenames[slot], slot);
00231     FioCloseFile(slot);
00232   }
00233 }
00234 #endif /* LIMITED_FDS */
00235 
00242 void FioOpenFile(int slot, const char *filename, Subdirectory subdir)
00243 {
00244   FILE *f;
00245 
00246 #if defined(LIMITED_FDS)
00247   FioFreeHandle();
00248 #endif /* LIMITED_FDS */
00249   f = FioFOpenFile(filename, "rb", subdir);
00250   if (f == NULL) usererror("Cannot open file '%s'", filename);
00251   uint32 pos = ftell(f);
00252 
00253   FioCloseFile(slot); // if file was opened before, close it
00254   _fio.handles[slot] = f;
00255   _fio.filenames[slot] = filename;
00256 
00257   /* Store the filename without path and extension */
00258   const char *t = strrchr(filename, PATHSEPCHAR);
00259   _fio.shortnames[slot] = strdup(t == NULL ? filename : t);
00260   char *t2 = strrchr(_fio.shortnames[slot], '.');
00261   if (t2 != NULL) *t2 = '\0';
00262   strtolower(_fio.shortnames[slot]);
00263 
00264 #if defined(LIMITED_FDS)
00265   _fio.usage_count[slot] = 0;
00266   _fio.open_handles++;
00267 #endif /* LIMITED_FDS */
00268   FioSeekToFile(slot, pos);
00269 }
00270 
00271 static const char * const _subdirs[] = {
00272   "",
00273   "save" PATHSEP,
00274   "save" PATHSEP "autosave" PATHSEP,
00275   "scenario" PATHSEP,
00276   "scenario" PATHSEP "heightmap" PATHSEP,
00277   "gm" PATHSEP,
00278   "data" PATHSEP,
00279   "baseset" PATHSEP,
00280   "newgrf" PATHSEP,
00281   "lang" PATHSEP,
00282   "ai" PATHSEP,
00283   "ai" PATHSEP "library" PATHSEP,
00284 };
00285 assert_compile(lengthof(_subdirs) == NUM_SUBDIRS);
00286 
00287 const char *_searchpaths[NUM_SEARCHPATHS];
00288 TarList _tar_list[NUM_SUBDIRS];
00289 TarFileList _tar_filelist[NUM_SUBDIRS];
00290 
00291 typedef std::map<std::string, std::string> TarLinkList;
00292 static TarLinkList _tar_linklist[NUM_SUBDIRS]; 
00293 
00300 bool FioCheckFileExists(const char *filename, Subdirectory subdir)
00301 {
00302   FILE *f = FioFOpenFile(filename, "rb", subdir);
00303   if (f == NULL) return false;
00304 
00305   FioFCloseFile(f);
00306   return true;
00307 }
00308 
00314 bool FileExists(const char *filename)
00315 {
00316 #if defined(WINCE)
00317   /* There is always one platform that doesn't support basic commands... */
00318   HANDLE hand = CreateFile(OTTD2FS(filename), 0, 0, NULL, OPEN_EXISTING, 0, NULL);
00319   if (hand == INVALID_HANDLE_VALUE) return 1;
00320   CloseHandle(hand);
00321   return 0;
00322 #else
00323   return access(OTTD2FS(filename), 0) == 0;
00324 #endif
00325 }
00326 
00330 void FioFCloseFile(FILE *f)
00331 {
00332   fclose(f);
00333 }
00334 
00335 char *FioGetFullPath(char *buf, size_t buflen, Searchpath sp, Subdirectory subdir, const char *filename)
00336 {
00337   assert(subdir < NUM_SUBDIRS);
00338   assert(sp < NUM_SEARCHPATHS);
00339 
00340   snprintf(buf, buflen, "%s%s%s", _searchpaths[sp], _subdirs[subdir], filename);
00341   return buf;
00342 }
00343 
00352 char *FioFindFullPath(char *buf, size_t buflen, Subdirectory subdir, const char *filename)
00353 {
00354   Searchpath sp;
00355   assert(subdir < NUM_SUBDIRS);
00356 
00357   FOR_ALL_SEARCHPATHS(sp) {
00358     FioGetFullPath(buf, buflen, sp, subdir, filename);
00359     if (FileExists(buf)) return buf;
00360 #if !defined(WIN32)
00361     /* Be, as opening files, aware that sometimes the filename
00362      * might be in uppercase when it is in lowercase on the
00363      * disk. Of course Windows doesn't care about casing. */
00364     if (strtolower(buf + strlen(_searchpaths[sp]) - 1) && FileExists(buf)) return buf;
00365 #endif
00366   }
00367 
00368   return NULL;
00369 }
00370 
00371 char *FioAppendDirectory(char *buf, size_t buflen, Searchpath sp, Subdirectory subdir)
00372 {
00373   assert(subdir < NUM_SUBDIRS);
00374   assert(sp < NUM_SEARCHPATHS);
00375 
00376   snprintf(buf, buflen, "%s%s", _searchpaths[sp], _subdirs[subdir]);
00377   return buf;
00378 }
00379 
00380 char *FioGetDirectory(char *buf, size_t buflen, Subdirectory subdir)
00381 {
00382   Searchpath sp;
00383 
00384   /* Find and return the first valid directory */
00385   FOR_ALL_SEARCHPATHS(sp) {
00386     char *ret = FioAppendDirectory(buf, buflen, sp, subdir);
00387     if (FileExists(buf)) return ret;
00388   }
00389 
00390   /* Could not find the directory, fall back to a base path */
00391   ttd_strlcpy(buf, _personal_dir, buflen);
00392 
00393   return buf;
00394 }
00395 
00396 static FILE *FioFOpenFileSp(const char *filename, const char *mode, Searchpath sp, Subdirectory subdir, size_t *filesize)
00397 {
00398 #if defined(WIN32) && defined(UNICODE)
00399   /* fopen is implemented as a define with ellipses for
00400    * Unicode support (prepend an L). As we are not sending
00401    * a string, but a variable, it 'renames' the variable,
00402    * so make that variable to makes it compile happily */
00403   wchar_t Lmode[5];
00404   MultiByteToWideChar(CP_ACP, 0, mode, -1, Lmode, lengthof(Lmode));
00405 #endif
00406   FILE *f = NULL;
00407   char buf[MAX_PATH];
00408 
00409   if (subdir == NO_DIRECTORY) {
00410     strecpy(buf, filename, lastof(buf));
00411   } else {
00412     snprintf(buf, lengthof(buf), "%s%s%s", _searchpaths[sp], _subdirs[subdir], filename);
00413   }
00414 
00415 #if defined(WIN32)
00416   if (mode[0] == 'r' && GetFileAttributes(OTTD2FS(buf)) == INVALID_FILE_ATTRIBUTES) return NULL;
00417 #endif
00418 
00419   f = fopen(buf, mode);
00420 #if !defined(WIN32)
00421   if (f == NULL && strtolower(buf + ((subdir == NO_DIRECTORY) ? 0 : strlen(_searchpaths[sp]) - 1))) {
00422     f = fopen(buf, mode);
00423   }
00424 #endif
00425   if (f != NULL && filesize != NULL) {
00426     /* Find the size of the file */
00427     fseek(f, 0, SEEK_END);
00428     *filesize = ftell(f);
00429     fseek(f, 0, SEEK_SET);
00430   }
00431   return f;
00432 }
00433 
00441 FILE *FioFOpenFileTar(TarFileListEntry *entry, size_t *filesize)
00442 {
00443   FILE *f = fopen(entry->tar_filename, "rb");
00444   if (f == NULL) return f;
00445 
00446   fseek(f, entry->position, SEEK_SET);
00447   if (filesize != NULL) *filesize = entry->size;
00448   return f;
00449 }
00450 
00458 FILE *FioFOpenFile(const char *filename, const char *mode, Subdirectory subdir, size_t *filesize)
00459 {
00460   FILE *f = NULL;
00461   Searchpath sp;
00462 
00463   assert(subdir < NUM_SUBDIRS || subdir == NO_DIRECTORY);
00464 
00465   FOR_ALL_SEARCHPATHS(sp) {
00466     f = FioFOpenFileSp(filename, mode, sp, subdir, filesize);
00467     if (f != NULL || subdir == NO_DIRECTORY) break;
00468   }
00469 
00470   /* We can only use .tar in case of data-dir, and read-mode */
00471   if (f == NULL && mode[0] == 'r' && subdir != NO_DIRECTORY) {
00472     static const uint MAX_RESOLVED_LENGTH = 2 * (100 + 100 + 155) + 1; // Enough space to hold two filenames plus link. See 'TarHeader'.
00473     char resolved_name[MAX_RESOLVED_LENGTH];
00474 
00475     /* Filenames in tars are always forced to be lowercase */
00476     strecpy(resolved_name, filename, lastof(resolved_name));
00477     strtolower(resolved_name);
00478 
00479     size_t resolved_len = strlen(resolved_name);
00480 
00481     /* Resolve ONE directory link */
00482     for (TarLinkList::iterator link = _tar_linklist[subdir].begin(); link != _tar_linklist[subdir].end(); link++) {
00483       const std::string &src = link->first;
00484       size_t len = src.length();
00485       if (resolved_len >= len && resolved_name[len - 1] == PATHSEPCHAR && strncmp(src.c_str(), resolved_name, len) == 0) {
00486         /* Apply link */
00487         char resolved_name2[MAX_RESOLVED_LENGTH];
00488         const std::string &dest = link->second;
00489         strecpy(resolved_name2, &(resolved_name[len]), lastof(resolved_name2));
00490         strecpy(resolved_name, dest.c_str(), lastof(resolved_name));
00491         strecpy(&(resolved_name[dest.length()]), resolved_name2, lastof(resolved_name));
00492         break; // Only resolve one level
00493       }
00494     }
00495 
00496     TarFileList::iterator it = _tar_filelist[subdir].find(resolved_name);
00497     if (it != _tar_filelist[subdir].end()) {
00498       f = FioFOpenFileTar(&((*it).second), filesize);
00499     }
00500   }
00501 
00502   /* Sometimes a full path is given. To support
00503    * the 'subdirectory' must be 'removed'. */
00504   if (f == NULL && subdir != NO_DIRECTORY) {
00505     switch (subdir) {
00506       case BASESET_DIR:
00507         f = FioFOpenFile(filename, mode, OLD_GM_DIR, filesize);
00508         if (f != NULL) break;
00509         /* FALL THROUGH */
00510       case NEWGRF_DIR:
00511         f = FioFOpenFile(filename, mode, OLD_DATA_DIR, filesize);
00512         break;
00513 
00514       default:
00515         f = FioFOpenFile(filename, mode, NO_DIRECTORY, filesize);
00516         break;
00517     }
00518   }
00519 
00520   return f;
00521 }
00522 
00527 static void FioCreateDirectory(const char *name)
00528 {
00529 #if defined(WIN32) || defined(WINCE)
00530   CreateDirectory(OTTD2FS(name), NULL);
00531 #elif defined(OS2) && !defined(__INNOTEK_LIBC__)
00532   mkdir(OTTD2FS(name));
00533 #elif defined(__MORPHOS__) || defined(__AMIGAOS__)
00534   char buf[MAX_PATH];
00535   ttd_strlcpy(buf, name, MAX_PATH);
00536 
00537   size_t len = strlen(name) - 1;
00538   if (buf[len] == '/') {
00539     buf[len] = '\0'; // Kill pathsep, so mkdir() will not fail
00540   }
00541 
00542   mkdir(OTTD2FS(buf), 0755);
00543 #else
00544   mkdir(OTTD2FS(name), 0755);
00545 #endif
00546 }
00547 
00555 bool AppendPathSeparator(char *buf, size_t buflen)
00556 {
00557   size_t s = strlen(buf);
00558 
00559   /* Length of string + path separator + '\0' */
00560   if (s != 0 && buf[s - 1] != PATHSEPCHAR) {
00561     if (s + 2 >= buflen) return false;
00562 
00563     buf[s]     = PATHSEPCHAR;
00564     buf[s + 1] = '\0';
00565   }
00566 
00567   return true;
00568 }
00569 
00576 char *BuildWithFullPath(const char *dir)
00577 {
00578   char *dest = MallocT<char>(MAX_PATH);
00579   ttd_strlcpy(dest, dir, MAX_PATH);
00580 
00581   /* Check if absolute or relative path */
00582   const char *s = strchr(dest, PATHSEPCHAR);
00583 
00584   /* Add absolute path */
00585   if (s == NULL || dest != s) {
00586     if (getcwd(dest, MAX_PATH) == NULL) *dest = '\0';
00587     AppendPathSeparator(dest, MAX_PATH);
00588     ttd_strlcat(dest, dir, MAX_PATH);
00589   }
00590   AppendPathSeparator(dest, MAX_PATH);
00591 
00592   return dest;
00593 }
00594 
00600 const char *FioTarFirstDir(const char *tarname, Subdirectory subdir)
00601 {
00602   TarList::iterator it = _tar_list[subdir].find(tarname);
00603   if (it == _tar_list[subdir].end()) return NULL;
00604   return (*it).second.dirname;
00605 }
00606 
00607 static void TarAddLink(const std::string &srcParam, const std::string &destParam, Subdirectory subdir)
00608 {
00609   std::string src = srcParam;
00610   std::string dest = destParam;
00611   /* Tar internals assume lowercase */
00612   std::transform(src.begin(), src.end(), src.begin(), tolower);
00613   std::transform(dest.begin(), dest.end(), dest.begin(), tolower);
00614 
00615   TarFileList::iterator dest_file = _tar_filelist[subdir].find(dest);
00616   if (dest_file != _tar_filelist[subdir].end()) {
00617     /* Link to file. Process the link like the destination file. */
00618     _tar_filelist[subdir].insert(TarFileList::value_type(src, dest_file->second));
00619   } else {
00620     /* Destination file not found. Assume 'link to directory'
00621      * Append PATHSEPCHAR to 'src' and 'dest' if needed */
00622     const std::string src_path = ((*src.rbegin() == PATHSEPCHAR) ? src : src + PATHSEPCHAR);
00623     const std::string dst_path = (dest.length() == 0 ? "" : ((*dest.rbegin() == PATHSEPCHAR) ? dest : dest + PATHSEPCHAR));
00624     _tar_linklist[subdir].insert(TarLinkList::value_type(src_path, dst_path));
00625   }
00626 }
00627 
00628 void FioTarAddLink(const char *src, const char *dest, Subdirectory subdir)
00629 {
00630   TarAddLink(src, dest, subdir);
00631 }
00632 
00638 static void SimplifyFileName(char *name)
00639 {
00640   /* Force lowercase */
00641   strtolower(name);
00642 
00643   /* Tar-files always have '/' path-seperator, but we want our PATHSEPCHAR */
00644 #if (PATHSEPCHAR != '/')
00645   for (char *n = name; *n != '\0'; n++) if (*n == '/') *n = PATHSEPCHAR;
00646 #endif
00647 }
00648 
00654 uint TarScanner::DoScan(Subdirectory sd)
00655 {
00656   _tar_filelist[sd].clear();
00657   _tar_list[sd].clear();
00658   uint num = this->Scan(".tar", sd, false);
00659   if (sd == BASESET_DIR || sd == NEWGRF_DIR) num += this->Scan(".tar", OLD_DATA_DIR, false);
00660   return num;
00661 }
00662 
00663 /* static */ uint TarScanner::DoScan(TarScanner::Mode mode)
00664 {
00665   DEBUG(misc, 1, "Scanning for tars");
00666   TarScanner fs;
00667   uint num = 0;
00668   if (mode & TarScanner::BASESET) {
00669     num += fs.DoScan(BASESET_DIR);
00670   }
00671   if (mode & TarScanner::NEWGRF) {
00672     num += fs.DoScan(NEWGRF_DIR);
00673   }
00674   if (mode & TarScanner::AI) {
00675     num += fs.DoScan(AI_DIR);
00676     num += fs.DoScan(AI_LIBRARY_DIR);
00677   }
00678   if (mode & TarScanner::SCENARIO) {
00679     num += fs.DoScan(SCENARIO_DIR);
00680   }
00681   DEBUG(misc, 1, "Scan complete, found %d files", num);
00682   return num;
00683 }
00684 
00691 bool TarScanner::AddFile(Subdirectory sd, const char *filename)
00692 {
00693   this->subdir = sd;
00694   return this->AddFile(filename, 0);
00695 }
00696 
00697 bool TarScanner::AddFile(const char *filename, size_t basepath_length, const char *tar_filename)
00698 {
00699   /* No tar within tar. */
00700   assert(tar_filename == NULL);
00701 
00702   /* The TAR-header, repeated for every file */
00703   typedef struct TarHeader {
00704     char name[100];      
00705     char mode[8];
00706     char uid[8];
00707     char gid[8];
00708     char size[12];       
00709     char mtime[12];
00710     char chksum[8];
00711     char typeflag;
00712     char linkname[100];
00713     char magic[6];
00714     char version[2];
00715     char uname[32];
00716     char gname[32];
00717     char devmajor[8];
00718     char devminor[8];
00719     char prefix[155];    
00720 
00721     char unused[12];
00722   } TarHeader;
00723 
00724   /* Check if we already seen this file */
00725   TarList::iterator it = _tar_list[this->subdir].find(filename);
00726   if (it != _tar_list[this->subdir].end()) return false;
00727 
00728   FILE *f = fopen(filename, "rb");
00729   /* Although the file has been found there can be
00730    * a number of reasons we cannot open the file.
00731    * Most common case is when we simply have not
00732    * been given read access. */
00733   if (f == NULL) return false;
00734 
00735   const char *dupped_filename = strdup(filename);
00736   _tar_list[this->subdir][filename].filename = dupped_filename;
00737   _tar_list[this->subdir][filename].dirname = NULL;
00738 
00739   TarLinkList links; 
00740 
00741   TarHeader th;
00742   char buf[sizeof(th.name) + 1], *end;
00743   char name[sizeof(th.prefix) + 1 + sizeof(th.name) + 1];
00744   char link[sizeof(th.linkname) + 1];
00745   char dest[sizeof(th.prefix) + 1 + sizeof(th.name) + 1 + 1 + sizeof(th.linkname) + 1];
00746   size_t num = 0, pos = 0;
00747 
00748   /* Make a char of 512 empty bytes */
00749   char empty[512];
00750   memset(&empty[0], 0, sizeof(empty));
00751 
00752   for (;;) { // Note: feof() always returns 'false' after 'fseek()'. Cool, isn't it?
00753     size_t num_bytes_read = fread(&th, 1, 512, f);
00754     if (num_bytes_read != 512) break;
00755     pos += num_bytes_read;
00756 
00757     /* Check if we have the new tar-format (ustar) or the old one (a lot of zeros after 'link' field) */
00758     if (strncmp(th.magic, "ustar", 5) != 0 && memcmp(&th.magic, &empty[0], 512 - offsetof(TarHeader, magic)) != 0) {
00759       /* If we have only zeros in the block, it can be an end-of-file indicator */
00760       if (memcmp(&th, &empty[0], 512) == 0) continue;
00761 
00762       DEBUG(misc, 0, "The file '%s' isn't a valid tar-file", filename);
00763       return false;
00764     }
00765 
00766     name[0] = '\0';
00767     size_t len = 0;
00768 
00769     /* The prefix contains the directory-name */
00770     if (th.prefix[0] != '\0') {
00771       memcpy(name, th.prefix, sizeof(th.prefix));
00772       name[sizeof(th.prefix)] = '\0';
00773       len = strlen(name);
00774       name[len] = PATHSEPCHAR;
00775       len++;
00776     }
00777 
00778     /* Copy the name of the file in a safe way at the end of 'name' */
00779     memcpy(&name[len], th.name, sizeof(th.name));
00780     name[len + sizeof(th.name)] = '\0';
00781 
00782     /* Calculate the size of the file.. for some strange reason this is stored as a string */
00783     memcpy(buf, th.size, sizeof(th.size));
00784     buf[sizeof(th.size)] = '\0';
00785     size_t skip = strtoul(buf, &end, 8);
00786 
00787     switch (th.typeflag) {
00788       case '\0':
00789       case '0': { // regular file
00790         /* Ignore empty files */
00791         if (skip == 0) break;
00792 
00793         if (strlen(name) == 0) break;
00794 
00795         /* Store this entry in the list */
00796         TarFileListEntry entry;
00797         entry.tar_filename = dupped_filename;
00798         entry.size         = skip;
00799         entry.position     = pos;
00800 
00801         /* Convert to lowercase and our PATHSEPCHAR */
00802         SimplifyFileName(name);
00803 
00804         DEBUG(misc, 6, "Found file in tar: %s (" PRINTF_SIZE " bytes, " PRINTF_SIZE " offset)", name, skip, pos);
00805         if (_tar_filelist[this->subdir].insert(TarFileList::value_type(name, entry)).second) num++;
00806 
00807         break;
00808       }
00809 
00810       case '1': // hard links
00811       case '2': { // symbolic links
00812         /* Copy the destination of the link in a safe way at the end of 'linkname' */
00813         memcpy(link, th.linkname, sizeof(th.linkname));
00814         link[sizeof(th.linkname)] = '\0';
00815 
00816         if (strlen(name) == 0 || strlen(link) == 0) break;
00817 
00818         /* Convert to lowercase and our PATHSEPCHAR */
00819         SimplifyFileName(name);
00820         SimplifyFileName(link);
00821 
00822         /* Only allow relative links */
00823         if (link[0] == PATHSEPCHAR) {
00824           DEBUG(misc, 1, "Ignoring absolute link in tar: %s -> %s", name, link);
00825           break;
00826         }
00827 
00828         /* Process relative path.
00829          * Note: The destination of links must not contain any directory-links. */
00830         strecpy(dest, name, lastof(dest));
00831         char *destpos = strrchr(dest, PATHSEPCHAR);
00832         if (destpos == NULL) destpos = dest;
00833         *destpos = '\0';
00834 
00835         char *pos = link;
00836         while (*pos != '\0') {
00837           char *next = strchr(link, PATHSEPCHAR);
00838           if (next == NULL) next = pos + strlen(pos);
00839 
00840           /* Skip '.' (current dir) */
00841           if (next != pos + 1 || pos[0] != '.') {
00842             if (next == pos + 2 && pos[0] == '.' && pos[1] == '.') {
00843               /* level up */
00844               if (dest[0] == '\0') {
00845                 DEBUG(misc, 1, "Ignoring link pointing outside of data directory: %s -> %s", name, link);
00846                 break;
00847               }
00848 
00849               /* Truncate 'dest' after last PATHSEPCHAR.
00850                * This assumes that the truncated part is a real directory and not a link. */
00851               destpos = strrchr(dest, PATHSEPCHAR);
00852               if (destpos == NULL) destpos = dest;
00853             } else {
00854               /* Append at end of 'dest' */
00855               if (destpos != dest) *(destpos++) = PATHSEPCHAR;
00856               strncpy(destpos, pos, next - pos); // Safe as we do '\0'-termination ourselves
00857               destpos += next - pos;
00858             }
00859             *destpos = '\0';
00860           }
00861 
00862           pos = next;
00863         }
00864 
00865         /* Store links in temporary list */
00866         DEBUG(misc, 6, "Found link in tar: %s -> %s", name, dest);
00867         links.insert(TarLinkList::value_type(name, dest));
00868 
00869         break;
00870       }
00871 
00872       case '5': // directory
00873         /* Convert to lowercase and our PATHSEPCHAR */
00874         SimplifyFileName(name);
00875 
00876         /* Store the first directory name we detect */
00877         DEBUG(misc, 6, "Found dir in tar: %s", name);
00878         if (_tar_list[this->subdir][filename].dirname == NULL) _tar_list[this->subdir][filename].dirname = strdup(name);
00879         break;
00880 
00881       default:
00882         /* Ignore other types */
00883         break;
00884     }
00885 
00886     /* Skip to the next block.. */
00887     skip = Align(skip, 512);
00888     fseek(f, skip, SEEK_CUR);
00889     pos += skip;
00890   }
00891 
00892   DEBUG(misc, 1, "Found tar '%s' with " PRINTF_SIZE " new files", filename, num);
00893   fclose(f);
00894 
00895   /* Resolve file links and store directory links.
00896    * We restrict usage of links to two cases:
00897    *  1) Links to directories:
00898    *      Both the source path and the destination path must NOT contain any further links.
00899    *      When resolving files at most one directory link is resolved.
00900    *  2) Links to files:
00901    *      The destination path must NOT contain any links.
00902    *      The source path may contain one directory link.
00903    */
00904   for (TarLinkList::iterator link = links.begin(); link != links.end(); link++) {
00905     const std::string &src = link->first;
00906     const std::string &dest = link->second;
00907     TarAddLink(src, dest, this->subdir);
00908   }
00909 
00910   return true;
00911 }
00912 
00920 bool ExtractTar(const char *tar_filename, Subdirectory subdir)
00921 {
00922   TarList::iterator it = _tar_list[subdir].find(tar_filename);
00923   /* We don't know the file. */
00924   if (it == _tar_list[subdir].end()) return false;
00925 
00926   const char *dirname = (*it).second.dirname;
00927 
00928   /* The file doesn't have a sub directory! */
00929   if (dirname == NULL) return false;
00930 
00931   char filename[MAX_PATH];
00932   strecpy(filename, tar_filename, lastof(filename));
00933   char *p = strrchr(filename, PATHSEPCHAR);
00934   /* The file's path does not have a separator? */
00935   if (p == NULL) return false;
00936 
00937   p++;
00938   strecpy(p, dirname, lastof(filename));
00939   DEBUG(misc, 8, "Extracting %s to directory %s", tar_filename, filename);
00940   FioCreateDirectory(filename);
00941 
00942   for (TarFileList::iterator it2 = _tar_filelist[subdir].begin(); it2 != _tar_filelist[subdir].end(); it2++) {
00943     if (strcmp((*it2).second.tar_filename, tar_filename) != 0) continue;
00944 
00945     strecpy(p, (*it2).first.c_str(), lastof(filename));
00946 
00947     DEBUG(misc, 9, "  extracting %s", filename);
00948 
00949     /* First open the file in the .tar. */
00950     size_t to_copy = 0;
00951     FILE *in = FioFOpenFileTar(&(*it2).second, &to_copy);
00952     if (in == NULL) {
00953       DEBUG(misc, 6, "Extracting %s failed; could not open %s", filename, tar_filename);
00954       return false;
00955     }
00956 
00957     /* Now open the 'output' file. */
00958     FILE *out = fopen(filename, "wb");
00959     if (out == NULL) {
00960       DEBUG(misc, 6, "Extracting %s failed; could not open %s", filename, filename);
00961       fclose(in);
00962       return false;
00963     }
00964 
00965     /* Now read from the tar and write it into the file. */
00966     char buffer[4096];
00967     size_t read;
00968     for (; to_copy != 0; to_copy -= read) {
00969       read = fread(buffer, 1, min(to_copy, lengthof(buffer)), in);
00970       if (read <= 0 || fwrite(buffer, 1, read, out) != read) break;
00971     }
00972 
00973     /* Close everything up. */
00974     fclose(in);
00975     fclose(out);
00976 
00977     if (to_copy != 0) {
00978       DEBUG(misc, 6, "Extracting %s failed; still %i bytes to copy", filename, (int)to_copy);
00979       return false;
00980     }
00981   }
00982 
00983   DEBUG(misc, 9, "  extraction successful");
00984   return true;
00985 }
00986 
00987 #if defined(WIN32) || defined(WINCE)
00988 
00993 extern void DetermineBasePaths(const char *exe);
00994 #else /* defined(WIN32) || defined(WINCE) */
00995 
01003 static bool ChangeWorkingDirectoryToExecutable(const char *exe)
01004 {
01005   bool success = false;
01006 #ifdef WITH_COCOA
01007   char *app_bundle = strchr(exe, '.');
01008   while (app_bundle != NULL && strncasecmp(app_bundle, ".app", 4) != 0) app_bundle = strchr(&app_bundle[1], '.');
01009 
01010   if (app_bundle != NULL) app_bundle[0] = '\0';
01011 #endif /* WITH_COCOA */
01012   char *s = const_cast<char *>(strrchr(exe, PATHSEPCHAR));
01013   if (s != NULL) {
01014     *s = '\0';
01015 #if defined(__DJGPP__)
01016     /* If we want to go to the root, we can't use cd C:, but we must use '/' */
01017     if (s[-1] == ':') chdir("/");
01018 #endif
01019     if (chdir(exe) != 0) {
01020       DEBUG(misc, 0, "Directory with the binary does not exist?");
01021     } else {
01022       success = true;
01023     }
01024     *s = PATHSEPCHAR;
01025   }
01026 #ifdef WITH_COCOA
01027   if (app_bundle != NULL) app_bundle[0] = '.';
01028 #endif /* WITH_COCOA */
01029   return success;
01030 }
01031 
01042 bool DoScanWorkingDirectory()
01043 {
01044   /* No working directory, so nothing to do. */
01045   if (_searchpaths[SP_WORKING_DIR] == NULL) return false;
01046 
01047   /* Working directory is root, so do nothing. */
01048   if (strcmp(_searchpaths[SP_WORKING_DIR], PATHSEP) == 0) return false;
01049 
01050   /* No personal/home directory, so the working directory won't be that. */
01051   if (_searchpaths[SP_PERSONAL_DIR] == NULL) return true;
01052 
01053   char tmp[MAX_PATH];
01054   snprintf(tmp, lengthof(tmp), "%s%s", _searchpaths[SP_WORKING_DIR], PERSONAL_DIR);
01055   AppendPathSeparator(tmp, MAX_PATH);
01056   return strcmp(tmp, _searchpaths[SP_PERSONAL_DIR]) != 0;
01057 }
01058 
01063 void DetermineBasePaths(const char *exe)
01064 {
01065   char tmp[MAX_PATH];
01066 #if defined(__MORPHOS__) || defined(__AMIGA__) || defined(DOS) || defined(OS2) || !defined(WITH_PERSONAL_DIR)
01067   _searchpaths[SP_PERSONAL_DIR] = NULL;
01068 #else
01069 #ifdef __HAIKU__
01070   BPath path;
01071   find_directory(B_USER_SETTINGS_DIRECTORY, &path);
01072   const char *homedir = path.Path();
01073 #else
01074   const char *homedir = getenv("HOME");
01075 
01076   if (homedir == NULL) {
01077     const struct passwd *pw = getpwuid(getuid());
01078     homedir = (pw == NULL) ? "" : pw->pw_dir;
01079   }
01080 #endif
01081 
01082   snprintf(tmp, MAX_PATH, "%s" PATHSEP "%s", homedir, PERSONAL_DIR);
01083   AppendPathSeparator(tmp, MAX_PATH);
01084 
01085   _searchpaths[SP_PERSONAL_DIR] = strdup(tmp);
01086 #endif
01087 
01088 #if defined(WITH_SHARED_DIR)
01089   snprintf(tmp, MAX_PATH, "%s", SHARED_DIR);
01090   AppendPathSeparator(tmp, MAX_PATH);
01091   _searchpaths[SP_SHARED_DIR] = strdup(tmp);
01092 #else
01093   _searchpaths[SP_SHARED_DIR] = NULL;
01094 #endif
01095 
01096 #if defined(__MORPHOS__) || defined(__AMIGA__)
01097   _searchpaths[SP_WORKING_DIR] = NULL;
01098 #else
01099   if (getcwd(tmp, MAX_PATH) == NULL) *tmp = '\0';
01100   AppendPathSeparator(tmp, MAX_PATH);
01101   _searchpaths[SP_WORKING_DIR] = strdup(tmp);
01102 #endif
01103 
01104   _do_scan_working_directory = DoScanWorkingDirectory();
01105 
01106   /* Change the working directory to that one of the executable */
01107   if (ChangeWorkingDirectoryToExecutable(exe)) {
01108     if (getcwd(tmp, MAX_PATH) == NULL) *tmp = '\0';
01109     AppendPathSeparator(tmp, MAX_PATH);
01110     _searchpaths[SP_BINARY_DIR] = strdup(tmp);
01111   } else {
01112     _searchpaths[SP_BINARY_DIR] = NULL;
01113   }
01114 
01115   if (_searchpaths[SP_WORKING_DIR] != NULL) {
01116     /* Go back to the current working directory. */
01117     if (chdir(_searchpaths[SP_WORKING_DIR]) != 0) {
01118       DEBUG(misc, 0, "Failed to return to working directory!");
01119     }
01120   }
01121 
01122 #if defined(__MORPHOS__) || defined(__AMIGA__) || defined(DOS) || defined(OS2)
01123   _searchpaths[SP_INSTALLATION_DIR] = NULL;
01124 #else
01125   snprintf(tmp, MAX_PATH, "%s", GLOBAL_DATA_DIR);
01126   AppendPathSeparator(tmp, MAX_PATH);
01127   _searchpaths[SP_INSTALLATION_DIR] = strdup(tmp);
01128 #endif
01129 #ifdef WITH_COCOA
01130 extern void cocoaSetApplicationBundleDir();
01131   cocoaSetApplicationBundleDir();
01132 #else
01133   _searchpaths[SP_APPLICATION_BUNDLE_DIR] = NULL;
01134 #endif
01135 }
01136 #endif /* defined(WIN32) || defined(WINCE) */
01137 
01138 char *_personal_dir;
01139 
01146 void DeterminePaths(const char *exe)
01147 {
01148   DetermineBasePaths(exe);
01149 
01150   Searchpath sp;
01151   FOR_ALL_SEARCHPATHS(sp) {
01152     if (sp == SP_WORKING_DIR && !_do_scan_working_directory) continue;
01153     DEBUG(misc, 4, "%s added as search path", _searchpaths[sp]);
01154   }
01155 
01156   if (_config_file != NULL) {
01157     _personal_dir = strdup(_config_file);
01158     char *end = strrchr(_personal_dir, PATHSEPCHAR);
01159     if (end == NULL) {
01160       _personal_dir[0] = '\0';
01161     } else {
01162       end[1] = '\0';
01163     }
01164   } else {
01165     char personal_dir[MAX_PATH];
01166     if (FioFindFullPath(personal_dir, lengthof(personal_dir), BASE_DIR, "openttd.cfg") != NULL) {
01167       char *end = strrchr(personal_dir, PATHSEPCHAR);
01168       if (end != NULL) end[1] = '\0';
01169       _personal_dir = strdup(personal_dir);
01170       _config_file = str_fmt("%sopenttd.cfg", _personal_dir);
01171     } else {
01172       static const Searchpath new_openttd_cfg_order[] = {
01173           SP_PERSONAL_DIR, SP_BINARY_DIR, SP_WORKING_DIR, SP_SHARED_DIR, SP_INSTALLATION_DIR
01174         };
01175 
01176       for (uint i = 0; i < lengthof(new_openttd_cfg_order); i++) {
01177         if (IsValidSearchPath(new_openttd_cfg_order[i])) {
01178           _personal_dir = strdup(_searchpaths[new_openttd_cfg_order[i]]);
01179           _config_file = str_fmt("%sopenttd.cfg", _personal_dir);
01180           break;
01181         }
01182       }
01183     }
01184   }
01185 
01186   DEBUG(misc, 3, "%s found as personal directory", _personal_dir);
01187 
01188   _highscore_file = str_fmt("%shs.dat", _personal_dir);
01189   extern char *_hotkeys_file;
01190   _hotkeys_file = str_fmt("%shotkeys.cfg",  _personal_dir);
01191 
01192   /* Make the necessary folders */
01193 #if !defined(__MORPHOS__) && !defined(__AMIGA__) && defined(WITH_PERSONAL_DIR)
01194   FioCreateDirectory(_personal_dir);
01195 #endif
01196 
01197   static const Subdirectory default_subdirs[] = {
01198     SAVE_DIR, AUTOSAVE_DIR, SCENARIO_DIR, HEIGHTMAP_DIR, BASESET_DIR, NEWGRF_DIR, AI_DIR, AI_LIBRARY_DIR
01199   };
01200 
01201   for (uint i = 0; i < lengthof(default_subdirs); i++) {
01202     char *dir = str_fmt("%s%s", _personal_dir, _subdirs[default_subdirs[i]]);
01203     FioCreateDirectory(dir);
01204     free(dir);
01205   }
01206 
01207   /* If we have network we make a directory for the autodownloading of content */
01208   _searchpaths[SP_AUTODOWNLOAD_DIR] = str_fmt("%s%s", _personal_dir, "content_download" PATHSEP);
01209 #ifdef ENABLE_NETWORK
01210   FioCreateDirectory(_searchpaths[SP_AUTODOWNLOAD_DIR]);
01211 
01212   /* Create the directory for each of the types of content */
01213   const Subdirectory dirs[] = { SCENARIO_DIR, HEIGHTMAP_DIR, BASESET_DIR, NEWGRF_DIR, AI_DIR, AI_LIBRARY_DIR };
01214   for (uint i = 0; i < lengthof(dirs); i++) {
01215     char *tmp = str_fmt("%s%s", _searchpaths[SP_AUTODOWNLOAD_DIR], _subdirs[dirs[i]]);
01216     FioCreateDirectory(tmp);
01217     free(tmp);
01218   }
01219 
01220   extern char *_log_file;
01221   _log_file = str_fmt("%sopenttd.log",  _personal_dir);
01222 #else /* ENABLE_NETWORK */
01223   /* If we don't have networking, we don't need to make the directory. But
01224    * if it exists we keep it, otherwise remove it from the search paths. */
01225   if (!FileExists(_searchpaths[SP_AUTODOWNLOAD_DIR]))  {
01226     free(_searchpaths[SP_AUTODOWNLOAD_DIR]);
01227     _searchpaths[SP_AUTODOWNLOAD_DIR] = NULL;
01228   }
01229 #endif /* ENABLE_NETWORK */
01230 }
01231 
01236 void SanitizeFilename(char *filename)
01237 {
01238   for (; *filename != '\0'; filename++) {
01239     switch (*filename) {
01240       /* The following characters are not allowed in filenames
01241        * on at least one of the supported operating systems: */
01242       case ':': case '\\': case '*': case '?': case '/':
01243       case '<': case '>': case '|': case '"':
01244         *filename = '_';
01245         break;
01246     }
01247   }
01248 }
01249 
01258 void *ReadFileToMem(const char *filename, size_t *lenp, size_t maxsize)
01259 {
01260   FILE *in = fopen(filename, "rb");
01261   if (in == NULL) return NULL;
01262 
01263   fseek(in, 0, SEEK_END);
01264   size_t len = ftell(in);
01265   fseek(in, 0, SEEK_SET);
01266   if (len > maxsize) {
01267     fclose(in);
01268     return NULL;
01269   }
01270   byte *mem = MallocT<byte>(len + 1);
01271   mem[len] = 0;
01272   if (fread(mem, len, 1, in) != 1) {
01273     fclose(in);
01274     free(mem);
01275     return NULL;
01276   }
01277   fclose(in);
01278 
01279   *lenp = len;
01280   return mem;
01281 }
01282 
01289 static bool MatchesExtension(const char *extension, const char *filename)
01290 {
01291   if (extension == NULL) return true;
01292 
01293   const char *ext = strrchr(filename, extension[0]);
01294   return ext != NULL && strcasecmp(ext, extension) == 0;
01295 }
01296 
01306 static uint ScanPath(FileScanner *fs, const char *extension, const char *path, size_t basepath_length, bool recursive)
01307 {
01308   extern bool FiosIsValidFile(const char *path, const struct dirent *ent, struct stat *sb);
01309 
01310   uint num = 0;
01311   struct stat sb;
01312   struct dirent *dirent;
01313   DIR *dir;
01314 
01315   if (path == NULL || (dir = ttd_opendir(path)) == NULL) return 0;
01316 
01317   while ((dirent = readdir(dir)) != NULL) {
01318     const char *d_name = FS2OTTD(dirent->d_name);
01319     char filename[MAX_PATH];
01320 
01321     if (!FiosIsValidFile(path, dirent, &sb)) continue;
01322 
01323     snprintf(filename, lengthof(filename), "%s%s", path, d_name);
01324 
01325     if (S_ISDIR(sb.st_mode)) {
01326       /* Directory */
01327       if (!recursive) continue;
01328       if (strcmp(d_name, ".") == 0 || strcmp(d_name, "..") == 0) continue;
01329       if (!AppendPathSeparator(filename, lengthof(filename))) continue;
01330       num += ScanPath(fs, extension, filename, basepath_length, recursive);
01331     } else if (S_ISREG(sb.st_mode)) {
01332       /* File */
01333       if (MatchesExtension(extension, filename) && fs->AddFile(filename, basepath_length, NULL)) num++;
01334     }
01335   }
01336 
01337   closedir(dir);
01338 
01339   return num;
01340 }
01341 
01348 static uint ScanTar(FileScanner *fs, const char *extension, TarFileList::iterator tar)
01349 {
01350   uint num = 0;
01351   const char *filename = (*tar).first.c_str();
01352 
01353   if (MatchesExtension(extension, filename) && fs->AddFile(filename, 0, (*tar).second.tar_filename)) num++;
01354 
01355   return num;
01356 }
01357 
01367 uint FileScanner::Scan(const char *extension, Subdirectory sd, bool tars, bool recursive)
01368 {
01369   this->subdir = sd;
01370 
01371   Searchpath sp;
01372   char path[MAX_PATH];
01373   TarFileList::iterator tar;
01374   uint num = 0;
01375 
01376   FOR_ALL_SEARCHPATHS(sp) {
01377     /* Don't search in the working directory */
01378     if (sp == SP_WORKING_DIR && !_do_scan_working_directory) continue;
01379 
01380     FioAppendDirectory(path, MAX_PATH, sp, sd);
01381     num += ScanPath(this, extension, path, strlen(path), recursive);
01382   }
01383 
01384   if (tars && sd != NO_DIRECTORY) {
01385     FOR_ALL_TARS(tar, sd) {
01386       num += ScanTar(this, extension, tar);
01387     }
01388   }
01389 
01390   switch (sd) {
01391     case BASESET_DIR:
01392       num += this->Scan(extension, OLD_GM_DIR, tars, recursive);
01393       /* FALL THROUGH */
01394     case NEWGRF_DIR:
01395       num += this->Scan(extension, OLD_DATA_DIR, tars, recursive);
01396       break;
01397 
01398     default: break;
01399   }
01400 
01401   return num;
01402 }
01403 
01412 uint FileScanner::Scan(const char *extension, const char *directory, bool recursive)
01413 {
01414   char path[MAX_PATH];
01415   strecpy(path, directory, lastof(path));
01416   if (!AppendPathSeparator(path, lengthof(path))) return 0;
01417   return ScanPath(this, extension, path, strlen(path), recursive);
01418 }