# fileio.cpp

Go to the documentation of this file.

    00001 /* $Id: fileio.cpp 20168 2010-07-17 11:45:42Z rubidium $ */
    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 "variables.h"
    00015 #include "debug.h"
    00016 #include "fios.h"
    00017 #include "string_func.h"
    00018 #include "tar_type.h"
    00019 #ifdef WIN32
    00020 #include <windows.h>
    00021 #elif defined(__HAIKU__)
    00022 #include <Path.h>
    00023 #include <storage/FindDirectory.h>
    00024 #else
    00025 #if defined(OPENBSD) || defined(DOS)
    00026 #include <unistd.h>
    00027 #endif
    00028 #include <pwd.h>
    00029 #endif
    00030 #include <sys/stat.h>
    00031 #include <algorithm>
    00032
    00033 /*************************************************/
    00034 /* FILE IO ROUTINES ******************************/
    00035 /*************************************************/
    00036
    00037 #define FIO_BUFFER_SIZE 512
    00038
    00039 struct Fio {
    00040   byte *buffer, *buffer_end;
    00041   size_t pos;
    00042   FILE *cur_fh;
    00043   const char *filename;
    00044   FILE *handles[MAX_FILE_SLOTS];
    00045   byte buffer_start[FIO_BUFFER_SIZE];
    00046   const char *filenames[MAX_FILE_SLOTS];
    00047   char *shortnames[MAX_FILE_SLOTS];
    00048 #if defined(LIMITED_FDS)
    00049   uint open_handles;
    00050   uint usage_count[MAX_FILE_SLOTS];
    00051 #endif /* LIMITED_FDS */
    00052 };
    00053
    00054 static Fio _fio;
    00055
    00057 static bool _do_scan_working_directory = true;
    00058
    00059 /* Get current position in file */
    00060 size_t FioGetPos()
    00061 {
    00062   return _fio.pos + (_fio.buffer - _fio.buffer_end);
    00063 }
    00064
    00065 const char *FioGetFilename(uint8 slot)
    00066 {
    00067   return _fio.shortnames[slot];
    00068 }
    00069
    00070 void FioSeekTo(size_t pos, int mode)
    00071 {
    00072   if (mode == SEEK_CUR) pos += FioGetPos();
    00073   _fio.buffer = _fio.buffer_end = _fio.buffer_start + FIO_BUFFER_SIZE;
    00074   _fio.pos = pos;
    00075   fseek(_fio.cur_fh, _fio.pos, SEEK_SET);
    00076 }
    00077
    00078 #if defined(LIMITED_FDS)
    00079 static void FioRestoreFile(int slot)
    00080 {
    00081   /* Do we still have the file open, or should we reopen it? */
    00082   if (_fio.handles[slot] == NULL) {
    00083     DEBUG(misc, 6, "Restoring file '%s' in slot '%d' from disk", _fio.filenames[slot], slot);
    00084     FioOpenFile(slot, _fio.filenames[slot]);
    00085   }
    00086   _fio.usage_count[slot]++;
    00087 }
    00088 #endif /* LIMITED_FDS */
    00089
    00090 /* Seek to a file and a position */
    00091 void FioSeekToFile(uint8 slot, size_t pos)
    00092 {
    00093   FILE *f;
    00094 #if defined(LIMITED_FDS)
    00095   /* Make sure we have this file open */
    00096   FioRestoreFile(slot);
    00097 #endif /* LIMITED_FDS */
    00098   f = _fio.handles[slot];
    00099   assert(f != NULL);
    00100   _fio.cur_fh = f;
    00101   _fio.filename = _fio.filenames[slot];
    00102   FioSeekTo(pos, SEEK_SET);
    00103 }
    00104
    00105 byte FioReadByte()
    00106 {
    00107   if (_fio.buffer == _fio.buffer_end) {
    00108     _fio.buffer = _fio.buffer_start;
    00109     size_t size = fread(_fio.buffer, 1, FIO_BUFFER_SIZE, _fio.cur_fh);
    00110     _fio.pos += size;
    00111     _fio.buffer_end = _fio.buffer_start + size;
    00112
    00113     if (size == 0) return 0;
    00114   }
    00115   return *_fio.buffer++;
    00116 }
    00117
    00118 void FioSkipBytes(int n)
    00119 {
    00120   for (;;) {
    00121     int m = min(_fio.buffer_end - _fio.buffer, n);
    00122     _fio.buffer += m;
    00123     n -= m;
    00124     if (n == 0) break;
    00125     FioReadByte();
    00126     n--;
    00127   }
    00128 }
    00129
    00130 uint16 FioReadWord()
    00131 {
    00132   byte b = FioReadByte();
    00133   return (FioReadByte() << 8) | b;
    00134 }
    00135
    00136 uint32 FioReadDword()
    00137 {
    00138   uint b = FioReadWord();
    00139   return (FioReadWord() << 16) | b;
    00140 }
    00141
    00142 void FioReadBlock(void *ptr, size_t size)
    00143 {
    00144   FioSeekTo(FioGetPos(), SEEK_SET);
    00145   _fio.pos += fread(ptr, 1, size, _fio.cur_fh);
    00146 }
    00147
    00148 static inline void FioCloseFile(int slot)
    00149 {
    00150   if (_fio.handles[slot] != NULL) {
    00151     fclose(_fio.handles[slot]);
    00152
    00153     free(_fio.shortnames[slot]);
    00154     _fio.shortnames[slot] = NULL;
    00155
    00156     _fio.handles[slot] = NULL;
    00157 #if defined(LIMITED_FDS)
    00158     _fio.open_handles--;
    00159 #endif /* LIMITED_FDS */
    00160   }
    00161 }
    00162
    00163 void FioCloseAll()
    00164 {
    00165   int i;
    00166
    00167   for (i = 0; i != lengthof(_fio.handles); i++)
    00168     FioCloseFile(i);
    00169 }
    00170
    00171 #if defined(LIMITED_FDS)
    00172 static void FioFreeHandle()
    00173 {
    00174   /* If we are about to open a file that will exceed the limit, close a file */
    00175   if (_fio.open_handles + 1 == LIMITED_FDS) {
    00176     uint i, count;
    00177     int slot;
    00178
    00179     count = UINT_MAX;
    00180     slot = -1;
    00181     /* Find the file that is used the least */
    00182     for (i = 0; i < lengthof(_fio.handles); i++) {
    00183       if (_fio.handles[i] != NULL && _fio.usage_count[i] < count) {
    00184         count = _fio.usage_count[i];
    00185         slot  = i;
    00186       }
    00187     }
    00188     assert(slot != -1);
    00189     DEBUG(misc, 6, "Closing filehandler '%s' in slot '%d' because of fd-limit", _fio.filenames[slot], slot);
    00190     FioCloseFile(slot);
    00191   }
    00192 }
    00193 #endif /* LIMITED_FDS */
    00194
    00195 void FioOpenFile(int slot, const char *filename)
    00196 {
    00197   FILE *f;
    00198
    00199 #if defined(LIMITED_FDS)
    00200   FioFreeHandle();
    00201 #endif /* LIMITED_FDS */
    00202   f = FioFOpenFile(filename);
    00203   if (f == NULL) usererror("Cannot open file '%s'", filename);
    00204   uint32 pos = ftell(f);
    00205
    00206   FioCloseFile(slot); // if file was opened before, close it
    00207   _fio.handles[slot] = f;
    00208   _fio.filenames[slot] = filename;
    00209
    00210   /* Store the filename without path and extension */
    00211   const char *t = strrchr(filename, PATHSEPCHAR);
    00212   _fio.shortnames[slot] = strdup(t == NULL ? filename : t);
    00213   char *t2 = strrchr(_fio.shortnames[slot], '.');
    00214   if (t2 != NULL) *t2 = '\0';
    00215   strtolower(_fio.shortnames[slot]);
    00216
    00217 #if defined(LIMITED_FDS)
    00218   _fio.usage_count[slot] = 0;
    00219   _fio.open_handles++;
    00220 #endif /* LIMITED_FDS */
    00221   FioSeekToFile(slot, pos);
    00222 }
    00223
    00224 static const char * const _subdirs[NUM_SUBDIRS] = {
    00225   "",
    00226   "save" PATHSEP,
    00227   "save" PATHSEP "autosave" PATHSEP,
    00228   "scenario" PATHSEP,
    00229   "scenario" PATHSEP "heightmap" PATHSEP,
    00230   "gm" PATHSEP,
    00231   "data" PATHSEP,
    00232   "lang" PATHSEP,
    00233   "ai" PATHSEP,
    00234   "ai" PATHSEP "library" PATHSEP,
    00235 };
    00236
    00237 const char *_searchpaths[NUM_SEARCHPATHS];
    00238 TarList _tar_list;
    00239 TarFileList _tar_filelist;
    00240
    00241 typedef std::map<std::string, std::string> TarLinkList;
    00242 static TarLinkList _tar_linklist;
    00243
    00250 bool FioCheckFileExists(const char *filename, Subdirectory subdir)
    00251 {
    00252   FILE *f = FioFOpenFile(filename, "rb", subdir);
    00253   if (f == NULL) return false;
    00254
    00255   FioFCloseFile(f);
    00256   return true;
    00257 }
    00258
    00262 void FioFCloseFile(FILE *f)
    00263 {
    00264   fclose(f);
    00265 }
    00266
    00267 char *FioGetFullPath(char *buf, size_t buflen, Searchpath sp, Subdirectory subdir, const char *filename)
    00268 {
    00269   assert(subdir < NUM_SUBDIRS);
    00270   assert(sp < NUM_SEARCHPATHS);
    00271
    00272   snprintf(buf, buflen, "%s%s%s", _searchpaths[sp], _subdirs[subdir], filename);
    00273   return buf;
    00274 }
    00275
    00276 char *FioFindFullPath(char *buf, size_t buflen, Subdirectory subdir, const char *filename)
    00277 {
    00278   Searchpath sp;
    00279   assert(subdir < NUM_SUBDIRS);
    00280
    00281   FOR_ALL_SEARCHPATHS(sp) {
    00282     FioGetFullPath(buf, buflen, sp, subdir, filename);
    00283     if (FileExists(buf)) break;
    00284 #if !defined(WIN32)
    00285     /* Be, as opening files, aware that sometimes the filename
    00286      * might be in uppercase when it is in lowercase on the
    00287      * disk. Ofcourse Windows doesn't care about casing. */
    00288     strtolower(buf + strlen(_searchpaths[sp]) - 1);
    00289     if (FileExists(buf)) break;
    00290 #endif
    00291   }
    00292
    00293   return buf;
    00294 }
    00295
    00296 char *FioAppendDirectory(char *buf, size_t buflen, Searchpath sp, Subdirectory subdir)
    00297 {
    00298   assert(subdir < NUM_SUBDIRS);
    00299   assert(sp < NUM_SEARCHPATHS);
    00300
    00301   snprintf(buf, buflen, "%s%s", _searchpaths[sp], _subdirs[subdir]);
    00302   return buf;
    00303 }
    00304
    00305 char *FioGetDirectory(char *buf, size_t buflen, Subdirectory subdir)
    00306 {
    00307   Searchpath sp;
    00308
    00309   /* Find and return the first valid directory */
    00310   FOR_ALL_SEARCHPATHS(sp) {
    00311     char *ret = FioAppendDirectory(buf, buflen, sp, subdir);
    00312     if (FileExists(buf)) return ret;
    00313   }
    00314
    00315   /* Could not find the directory, fall back to a base path */
    00316   ttd_strlcpy(buf, _personal_dir, buflen);
    00317
    00318   return buf;
    00319 }
    00320
    00321 static FILE *FioFOpenFileSp(const char *filename, const char *mode, Searchpath sp, Subdirectory subdir, size_t *filesize)
    00322 {
    00323 #if defined(WIN32) && defined(UNICODE)
    00324   /* fopen is implemented as a define with ellipses for
    00325    * Unicode support (prepend an L). As we are not sending
    00326    * a string, but a variable, it 'renames' the variable,
    00327    * so make that variable to makes it compile happily */
    00328   wchar_t Lmode[5];
    00329   MultiByteToWideChar(CP_ACP, 0, mode, -1, Lmode, lengthof(Lmode));
    00330 #endif
    00331   FILE *f = NULL;
    00332   char buf[MAX_PATH];
    00333
    00334   if (subdir == NO_DIRECTORY) {
    00335     strecpy(buf, filename, lastof(buf));
    00336   } else {
    00337     snprintf(buf, lengthof(buf), "%s%s%s", _searchpaths[sp], _subdirs[subdir], filename);
    00338   }
    00339
    00340 #if defined(WIN32)
    00341   if (mode[0] == 'r' && GetFileAttributes(OTTD2FS(buf)) == INVALID_FILE_ATTRIBUTES) return NULL;
    00342 #endif
    00343
    00344   f = fopen(buf, mode);
    00345 #if !defined(WIN32)
    00346   if (f == NULL) {
    00347     strtolower(buf + ((subdir == NO_DIRECTORY) ? 0 : strlen(_searchpaths[sp]) - 1));
    00348     f = fopen(buf, mode);
    00349   }
    00350 #endif
    00351   if (f != NULL && filesize != NULL) {
    00352     /* Find the size of the file */
    00353     fseek(f, 0, SEEK_END);
    00354     *filesize = ftell(f);
    00355     fseek(f, 0, SEEK_SET);
    00356   }
    00357   return f;
    00358 }
    00359
    00360 FILE *FioFOpenFileTar(TarFileListEntry *entry, size_t *filesize)
    00361 {
    00362   FILE *f = fopen(entry->tar_filename, "rb");
    00363   if (f == NULL) return f;
    00364
    00365   fseek(f, entry->position, SEEK_SET);
    00366   if (filesize != NULL) *filesize = entry->size;
    00367   return f;
    00368 }
    00369
    00371 FILE *FioFOpenFile(const char *filename, const char *mode, Subdirectory subdir, size_t *filesize)
    00372 {
    00373   FILE *f = NULL;
    00374   Searchpath sp;
    00375
    00376   assert(subdir < NUM_SUBDIRS || subdir == NO_DIRECTORY);
    00377
    00378   FOR_ALL_SEARCHPATHS(sp) {
    00379     f = FioFOpenFileSp(filename, mode, sp, subdir, filesize);
    00380     if (f != NULL || subdir == NO_DIRECTORY) break;
    00381   }
    00382
    00383   /* We can only use .tar in case of data-dir, and read-mode */
    00384   if (f == NULL && mode[0] == 'r') {
    00385     static const uint MAX_RESOLVED_LENGTH = 2 * (100 + 100 + 155) + 1; // Enough space to hold two filenames plus link. See 'TarHeader'.
    00386     char resolved_name[MAX_RESOLVED_LENGTH];
    00387
    00388     /* Filenames in tars are always forced to be lowercase */
    00389     strecpy(resolved_name, filename, lastof(resolved_name));
    00390     strtolower(resolved_name);
    00391
    00392     size_t resolved_len = strlen(resolved_name);
    00393
    00394     /* Resolve ONE directory link */
    00395     for (TarLinkList::iterator link = _tar_linklist.begin(); link != _tar_linklist.end(); link++) {
    00396       const std::string &src = link->first;
    00397       size_t len = src.length();
    00398       if (resolved_len >= len && resolved_name[len - 1] == PATHSEPCHAR && strncmp(src.c_str(), resolved_name, len) == 0) {
    00399         /* Apply link */
    00400         char resolved_name2[MAX_RESOLVED_LENGTH];
    00401         const std::string &dest = link->second;
    00402         strecpy(resolved_name2, &(resolved_name[len]), lastof(resolved_name2));
    00403         strecpy(resolved_name, dest.c_str(), lastof(resolved_name));
    00404         strecpy(&(resolved_name[dest.length()]), resolved_name2, lastof(resolved_name));
    00405         break; // Only resolve one level
    00406       }
    00407     }
    00408
    00409     TarFileList::iterator it = _tar_filelist.find(resolved_name);
    00410     if (it != _tar_filelist.end()) {
    00411       f = FioFOpenFileTar(&((*it).second), filesize);
    00412     }
    00413   }
    00414
    00415   /* Sometimes a full path is given. To support
    00416    * the 'subdirectory' must be 'removed'. */
    00417   if (f == NULL && subdir != NO_DIRECTORY) {
    00418     f = FioFOpenFile(filename, mode, NO_DIRECTORY, filesize);
    00419   }
    00420
    00421   return f;
    00422 }
    00423
    00428 void FioCreateDirectory(const char *name)
    00429 {
    00430 #if defined(WIN32) || defined(WINCE)
    00431   CreateDirectory(OTTD2FS(name), NULL);
    00432 #elif defined(OS2) && !defined(__INNOTEK_LIBC__)
    00433   mkdir(OTTD2FS(name));
    00434 #elif defined(__MORPHOS__) || defined(__AMIGAOS__)
    00435   char buf[MAX_PATH];
    00436   ttd_strlcpy(buf, name, MAX_PATH);
    00437
    00438   size_t len = strlen(name) - 1;
    00439   if (buf[len] == '/') {
    00440     buf[len] = '\0'; // Kill pathsep, so mkdir() will not fail
    00441   }
    00442
    00443   mkdir(OTTD2FS(buf), 0755);
    00444 #else
    00445   mkdir(OTTD2FS(name), 0755);
    00446 #endif
    00447 }
    00448
    00456 bool AppendPathSeparator(char *buf, size_t buflen)
    00457 {
    00458   size_t s = strlen(buf);
    00459
    00460   /* Length of string + path separator + '\0' */
    00461   if (s != 0 && buf[s - 1] != PATHSEPCHAR) {
    00462     if (s + 2 >= buflen) return false;
    00463
    00464     buf[s]     = PATHSEPCHAR;
    00465     buf[s + 1] = '\0';
    00466   }
    00467
    00468   return true;
    00469 }
    00470
    00477 char *BuildWithFullPath(const char *dir)
    00478 {
    00479   char *dest = MallocT<char>(MAX_PATH);
    00480   ttd_strlcpy(dest, dir, MAX_PATH);
    00481
    00482   /* Check if absolute or relative path */
    00483   const char *s = strchr(dest, PATHSEPCHAR);
    00484
    00485   /* Add absolute path */
    00486   if (s == NULL || dest != s) {
    00487     if (getcwd(dest, MAX_PATH) == NULL) *dest = '\0';
    00488     AppendPathSeparator(dest, MAX_PATH);
    00489     ttd_strlcat(dest, dir, MAX_PATH);
    00490   }
    00491   AppendPathSeparator(dest, MAX_PATH);
    00492
    00493   return dest;
    00494 }
    00495
    00496 const char *FioTarFirstDir(const char *tarname)
    00497 {
    00498   TarList::iterator it = _tar_list.find(tarname);
    00499   if (it == _tar_list.end()) return NULL;
    00500   return (*it).second.dirname;
    00501 }
    00502
    00503 static void TarAddLink(const std::string &srcParam, const std::string &destParam)
    00504 {
    00505   std::string src = srcParam;
    00506   std::string dest = destParam;
    00507   /* Tar internals assume lowercase */
    00508   std::transform(src.begin(), src.end(), src.begin(), tolower);
    00509   std::transform(dest.begin(), dest.end(), dest.begin(), tolower);
    00510
    00511   TarFileList::iterator dest_file = _tar_filelist.find(dest);
    00512   if (dest_file != _tar_filelist.end()) {
    00513     /* Link to file. Process the link like the destination file. */
    00514     _tar_filelist.insert(TarFileList::value_type(src, dest_file->second));
    00515   } else {
    00516     /* Destination file not found. Assume 'link to directory'
    00517      * Append PATHSEPCHAR to 'src' and 'dest' if needed */
    00518     const std::string src_path = ((*src.rbegin() == PATHSEPCHAR) ? src : src + PATHSEPCHAR);
    00519     const std::string dst_path = (dest.length() == 0 ? "" : ((*dest.rbegin() == PATHSEPCHAR) ? dest : dest + PATHSEPCHAR));
    00520     _tar_linklist.insert(TarLinkList::value_type(src_path, dst_path));
    00521   }
    00522 }
    00523
    00524 void FioTarAddLink(const char *src, const char *dest)
    00525 {
    00526   TarAddLink(src, dest);
    00527 }
    00528
    00534 static void SimplifyFileName(char *name)
    00535 {
    00536   /* Force lowercase */
    00537   strtolower(name);
    00538
    00539   /* Tar-files always have '/' path-seperator, but we want our PATHSEPCHAR */
    00540 #if (PATHSEPCHAR != '/')
    00541   for (char *n = name; *n != '\0'; n++) if (*n == '/') *n = PATHSEPCHAR;
    00542 #endif
    00543 }
    00544
    00545 /* static */ uint TarScanner::DoScan() {
    00546   DEBUG(misc, 1, "Scanning for tars");
    00547   TarScanner fs;
    00548   uint num = fs.Scan(".tar", DATA_DIR, false);
    00549   num += fs.Scan(".tar", AI_DIR, false);
    00550   num += fs.Scan(".tar", AI_LIBRARY_DIR, false);
    00551   num += fs.Scan(".tar", SCENARIO_DIR, false);
    00552   DEBUG(misc, 1, "Scan complete, found %d files", num);
    00553   return num;
    00554 }
    00555
    00556 bool TarScanner::AddFile(const char *filename, size_t basepath_length)
    00557 {
    00558   /* The TAR-header, repeated for every file */
    00559   typedef struct TarHeader {
    00560     char name[100];
    00561     char mode[8];
    00562     char uid[8];
    00563     char gid[8];
    00564     char size[12];
    00565     char mtime[12];
    00566     char chksum[8];
    00567     char typeflag;
    00568     char linkname[100];
    00569     char magic[6];
    00570     char version[2];
    00571     char uname[32];
    00572     char gname[32];
    00573     char devmajor[8];
    00574     char devminor[8];
    00575     char prefix[155];
    00576
    00577     char unused[12];
    00578   } TarHeader;
    00579
    00580   /* Check if we already seen this file */
    00581   TarList::iterator it = _tar_list.find(filename);
    00582   if (it != _tar_list.end()) return false;
    00583
    00584   FILE *f = fopen(filename, "rb");
    00585   /* Although the file has been found there can be
    00586    * a number of reasons we cannot open the file.
    00587    * Most common case is when we simply have not
    00588    * been given read access. */
    00589   if (f == NULL) return false;
    00590
    00591   const char *dupped_filename = strdup(filename);
    00592   _tar_list[filename].filename = dupped_filename;
    00593   _tar_list[filename].dirname = NULL;
    00594
    00595   TarLinkList links;
    00596
    00597   TarHeader th;
    00598   char buf[sizeof(th.name) + 1], *end;
    00599   char name[sizeof(th.prefix) + 1 + sizeof(th.name) + 1];
    00600   char link[sizeof(th.linkname) + 1];
    00601   char dest[sizeof(th.prefix) + 1 + sizeof(th.name) + 1 + 1 + sizeof(th.linkname) + 1];
    00602   size_t num = 0, pos = 0;
    00603
    00604   /* Make a char of 512 empty bytes */
    00605   char empty[512];
    00606   memset(&empty[0], 0, sizeof(empty));
    00607
    00608   for (;;) { // Note: feof() always returns 'false' after 'fseek()'. Cool, isn't it?
    00609     size_t num_bytes_read = fread(&th, 1, 512, f);
    00610     if (num_bytes_read != 512) break;
    00611     pos += num_bytes_read;
    00612
    00613     /* Check if we have the new tar-format (ustar) or the old one (a lot of zeros after 'link' field) */
    00614     if (strncmp(th.magic, "ustar", 5) != 0 && memcmp(&th.magic, &empty[0], 512 - offsetof(TarHeader, magic)) != 0) {
    00615       /* If we have only zeros in the block, it can be an end-of-file indicator */
    00616       if (memcmp(&th, &empty[0], 512) == 0) continue;
    00617
    00618       DEBUG(misc, 0, "The file '%s' isn't a valid tar-file", filename);
    00619       return false;
    00620     }
    00621
    00622     name[0] = '\0';
    00623     size_t len = 0;
    00624
    00625     /* The prefix contains the directory-name */
    00626     if (th.prefix[0] != '\0') {
    00627       memcpy(name, th.prefix, sizeof(th.prefix));
    00628       name[sizeof(th.prefix)] = '\0';
    00629       len = strlen(name);
    00630       name[len] = PATHSEPCHAR;
    00631       len++;
    00632     }
    00633
    00634     /* Copy the name of the file in a safe way at the end of 'name' */
    00635     memcpy(&name[len], th.name, sizeof(th.name));
    00636     name[len + sizeof(th.name)] = '\0';
    00637
    00638     /* Calculate the size of the file.. for some strange reason this is stored as a string */
    00639     memcpy(buf, th.size, sizeof(th.size));
    00640     buf[sizeof(th.size)] = '\0';
    00641     size_t skip = strtoul(buf, &end, 8);
    00642
    00643     switch (th.typeflag) {
    00644       case '\0':
    00645       case '0': { // regular file
    00646         /* Ignore empty files */
    00647         if (skip == 0) break;
    00648
    00649         if (strlen(name) == 0) break;
    00650
    00651         /* Store this entry in the list */
    00652         TarFileListEntry entry;
    00653         entry.tar_filename = dupped_filename;
    00654         entry.size         = skip;
    00655         entry.position     = pos;
    00656
    00657         /* Convert to lowercase and our PATHSEPCHAR */
    00658         SimplifyFileName(name);
    00659
    00660         DEBUG(misc, 6, "Found file in tar: %s (" PRINTF_SIZE " bytes, " PRINTF_SIZE " offset)", name, skip, pos);
    00661         if (_tar_filelist.insert(TarFileList::value_type(name, entry)).second) num++;
    00662
    00663         break;
    00664       }
    00665
    00666       case '1': // hard links
    00667       case '2': { // symbolic links
    00668         /* Copy the destination of the link in a safe way at the end of 'linkname' */
    00669         memcpy(link, th.linkname, sizeof(th.linkname));
    00670         link[sizeof(th.linkname)] = '\0';
    00671
    00672         if (strlen(name) == 0 || strlen(link) == 0) break;
    00673
    00674         /* Convert to lowercase and our PATHSEPCHAR */
    00675         SimplifyFileName(name);
    00676         SimplifyFileName(link);
    00677
    00678         /* Only allow relative links */
    00679         if (link[0] == PATHSEPCHAR) {
    00680           DEBUG(misc, 1, "Ignoring absolute link in tar: %s -> %s", name, link);
    00681           break;
    00682         }
    00683
    00684         /* Process relative path.
    00685          * Note: The destination of links must not contain any directory-links. */
    00686         strecpy(dest, name, lastof(dest));
    00687         char *destpos = strrchr(dest, PATHSEPCHAR);
    00688         if (destpos == NULL) destpos = dest;
    00689         *destpos = '\0';
    00690
    00691         char *pos = link;
    00692         while (*pos != '\0') {
    00693           char *next = strchr(link, PATHSEPCHAR);
    00694           if (next == NULL) next = pos + strlen(pos);
    00695
    00696           /* Skip '.' (current dir) */
    00697           if (next != pos + 1 || pos[0] != '.') {
    00698             if (next == pos + 2 && pos[0] == '.' && pos[1] == '.') {
    00699               /* level up */
    00700               if (dest[0] == '\0') {
    00701                 DEBUG(misc, 1, "Ignoring link pointing outside of data directory: %s -> %s", name, link);
    00702                 break;
    00703               }
    00704
    00705               /* Truncate 'dest' after last PATHSEPCHAR.
    00706                * This assumes, that the truncated part is a real directory and not a link */
    00707               destpos = strrchr(dest, PATHSEPCHAR);
    00708               if (destpos == NULL) destpos = dest;
    00709             } else {
    00710               /* Append at end of 'dest' */
    00711               if (destpos != dest) *(destpos++) = PATHSEPCHAR;
    00712               strncpy(destpos, pos, next - pos); // Safe as we do '\0'-termination ourselves
    00713               destpos += next - pos;
    00714             }
    00715             *destpos = '\0';
    00716           }
    00717
    00718           pos = next;
    00719         }
    00720
    00721         /* Store links in temporary list */
    00722         DEBUG(misc, 6, "Found link in tar: %s -> %s", name, dest);
    00723         links.insert(TarLinkList::value_type(name, dest));
    00724
    00725         break;
    00726       }
    00727
    00728       case '5': // directory
    00729         /* Convert to lowercase and our PATHSEPCHAR */
    00730         SimplifyFileName(name);
    00731
    00732         /* Store the first directory name we detect */
    00733         DEBUG(misc, 6, "Found dir in tar: %s", name);
    00734         if (_tar_list[filename].dirname == NULL) _tar_list[filename].dirname = strdup(name);
    00735         break;
    00736
    00737       default:
    00738         /* Ignore other types */
    00739         break;
    00740     }
    00741
    00742     /* Skip to the next block.. */
    00743     skip = Align(skip, 512);
    00744     fseek(f, skip, SEEK_CUR);
    00745     pos += skip;
    00746   }
    00747
    00748   DEBUG(misc, 1, "Found tar '%s' with " PRINTF_SIZE " new files", filename, num);
    00749   fclose(f);
    00750
    00751   /* Resolve file links and store directory links.
    00752    * We restrict usage of links to two cases:
    00753    *  1) Links to directories:
    00754    *      Both the source path and the destination path must NOT contain any further links.
    00755    *      When resolving files at most one directory link is resolved.
    00756    *  2) Links to files:
    00757    *      The destination path must NOT contain any links.
    00758    *      The source path may contain one directory link.
    00759    */
    00760   for (TarLinkList::iterator link = links.begin(); link != links.end(); link++) {
    00761     const std::string &src = link->first;
    00762     const std::string &dest = link->second;
    00763     TarAddLink(src, dest);
    00764   }
    00765
    00766   return true;
    00767 }
    00768
    00775 bool ExtractTar(const char *tar_filename)
    00776 {
    00777   TarList::iterator it = _tar_list.find(tar_filename);
    00778   /* We don't know the file. */
    00779   if (it == _tar_list.end()) return false;
    00780
    00781   const char *dirname = (*it).second.dirname;
    00782
    00783   /* The file doesn't have a sub directory! */
    00784   if (dirname == NULL) return false;
    00785
    00786   char filename[MAX_PATH];
    00787   strecpy(filename, tar_filename, lastof(filename));
    00788   char *p = strrchr(filename, PATHSEPCHAR);
    00789   /* The file's path does not have a separator? */
    00790   if (p == NULL) return false;
    00791
    00792   p++;
    00793   strecpy(p, dirname, lastof(filename));
    00794   DEBUG(misc, 8, "Extracting %s to directory %s", tar_filename, filename);
    00795   FioCreateDirectory(filename);
    00796
    00797   for (TarFileList::iterator it2 = _tar_filelist.begin(); it2 != _tar_filelist.end(); it2++) {
    00798     if (strcmp((*it2).second.tar_filename, tar_filename) != 0) continue;
    00799
    00800     strecpy(p, (*it2).first.c_str(), lastof(filename));
    00801
    00802     DEBUG(misc, 9, "  extracting %s", filename);
    00803
    00804     /* First open the file in the .tar. */
    00805     size_t to_copy = 0;
    00806     FILE *in = FioFOpenFileTar(&(*it2).second, &to_copy);
    00807     if (in == NULL) {
    00808       DEBUG(misc, 6, "Extracting %s failed; could not open %s", filename, tar_filename);
    00809       return false;
    00810     }
    00811
    00812     /* Now open the 'output' file. */
    00813     FILE *out = fopen(filename, "wb");
    00814     if (out == NULL) {
    00815       DEBUG(misc, 6, "Extracting %s failed; could not open %s", filename, filename);
    00816       fclose(in);
    00817       return false;
    00818     }
    00819
    00820     /* Now read from the tar and write it into the file. */
    00821     char buffer[4096];
    00822     size_t read;
    00823     for (; to_copy != 0; to_copy -= read) {
    00824       read = fread(buffer, 1, min(to_copy, lengthof(buffer)), in);
    00825       if (read <= 0 || fwrite(buffer, 1, read, out) != read) break;
    00826     }
    00827
    00828     /* Close everything up. */
    00829     fclose(in);
    00830     fclose(out);
    00831
    00832     if (to_copy != 0) {
    00833       DEBUG(misc, 6, "Extracting %s failed; still %i bytes to copy", filename, (int)to_copy);
    00834       return false;
    00835     }
    00836   }
    00837
    00838   DEBUG(misc, 9, "  extraction successful");
    00839   return true;
    00840 }
    00841
    00842 #if defined(WIN32) || defined(WINCE)
    00843
    00848 extern void DetermineBasePaths(const char *exe);
    00849 #else /* defined(WIN32) || defined(WINCE) */
    00850
    00858 void ChangeWorkingDirectory(const char *exe)
    00859 {
    00860 #ifdef WITH_COCOA
    00861   char *app_bundle = strchr(exe, '.');
    00862   while (app_bundle != NULL && strncasecmp(app_bundle, ".app", 4) != 0) app_bundle = strchr(&app_bundle[1], '.');
    00863
    00864   if (app_bundle != NULL) app_bundle[0] = '\0';
    00865 #endif /* WITH_COCOA */
    00866   char *s = const_cast<char *>(strrchr(exe, PATHSEPCHAR));
    00867   if (s != NULL) {
    00868     *s = '\0';
    00869 #if defined(__DJGPP__)
    00870     /* If we want to go to the root, we can't use cd C:, but we must use '/' */
    00871     if (s[-1] == ':') chdir("/");
    00872 #endif
    00873     if (chdir(exe) != 0) DEBUG(misc, 0, "Directory with the binary does not exist?");
    00874     *s = PATHSEPCHAR;
    00875   }
    00876 #ifdef WITH_COCOA
    00877   if (app_bundle != NULL) app_bundle[0] = '.';
    00878 #endif /* WITH_COCOA */
    00879 }
    00880
    00891 bool DoScanWorkingDirectory()
    00892 {
    00893   /* No working directory, so nothing to do. */
    00894   if (_searchpaths[SP_WORKING_DIR] == NULL) return false;
    00895
    00896   /* Working directory is root, so do nothing. */
    00897   if (strcmp(_searchpaths[SP_WORKING_DIR], PATHSEP) == 0) return false;
    00898
    00899   /* No personal/home directory, so the working directory won't be that. */
    00900   if (_searchpaths[SP_PERSONAL_DIR] == NULL) return true;
    00901
    00902   char tmp[MAX_PATH];
    00903   snprintf(tmp, lengthof(tmp), "%s%s", _searchpaths[SP_WORKING_DIR], PERSONAL_DIR);
    00904   AppendPathSeparator(tmp, MAX_PATH);
    00905   return strcmp(tmp, _searchpaths[SP_PERSONAL_DIR]) != 0;
    00906 }
    00907
    00912 void DetermineBasePaths(const char *exe)
    00913 {
    00914   char tmp[MAX_PATH];
    00915 #if defined(__MORPHOS__) || defined(__AMIGA__) || defined(DOS) || defined(OS2) || !defined(WITH_PERSONAL_DIR)
    00916   _searchpaths[SP_PERSONAL_DIR] = NULL;
    00917 #else
    00918 #ifdef __HAIKU__
    00919   BPath path;
    00920   find_directory(B_USER_SETTINGS_DIRECTORY, &path);
    00921   const char *homedir = path.Path();
    00922 #else
    00923   const char *homedir = getenv("HOME");
    00924
    00925   if (homedir == NULL) {
    00926     const struct passwd *pw = getpwuid(getuid());
    00927     homedir = (pw == NULL) ? "" : pw->pw_dir;
    00928   }
    00929 #endif
    00930
    00931   snprintf(tmp, MAX_PATH, "%s" PATHSEP "%s", homedir, PERSONAL_DIR);
    00932   AppendPathSeparator(tmp, MAX_PATH);
    00933
    00934   _searchpaths[SP_PERSONAL_DIR] = strdup(tmp);
    00935 #endif
    00936
    00937 #if defined(WITH_SHARED_DIR)
    00938   snprintf(tmp, MAX_PATH, "%s", SHARED_DIR);
    00939   AppendPathSeparator(tmp, MAX_PATH);
    00940   _searchpaths[SP_SHARED_DIR] = strdup(tmp);
    00941 #else
    00942   _searchpaths[SP_SHARED_DIR] = NULL;
    00943 #endif
    00944
    00945 #if defined(__MORPHOS__) || defined(__AMIGA__)
    00946   _searchpaths[SP_WORKING_DIR] = NULL;
    00947 #else
    00948   if (getcwd(tmp, MAX_PATH) == NULL) *tmp = '\0';
    00949   AppendPathSeparator(tmp, MAX_PATH);
    00950   _searchpaths[SP_WORKING_DIR] = strdup(tmp);
    00951 #endif
    00952
    00953   _do_scan_working_directory = DoScanWorkingDirectory();
    00954
    00955   /* Change the working directory to that one of the executable */
    00956   ChangeWorkingDirectory(exe);
    00957   if (getcwd(tmp, MAX_PATH) == NULL) *tmp = '\0';
    00958   AppendPathSeparator(tmp, MAX_PATH);
    00959   _searchpaths[SP_BINARY_DIR] = strdup(tmp);
    00960
    00961   if (_searchpaths[SP_WORKING_DIR] != NULL) {
    00962     /* Go back to the current working directory. */
    00963     ChangeWorkingDirectory(_searchpaths[SP_WORKING_DIR]);
    00964   }
    00965
    00966 #if defined(__MORPHOS__) || defined(__AMIGA__) || defined(DOS) || defined(OS2)
    00967   _searchpaths[SP_INSTALLATION_DIR] = NULL;
    00968 #else
    00969   snprintf(tmp, MAX_PATH, "%s", GLOBAL_DATA_DIR);
    00970   AppendPathSeparator(tmp, MAX_PATH);
    00971   _searchpaths[SP_INSTALLATION_DIR] = strdup(tmp);
    00972 #endif
    00973 #ifdef WITH_COCOA
    00974 extern void cocoaSetApplicationBundleDir();
    00975   cocoaSetApplicationBundleDir();
    00976 #else
    00977   _searchpaths[SP_APPLICATION_BUNDLE_DIR] = NULL;
    00978 #endif
    00979 }
    00980 #endif /* defined(WIN32) || defined(WINCE) */
    00981
    00982 char *_personal_dir;
    00983
    00990 void DeterminePaths(const char *exe)
    00991 {
    00992   DetermineBasePaths(exe);
    00993
    00994   Searchpath sp;
    00995   FOR_ALL_SEARCHPATHS(sp) {
    00996     if (sp == SP_WORKING_DIR && !_do_scan_working_directory) continue;
    00997     DEBUG(misc, 4, "%s added as search path", _searchpaths[sp]);
    00998   }
    00999
    01000   if (_config_file != NULL) {
    01001     _personal_dir = strdup(_config_file);
    01002     char *end = strrchr(_personal_dir, PATHSEPCHAR);
    01003     if (end == NULL) {
    01004       _personal_dir[0] = '\0';
    01005     } else {
    01006       end[1] = '\0';
    01007     }
    01008   } else {
    01009     char personal_dir[MAX_PATH];
    01010     FioFindFullPath(personal_dir, lengthof(personal_dir), BASE_DIR, "openttd.cfg");
    01011
    01012     if (FileExists(personal_dir)) {
    01013       char *end = strrchr(personal_dir, PATHSEPCHAR);
    01014       if (end != NULL) end[1] = '\0';
    01015       _personal_dir = strdup(personal_dir);
    01016       _config_file = str_fmt("%sopenttd.cfg", _personal_dir);
    01017     } else {
    01018       static const Searchpath new_openttd_cfg_order[] = {
    01019           SP_PERSONAL_DIR, SP_BINARY_DIR, SP_WORKING_DIR, SP_SHARED_DIR, SP_INSTALLATION_DIR
    01020         };
    01021
    01022       for (uint i = 0; i < lengthof(new_openttd_cfg_order); i++) {
    01023         if (IsValidSearchPath(new_openttd_cfg_order[i])) {
    01024           _personal_dir = strdup(_searchpaths[new_openttd_cfg_order[i]]);
    01025           _config_file = str_fmt("%sopenttd.cfg", _personal_dir);
    01026           break;
    01027         }
    01028       }
    01029     }
    01030   }
    01031
    01032   DEBUG(misc, 3, "%s found as personal directory", _personal_dir);
    01033
    01034   _highscore_file = str_fmt("%shs.dat", _personal_dir);
    01035   _log_file = str_fmt("%sopenttd.log",  _personal_dir);
    01036
    01037   /* Make the necessary folders */
    01038 #if !defined(__MORPHOS__) && !defined(__AMIGA__) && defined(WITH_PERSONAL_DIR)
    01039   FioCreateDirectory(_personal_dir);
    01040 #endif
    01041
    01042   static const Subdirectory default_subdirs[] = {
    01043     SAVE_DIR, AUTOSAVE_DIR, SCENARIO_DIR, HEIGHTMAP_DIR
    01044   };
    01045
    01046   for (uint i = 0; i < lengthof(default_subdirs); i++) {
    01047     char *dir = str_fmt("%s%s", _personal_dir, _subdirs[default_subdirs[i]]);
    01048     FioCreateDirectory(dir);
    01049     free(dir);
    01050   }
    01051
    01052   /* If we have network we make a directory for the autodownloading of content */
    01053   _searchpaths[SP_AUTODOWNLOAD_DIR] = str_fmt("%s%s", _personal_dir, "content_download" PATHSEP);
    01054 #ifdef ENABLE_NETWORK
    01055   FioCreateDirectory(_searchpaths[SP_AUTODOWNLOAD_DIR]);
    01056
    01057   /* Create the directory for each of the types of content */
    01058   const Subdirectory dirs[] = { SCENARIO_DIR, HEIGHTMAP_DIR, DATA_DIR, AI_DIR, AI_LIBRARY_DIR, GM_DIR };
    01059   for (uint i = 0; i < lengthof(dirs); i++) {
    01060     char *tmp = str_fmt("%s%s", _searchpaths[SP_AUTODOWNLOAD_DIR], _subdirs[dirs[i]]);
    01061     FioCreateDirectory(tmp);
    01062     free(tmp);
    01063   }
    01064 #else /* ENABLE_NETWORK */
    01065   /* If we don't have networking, we don't need to make the directory. But
    01066    * if it exists we keep it, otherwise remove it from the search paths. */
    01067   if (!FileExists(_searchpaths[SP_AUTODOWNLOAD_DIR]))  {
    01068     free((void*)_searchpaths[SP_AUTODOWNLOAD_DIR]);
    01069     _searchpaths[SP_AUTODOWNLOAD_DIR] = NULL;
    01070   }
    01071 #endif /* ENABLE_NETWORK */
    01072
    01073   TarScanner::DoScan();
    01074 }
    01075
    01080 void SanitizeFilename(char *filename)
    01081 {
    01082   for (; *filename != '\0'; filename++) {
    01083     switch (*filename) {
    01084       /* The following characters are not allowed in filenames
    01085        * on at least one of the supported operating systems: */
    01086       case ':': case '\\': case '*': case '?': case '/':
    01087       case '<': case '>': case '|': case '"':
    01088         *filename = '_';
    01089         break;
    01090     }
    01091   }
    01092 }
    01093
    01094 void *ReadFileToMem(const char *filename, size_t *lenp, size_t maxsize)
    01095 {
    01096   FILE *in = fopen(filename, "rb");
    01097   if (in == NULL) return NULL;
    01098
    01099   fseek(in, 0, SEEK_END);
    01100   size_t len = ftell(in);
    01101   fseek(in, 0, SEEK_SET);
    01102   if (len > maxsize) {
    01103     fclose(in);
    01104     return NULL;
    01105   }
    01106   byte *mem = MallocT<byte>(len + 1);
    01107   mem[len] = 0;
    01108   if (fread(mem, len, 1, in) != 1) {
    01109     fclose(in);
    01110     free(mem);
    01111     return NULL;
    01112   }
    01113   fclose(in);
    01114
    01115   *lenp = len;
    01116   return mem;
    01117 }
    01118
    01119
    01129 static uint ScanPath(FileScanner *fs, const char *extension, const char *path, size_t basepath_length, bool recursive)
    01130 {
    01131   extern bool FiosIsValidFile(const char *path, const struct dirent *ent, struct stat *sb);
    01132
    01133   uint num = 0;
    01134   struct stat sb;
    01135   struct dirent *dirent;
    01136   DIR *dir;
    01137
    01138   if (path == NULL || (dir = ttd_opendir(path)) == NULL) return 0;
    01139
    01140   while ((dirent = readdir(dir)) != NULL) {
    01141     const char *d_name = FS2OTTD(dirent->d_name);
    01142     char filename[MAX_PATH];
    01143
    01144     if (!FiosIsValidFile(path, dirent, &sb)) continue;
    01145
    01146     snprintf(filename, lengthof(filename), "%s%s", path, d_name);
    01147
    01148     if (S_ISDIR(sb.st_mode)) {
    01149       /* Directory */
    01150       if (!recursive) continue;
    01151       if (strcmp(d_name, ".") == 0 || strcmp(d_name, "..") == 0) continue;
    01152       if (!AppendPathSeparator(filename, lengthof(filename))) continue;
    01153       num += ScanPath(fs, extension, filename, basepath_length, recursive);
    01154     } else if (S_ISREG(sb.st_mode)) {
    01155       /* File */
    01156       if (extension != NULL) {
    01157         char *ext = strrchr(filename, '.');
    01158
    01159         /* If no extension or extension isn't .grf, skip the file */
    01160         if (ext == NULL) continue;
    01161         if (strcasecmp(ext, extension) != 0) continue;
    01162       }
    01163
    01164       if (fs->AddFile(filename, basepath_length)) num++;
    01165     }
    01166   }
    01167
    01168   closedir(dir);
    01169
    01170   return num;
    01171 }
    01172
    01179 static uint ScanTar(FileScanner *fs, const char *extension, TarFileList::iterator tar)
    01180 {
    01181   uint num = 0;
    01182   const char *filename = (*tar).first.c_str();
    01183
    01184   if (extension != NULL) {
    01185     const char *ext = strrchr(filename, '.');
    01186
    01187     /* If no extension or extension isn't .grf, skip the file */
    01188     if (ext == NULL) return false;
    01189     if (strcasecmp(ext, extension) != 0) return false;
    01190   }
    01191
    01192   if (fs->AddFile(filename, 0)) num++;
    01193
    01194   return num;
    01195 }
    01196
    01206 uint FileScanner::Scan(const char *extension, Subdirectory sd, bool tars, bool recursive)
    01207 {
    01208   Searchpath sp;
    01209   char path[MAX_PATH];
    01210   TarFileList::iterator tar;
    01211   uint num = 0;
    01212
    01213   FOR_ALL_SEARCHPATHS(sp) {
    01214     /* Don't search in the working directory */
    01215     if (sp == SP_WORKING_DIR && !_do_scan_working_directory) continue;
    01216
    01217     FioAppendDirectory(path, MAX_PATH, sp, sd);
    01218     num += ScanPath(this, extension, path, strlen(path), recursive);
    01219   }
    01220
    01221   if (tars) {
    01222     FOR_ALL_TARS(tar) {
    01223       num += ScanTar(this, extension, tar);
    01224     }
    01225   }
    01226
    01227   return num;
    01228 }
    01229
    01238 uint FileScanner::Scan(const char *extension, const char *directory, bool recursive)
    01239 {
    01240   char path[MAX_PATH];
    01241   strecpy(path, directory, lastof(path));
    01242   if (!AppendPathSeparator(path, lengthof(path))) return 0;
    01243   return ScanPath(this, extension, path, strlen(path), recursive);
    01244 }