filesystem.cpp

Go to the documentation of this file.
00001 /* $Id: filesystem.cpp 26482 2008-05-09 12:31:23Z suokko $ */
00002 /*
00003    Copyright (C) 2003 - 2008 by David White <dave@whitevine.net>
00004    Part of the Battle for Wesnoth Project http://www.wesnoth.org/
00005 
00006    This program is free software; you can redistribute it and/or modify
00007    it under the terms of the GNU General Public License version 2
00008    or at your option any later version.
00009    This program is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY.
00011 
00012    See the COPYING file for more details.
00013 */
00014 
00015 //! @file filesystem.cpp 
00016 //! File-IO
00017 
00018 #include "global.hpp"
00019 
00020 // Include files for opendir(3), readdir(3), etc. 
00021 // These files may vary from platform to platform, 
00022 // since these functions are NOT ANSI-conforming functions. 
00023 // They may have to be altered to port to new platforms
00024 #include <sys/types.h>
00025 
00026 //for mkdir
00027 #include <sys/stat.h>
00028 
00029 #ifdef _WIN32
00030 #include "filesystem_win32.ii"
00031 #else /* !_WIN32 */
00032 #include <unistd.h>
00033 #include <dirent.h>
00034 #endif /* !_WIN32 */
00035 
00036 #ifdef __BEOS__
00037 #include <Directory.h>
00038 #include <FindDirectory.h>
00039 #include <Path.h>
00040 BPath be_path;
00041 #endif
00042 
00043 // for getenv
00044 #include <cstdlib>
00045 #include <cerrno>
00046 #include <string>
00047 #include <cstring>
00048 #include <algorithm>
00049 #include <fstream>
00050 #include <iostream>
00051 #include <iomanip>
00052 #include <sstream>
00053 #include <set>
00054 
00055 #include "wesconfig.h"
00056 #include "config.hpp"
00057 #include "filesystem.hpp"
00058 #include "game_config.hpp"
00059 #include "log.hpp"
00060 #include "scoped_resource.hpp"
00061 #include "util.hpp"
00062 #include "loadscreen.hpp"
00063 
00064 #define DBG_FS LOG_STREAM(debug, filesystem)
00065 #define LOG_FS LOG_STREAM(info, filesystem)
00066 #define ERR_FS LOG_STREAM(err, filesystem)
00067 
00068 namespace {
00069     const mode_t AccessMode = 00770;
00070 }
00071 
00072 #ifdef __APPLE__
00073 #include <CoreFoundation/CoreFoundation.h>
00074 #include <CoreFoundation/CFString.h>
00075 #include <CoreFoundation/CFBase.h>
00076 #endif
00077 
00078 bool ends_with(const std::string& str, const std::string& suffix)
00079 {
00080     return str.size() >= suffix.size() && std::equal(suffix.begin(),suffix.end(),str.end()-suffix.size());
00081 }
00082 
00083 // These are the filenames that get special processing
00084 #define MAINCFG "_main.cfg"
00085 #define FINALCFG    "_final.cfg"
00086 
00087 // Don't pass directory as reference, it seems to break on 
00088 // arklinux with GCC-4.3.
00089 void get_files_in_dir(const std::string directory,
00090                       std::vector<std::string>* files,
00091                       std::vector<std::string>* dirs,
00092                       FILE_NAME_MODE mode,
00093                       FILE_FILTER filter,
00094                       FILE_REORDER_OPTION reorder)
00095 {
00096     // If we have a path to find directories in, 
00097     // then convert relative pathnames to be rooted 
00098     // on the wesnoth path
00099 #ifndef __AMIGAOS4__
00100     if(!directory.empty() && directory[0] != '/' && !game_config::path.empty()){
00101         const std::string& dir = game_config::path + "/" + directory;
00102         if(is_directory(dir)) {
00103             get_files_in_dir(dir,files,dirs,mode,filter,reorder);
00104             return;
00105         }
00106     }
00107 #endif /* __AMIGAOS4__ */
00108 
00109     struct stat st;
00110 
00111     if (reorder == DO_REORDER) {
00112         LOG_FS << "searching _main.cfg in directory " << directory << '\n';
00113         std::string maincfg;
00114         if (directory.empty() || directory[directory.size()-1] == '/'
00115 #ifdef __AMIGAOS4__
00116             || (directory[directory.size()-1]==':')
00117 #endif /* __AMIGAOS4__ */
00118         )
00119             maincfg = directory + MAINCFG;
00120         else
00121             maincfg = (directory + "/") + MAINCFG;
00122 
00123         if (::stat(maincfg.c_str(), &st) != -1) {
00124             LOG_FS << "_main.cfg found : " << maincfg << '\n';
00125             if (files != NULL) {
00126                 if (mode == ENTIRE_FILE_PATH)
00127                     files->push_back(maincfg);
00128                 else
00129                     files->push_back(MAINCFG);
00130             }
00131             return;
00132         }
00133     }
00134 
00135     DIR* dir = opendir(directory.c_str());
00136 
00137     if(dir == NULL) {
00138         return;
00139     }
00140 
00141     struct dirent* entry;
00142     while((entry = readdir(dir)) != NULL) {
00143         if(entry->d_name[0] == '.')
00144             continue;
00145 #ifdef __APPLE__
00146         // HFS Mac OS X decomposes filenames using combining unicode characters. 
00147         // Try to get the precomposed form.
00148         char macname[MAXNAMLEN+1];
00149         CFStringRef cstr = CFStringCreateWithCString(NULL,
00150                              entry->d_name,
00151                              kCFStringEncodingUTF8);
00152         CFMutableStringRef mut_str = CFStringCreateMutableCopy(NULL,
00153                              0, cstr);
00154         CFStringNormalize(mut_str, kCFStringNormalizationFormC);
00155         CFStringGetCString(mut_str,
00156                 macname,sizeof(macname)-1,
00157                 kCFStringEncodingUTF8);
00158         CFRelease(cstr);
00159         CFRelease(mut_str);
00160         const std::string basename = macname;
00161 #else
00162         // generic Unix 
00163         const std::string basename = entry->d_name;
00164 #endif /* !APPLE */
00165 
00166         std::string fullname;
00167         if (directory.empty() || directory[directory.size()-1] == '/'
00168 #ifdef __AMIGAOS4__
00169             || (directory[directory.size()-1]==':')
00170 #endif /* __AMIGAOS4__ */
00171         )
00172             fullname = directory + basename;
00173         else
00174             fullname = directory + "/" + basename;
00175 
00176         if (::stat(fullname.c_str(), &st) != -1) {
00177             if (S_ISREG(st.st_mode)) {
00178                 if (files != NULL) {
00179                     if (mode == ENTIRE_FILE_PATH)
00180                         files->push_back(fullname);
00181                     else
00182                         files->push_back(basename);
00183                 }
00184             } else if (S_ISDIR(st.st_mode)) {
00185                 if (filter == SKIP_MEDIA_DIR
00186                         && (basename == "images"|| basename == "sounds"))
00187                     continue;
00188             
00189                 if (reorder == DO_REORDER &&
00190                         ::stat((fullname+"/"+MAINCFG).c_str(), &st)!=-1 &&
00191                         S_ISREG(st.st_mode)) {
00192                     LOG_FS << "_main.cfg found : ";
00193                     if (files != NULL) {
00194                         if (mode == ENTIRE_FILE_PATH) {
00195                             files->push_back(fullname + "/" + MAINCFG);
00196                             LOG_FS << fullname << "/" << MAINCFG << '\n';
00197                         } else {
00198                             files->push_back(basename + "/" + MAINCFG);
00199                             LOG_FS << basename << "/" << MAINCFG << '\n';
00200                     }
00201                     } else {
00202                     // Show what I consider strange
00203                         LOG_FS << fullname << "/" << MAINCFG << " not used now but skip the directory \n";
00204                     }
00205                 } else if (dirs != NULL) {
00206                     if (mode == ENTIRE_FILE_PATH)
00207                         dirs->push_back(fullname);
00208                     else
00209                         dirs->push_back(basename);
00210                 }
00211             }
00212         }
00213     }
00214 
00215     closedir(dir);
00216 
00217     if(files != NULL)
00218         std::sort(files->begin(),files->end());
00219 
00220     if (dirs != NULL)
00221         std::sort(dirs->begin(),dirs->end());
00222 
00223     if (files != NULL && reorder == DO_REORDER) {
00224         for (unsigned int i = 0; i < files->size(); i++)
00225             if (ends_with((*files)[i], FINALCFG)) {
00226                 files->push_back((*files)[i]);
00227                 files->erase(files->begin()+i);
00228                 break;
00229             }
00230     }
00231 }
00232 
00233 std::string get_prefs_file()
00234 {
00235     return get_user_data_dir() + "/preferences";
00236 }
00237 
00238 std::string get_save_index_file()
00239 {
00240     return get_user_data_dir() + "/save_index";
00241 }
00242 
00243 std::string get_saves_dir()
00244 {
00245     const std::string dir_path = get_user_data_dir() + "/saves";
00246     return get_dir(dir_path);
00247 }
00248 
00249 std::string get_cache_dir()
00250 {
00251     const std::string dir_path = get_user_data_dir() + "/cache";
00252     return get_dir(dir_path);
00253 }
00254 
00255 std::string get_intl_dir()
00256 {
00257 #ifdef _WIN32
00258     return get_cwd() + "/po";
00259 #endif
00260 
00261 #ifdef USE_INTERNAL_DATA
00262     return get_cwd() + "/" LOCALEDIR;
00263 #endif
00264 
00265 #if HAS_RELATIVE_LOCALEDIR
00266     std::string res = game_config::path + "/" LOCALEDIR;
00267 #else
00268     std::string res = LOCALEDIR;
00269 #endif
00270 
00271     return res;
00272 }
00273 
00274 std::string get_screenshot_dir()
00275 {
00276     const std::string dir_path = get_user_data_dir() + "/screenshots";
00277     return get_dir(dir_path);
00278 }
00279 
00280 std::string get_next_filename(const std::string& name, const std::string& extension)
00281 {
00282     std::string next_filename;
00283     int counter = 0;
00284 
00285     do {
00286         std::stringstream filename;
00287 
00288         filename << name;
00289         filename.width(3);
00290         filename.fill('0');
00291         filename.setf(std::ios_base::right);
00292         filename << counter << extension;
00293         counter++;
00294         next_filename = filename.str();
00295     } while(file_exists(next_filename) && counter < 1000);
00296     return next_filename;
00297 }
00298 
00299 
00300 std::string get_upload_dir()
00301 {
00302     const std::string dir_path = get_user_data_dir() + "/upload";
00303     return get_dir(dir_path);
00304 }
00305 
00306 std::string get_dir(const std::string& dir_path)
00307 {
00308     DIR* dir = opendir(dir_path.c_str());
00309     if(dir == NULL) {
00310         const int res = mkdir(dir_path.c_str(),AccessMode);
00311         if(res == 0) {
00312             dir = opendir(dir_path.c_str());
00313         } else {
00314             ERR_FS << "could not open or create directory: " << dir_path << '\n';
00315         }
00316     }
00317 
00318     if(dir == NULL)
00319         return "";
00320 
00321     closedir(dir);
00322 
00323     return dir_path;
00324 }
00325 
00326 bool make_directory(const std::string& path)
00327 {
00328     return (mkdir(path.c_str(),AccessMode) == 0);
00329 }
00330 
00331 // This deletes a directory with no hidden files and subdirectories.
00332 // Also deletes a single file.
00333 bool delete_directory(const std::string& path)
00334 {
00335     bool ret = true;
00336     std::vector<std::string> files;
00337     std::vector<std::string> dirs;
00338 
00339     get_files_in_dir(path, &files, &dirs, ENTIRE_FILE_PATH);
00340 
00341     if(!files.empty()) {
00342         for(std::vector<std::string>::const_iterator i = files.begin(); i != files.end(); ++i) {
00343             errno = 0;
00344             if(remove((*i).c_str()) != 0) {
00345                 LOG_FS << "remove(" << (*i) << "): " << strerror(errno) << "\n";
00346                 ret = false;
00347             }
00348         }
00349     }
00350 
00351     if(!dirs.empty()) {
00352         for(std::vector<std::string>::const_iterator j = dirs.begin(); j != dirs.end(); ++j) {
00353             if(!delete_directory(*j))
00354                 ret = false;
00355         }
00356     }
00357 
00358     errno = 0;
00359     if(remove(path.c_str()) != 0) {
00360         LOG_FS << "remove(" << path << "): " << strerror(errno) << "\n";
00361         ret = false;
00362     }
00363     return ret;
00364 }
00365 
00366 std::string get_cwd()
00367 {
00368     char buf[1024];
00369     const char* const res = getcwd(buf,sizeof(buf));
00370     if(res != NULL) {
00371         std::string str(res);
00372 
00373 #ifdef _WIN32
00374         std::replace(str.begin(),str.end(),'\\','/');
00375 #endif
00376 
00377         return str;
00378     } else {
00379         return "";
00380     }
00381 }
00382 
00383 std::string get_user_data_dir()
00384 {
00385 #ifdef _WIN32
00386 
00387     static bool inited_dirs = false;
00388 
00389     if(!inited_dirs) {
00390         _mkdir("userdata");
00391         _mkdir("userdata/editor");
00392         _mkdir("userdata/editor/maps");
00393         _mkdir("userdata/data");
00394         _mkdir("userdata/data/ais");
00395         _mkdir("userdata/data/scenarios");
00396         _mkdir("userdata/data/scenarios/multiplayer");
00397         _mkdir("userdata/data/maps");
00398         _mkdir("userdata/data/maps/multiplayer");
00399         _mkdir("userdata/saves");
00400         inited_dirs = true;
00401     }
00402 
00403     char buf[256];
00404     const char* const res = getcwd(buf,sizeof(buf));
00405 
00406     if(res != NULL) {
00407         std::string cur_path(res);
00408         std::replace(cur_path.begin(),cur_path.end(),'\\','/');
00409         return cur_path + "/userdata";
00410     } else {
00411         return "userdata";
00412     }
00413 #elif defined(__BEOS__)
00414     if (be_path.InitCheck() != B_OK) {
00415         BPath tpath;
00416         if (find_directory(B_USER_SETTINGS_DIRECTORY, &be_path, true) == B_OK) {
00417             be_path.Append("wesnoth");
00418         } else {
00419             be_path.SetTo("/boot/home/config/settings/wesnoth");
00420         }
00421         tpath = be_path;
00422         tpath.Append("editor/maps");
00423         create_directory(tpath.Path(), 0775);
00424     }
00425     return be_path.Path();
00426 #else
00427 
00428 #ifndef __AMIGAOS4__
00429     static const char* const current_dir = ".";
00430     const char* home_str = getenv("HOME");
00431 #else
00432     static const char* const current_dir = " ";
00433     const char* home_str = "PROGDIR:";
00434 #endif
00435     if(home_str == NULL)
00436         home_str = current_dir;
00437 
00438     const std::string home(home_str);
00439 
00440 #ifndef PREFERENCES_DIR
00441 #define PREFERENCES_DIR ".wesnoth"
00442 #endif
00443 
00444 #ifndef __AMIGAOS4__
00445     const std::string dir_path = home + std::string("/") + PREFERENCES_DIR;
00446 #else
00447     const std::string dir_path = home + PREFERENCES_DIR;
00448 #endif
00449     DIR* dir = opendir(dir_path.c_str());
00450     if(dir == NULL) {
00451         const int res = mkdir(dir_path.c_str(),AccessMode);
00452 
00453         // Also create the maps directory
00454         mkdir((dir_path + "/editor").c_str(),AccessMode);
00455         mkdir((dir_path + "/editor/maps").c_str(),AccessMode);
00456         mkdir((dir_path + "/data").c_str(),AccessMode);
00457         mkdir((dir_path + "/data/ais").c_str(),AccessMode);
00458         mkdir((dir_path + "/data/scenarios").c_str(),AccessMode);
00459         mkdir((dir_path + "/data/scenarios/multiplayer").c_str(),AccessMode);
00460         mkdir((dir_path + "/data/maps").c_str(),AccessMode);
00461         mkdir((dir_path + "/data/maps/multiplayer").c_str(),AccessMode);
00462         mkdir((dir_path + "/saves").c_str(),AccessMode);
00463         if(res == 0) {
00464             dir = opendir(dir_path.c_str());
00465         } else {
00466             ERR_FS << "could not open or create directory: " << dir_path << '\n';
00467         }
00468     }
00469 
00470     if(dir == NULL)
00471         return "";
00472 
00473     closedir(dir);
00474 
00475     return dir_path;
00476 #endif
00477 }
00478 
00479 static std::string read_stream(std::istream& s)
00480 {
00481     std::stringstream ss;
00482     ss << s.rdbuf();
00483     return ss.str();
00484 }
00485 
00486 std::istream *istream_file(std::string const &fname)
00487 {
00488     LOG_FS << "streaming " << fname << " for reading.\n";
00489     if (!fname.empty() && fname[0] != '/' && !game_config::path.empty()) {
00490         std::ifstream *s = new std::ifstream((game_config::path + "/" + fname).c_str(),std::ios_base::binary);
00491         if (s->is_open())
00492             return s;
00493         LOG_FS << "could not open " << fname << " for reading.\n";
00494         delete s;
00495     }
00496 
00497     //! @todo FIXME: why do we rely on this even with relative paths ?
00498     // Still useful with zipios, for things like cache and prefs.
00499     // NOTE zipios has been removed - not sure what to do with this code.
00500     std::istream *s = new std::ifstream(fname.c_str(), std::ios_base::binary);
00501     if (s->fail()) {
00502         LOG_FS << "streaming " << fname << " failed.\n";
00503     }
00504     return s;
00505 }
00506 
00507 std::string read_file(std::string const &fname)
00508 {
00509     scoped_istream s = istream_file(fname);
00510     return read_stream(*s);
00511 }
00512 
00513 std::ostream *ostream_file(std::string const &fname)
00514 {
00515     LOG_FS << "streaming " << fname << " for writing.\n";
00516     return new std::ofstream(fname.c_str(), std::ios_base::binary);
00517 }
00518 
00519 // Throws io_exception if an error occurs
00520 void write_file(const std::string& fname, const std::string& data)
00521 {
00522     //const util::scoped_resource<FILE*,close_FILE> file(fopen(fname.c_str(),"wb"));
00523     const util::scoped_FILE file(fopen(fname.c_str(),"wb"));
00524     if(file.get() == NULL) {
00525         throw io_exception("Could not open file for writing: '" + fname + "'");
00526     }
00527 
00528     const size_t block_size = 4096;
00529     char buf[block_size];
00530 
00531     for(size_t i = 0; i < data.size(); i += block_size) {
00532         const size_t bytes = minimum<size_t>(block_size,data.size() - i);
00533         std::copy(data.begin() + i, data.begin() + i + bytes,buf);
00534         const size_t res = fwrite(buf,1,bytes,file.get());
00535         if(res != bytes) {
00536             throw io_exception("Error writing to file: '" + fname + "'");
00537         }
00538     }
00539 }
00540 
00541 
00542 std::string read_map(const std::string& name)
00543 {
00544     std::string res = read_file("data/maps/" + name);
00545     if(res == "") {
00546         res = read_file(get_user_data_dir() + "/data/maps/" + name);
00547     }
00548 
00549     if(res == "") {
00550         res = read_file(get_user_data_dir() + "/editor/maps/" + name);
00551     }
00552 
00553     return res;
00554 }
00555 
00556 static bool is_directory_internal(const std::string& fname)
00557 {
00558 #ifdef _WIN32
00559     _finddata_t info;
00560     const long handle = _findfirst((fname + "/*").c_str(),&info);
00561     if(handle >= 0) {
00562         _findclose(handle);
00563         return true;
00564     } else {
00565         return false;
00566     }
00567 
00568 #else
00569     struct stat dir_stat;
00570     if(::stat(fname.c_str(), &dir_stat) == -1) {
00571         return false;
00572     }
00573 
00574     return S_ISDIR(dir_stat.st_mode);
00575 #endif
00576 }
00577 
00578 bool is_directory(const std::string& fname)
00579 {
00580     if(fname.empty()) {
00581         return false;
00582     }
00583     if(fname[0] != '/' && !game_config::path.empty()) {
00584         if(is_directory_internal(game_config::path + "/" + fname))
00585             return true;
00586     }
00587 
00588     return is_directory_internal(fname);
00589 }
00590 
00591 bool file_exists(const std::string& name)
00592 {
00593     std::ifstream file(name.c_str(),std::ios_base::binary);
00594     if (file.rdstate() != 0) {
00595         return false;
00596     }
00597     file.close();
00598     return true;
00599 }
00600 
00601 time_t file_create_time(const std::string& fname)
00602 {
00603     struct stat buf;
00604     if(::stat(fname.c_str(),&buf) == -1)
00605         return 0;
00606 
00607     return buf.st_mtime;
00608 }
00609 
00610 //! Return the next ordered full filename within this directory.
00611 std::string next_filename(const std::string &dirname, unsigned int max)
00612 {
00613     std::vector<std::string> files;
00614     std::stringstream fname;
00615     unsigned int num = 1;
00616 
00617     // These are sorted, so we can simply add one to last one.
00618     get_files_in_dir(dirname, &files);
00619 
00620     // Make sure we skip over any files we didn't create ourselves.
00621     std::vector<std::string>::reverse_iterator i;
00622     for (i = files.rbegin(); i != files.rend(); ++i) {
00623         if (i->length() == 8) {
00624             try {
00625                 num = lexical_cast<int>(*i)+1;
00626                 break;
00627             } catch (bad_lexical_cast &) {
00628             }
00629         }
00630     }
00631 
00632     // Erase oldest files if we have too many
00633     if (max) {
00634         for (unsigned int j = 0; j + max < files.size(); j++) {
00635             delete_directory(dirname + "/" + files[j]);
00636         }
00637     }
00638 
00639     fname << std::setw(8) << std::setfill('0') << num;
00640     return dirname + "/" + fname.str();
00641 }
00642 
00643 //! Returns true if the file ends with '.gz'.
00644 //! 
00645 //! @param filename     The name to test.
00646 bool is_gzip_file(const std::string& filename)
00647 { 
00648     return (filename.length() > 3 
00649         && filename.substr(filename.length() - 3) == ".gz"); 
00650 }
00651 
00652 file_tree_checksum::file_tree_checksum()
00653     : nfiles(0), sum_size(0), modified(0)
00654 {}
00655 
00656 file_tree_checksum::file_tree_checksum(const config& cfg) :
00657     nfiles  (lexical_cast_default<size_t>(cfg["nfiles"])),
00658     sum_size(lexical_cast_default<size_t>(cfg["size"])),
00659     modified(lexical_cast_default<time_t>(cfg["modified"]))
00660 {
00661 }
00662 
00663 void file_tree_checksum::write(config& cfg) const
00664 {
00665     cfg["nfiles"] = lexical_cast<std::string>(nfiles);
00666     cfg["size"] = lexical_cast<std::string>(sum_size);
00667     cfg["modified"] = lexical_cast<std::string>(modified);
00668 }
00669 
00670 bool operator==(const file_tree_checksum& lhs, const file_tree_checksum& rhs)
00671 {
00672     return lhs.nfiles == rhs.nfiles && lhs.sum_size == rhs.sum_size &&
00673            lhs.modified == rhs.modified;
00674 }
00675 
00676 bool operator!=(const file_tree_checksum& lhs, const file_tree_checksum& rhs)
00677 {
00678     return !operator==(lhs,rhs);
00679 }
00680 
00681 static void get_file_tree_checksum_internal(const std::string& path, file_tree_checksum& res)
00682 {
00683     std::vector<std::string> files, dirs;
00684     get_files_in_dir(path,&files,&dirs, ENTIRE_FILE_PATH, SKIP_MEDIA_DIR);
00685     increment_filesystem_progress();
00686     for(std::vector<std::string>::const_iterator i = files.begin(); i != files.end(); ++i) {
00687         ++res.nfiles;
00688 
00689         struct stat buf;
00690         if(::stat(i->c_str(),&buf) != -1) {
00691             if(buf.st_mtime > res.modified) {
00692                 res.modified = buf.st_mtime;
00693             }
00694 
00695             res.sum_size += buf.st_size;
00696         }
00697     }
00698 
00699     for(std::vector<std::string>::const_iterator j = dirs.begin(); j != dirs.end(); ++j) {
00700         get_file_tree_checksum_internal(*j,res);
00701     }
00702 }
00703 
00704 const file_tree_checksum& data_tree_checksum(bool reset)
00705 {
00706     static file_tree_checksum checksum;
00707     if (reset)
00708         checksum.reset();
00709     if(checksum.nfiles == 0) {
00710         get_file_tree_checksum_internal("data/",checksum);
00711         get_file_tree_checksum_internal(get_user_data_dir() + "/data/",checksum);
00712         LOG_FS << "calculated data tree checksum: "
00713                << checksum.nfiles << " files; "
00714                << checksum.sum_size << " bytes\n";
00715     }
00716 
00717     return checksum;
00718 }
00719 
00720 int file_size(const std::string& fname)
00721 {
00722     struct stat buf;
00723     if(::stat(fname.c_str(),&buf) == -1)
00724         return -1;
00725 
00726     return buf.st_size;
00727 }
00728 
00729 std::string file_name(const std::string& file)
00730 // Analogous to POSIX basename(3), but for C++ string-object pathnames
00731 {
00732 #ifdef _WIN32
00733     static const std::string dir_separators = "\\/:";
00734 #else
00735     static const std::string dir_separators = "/";
00736 #endif
00737 
00738     std::string::size_type pos = file.find_last_of(dir_separators);
00739 
00740     if(pos == std::string::npos)
00741         return file;
00742     if(pos >= file.size()-1)
00743         return "";
00744 
00745     return file.substr(pos+1);
00746 }
00747 
00748 std::string directory_name(const std::string& file)
00749 // Analogous to POSIX dirname(3), but for C++ string-object pathnames
00750 {
00751 #ifdef _WIN32
00752     static const std::string dir_separators = "\\/:";
00753 #else
00754     static const std::string dir_separators = "/";
00755 #endif
00756 
00757     std::string::size_type pos = file.find_last_of(dir_separators);
00758 
00759     if(pos == std::string::npos)
00760         return "";
00761 
00762     return file.substr(0,pos+1);
00763 }
00764 
00765 namespace {
00766 
00767 std::set<std::string> binary_paths;
00768 
00769 typedef std::map<std::string,std::vector<std::string> > paths_map;
00770 paths_map binary_paths_cache;
00771 
00772 }
00773 
00774 static void init_binary_paths()
00775 {
00776     if(binary_paths.empty()) {
00777         binary_paths.insert("");
00778     }
00779 }
00780 
00781 binary_paths_manager::binary_paths_manager() : paths_()
00782 {}
00783 
00784 binary_paths_manager::binary_paths_manager(const config& cfg) : paths_()
00785 {
00786     set_paths(cfg);
00787 }
00788 
00789 binary_paths_manager::~binary_paths_manager()
00790 {
00791     cleanup();
00792 }
00793 
00794 void binary_paths_manager::set_paths(const config& cfg)
00795 {
00796     cleanup();
00797     init_binary_paths();
00798 
00799     const config::child_list& items = cfg.get_children("binary_path");
00800     for(config::child_list::const_iterator i = items.begin(); i != items.end(); ++i) {
00801         std::string path = (**i)["path"].str();
00802         if (!path.empty() && path[path.size()-1] != '/') path += "/";
00803         if(binary_paths.count(path) == 0) {
00804             binary_paths.insert(path);
00805             paths_.push_back(path);
00806         }
00807     }
00808 }
00809 
00810 void binary_paths_manager::cleanup()
00811 {
00812     binary_paths_cache.clear();
00813 
00814     for(std::vector<std::string>::const_iterator i = paths_.begin(); i != paths_.end(); ++i) {
00815         binary_paths.erase(*i);
00816     }
00817 }
00818 
00819 void clear_binary_paths_cache()
00820 {
00821     binary_paths_cache.clear();
00822 }
00823 
00824 const std::vector<std::string>& get_binary_paths(const std::string& type)
00825 {
00826     const paths_map::const_iterator itor = binary_paths_cache.find(type);
00827     if(itor != binary_paths_cache.end()) {
00828         return itor->second;
00829     }
00830 
00831     std::vector<std::string>& res = binary_paths_cache[type];
00832 
00833     init_binary_paths();
00834 
00835     for(std::set<std::string>::const_iterator i = binary_paths.begin(); i != binary_paths.end(); ++i) {
00836         res.push_back(get_user_data_dir() + "/" + *i + type + "/");
00837 
00838         if(!game_config::path.empty()) {
00839             res.push_back(game_config::path + "/" + *i + type + "/");
00840         }
00841 
00842         res.push_back(*i + type + "/");
00843     }
00844 
00845     return res;
00846 }
00847 
00848 std::string get_binary_file_location(const std::string& type, const std::string& filename)
00849 {
00850     const std::vector<std::string>& paths = get_binary_paths(type);
00851     if(!filename.empty()) {
00852         DBG_FS << "Looking for " << filename << " in  '.'\n";
00853         if(file_exists(filename) || is_directory(filename)) {
00854           DBG_FS << "  Found at " << filename << "\n";
00855             return filename;
00856         }
00857     }
00858 
00859     for(std::vector<std::string>::const_iterator i = paths.begin(); i != paths.end(); ++i) {
00860         const std::string file = *i + filename;
00861         DBG_FS << "  Checking " << *i << "\n";
00862         if(file_exists(file) || is_directory(file)) {
00863           DBG_FS << "  Found at " << file << "\n";
00864             return file;
00865         }
00866     }
00867 
00868     DBG_FS << "  " << filename << " not found.\n";
00869     return "";
00870 }
00871 
00872 scoped_istream& scoped_istream::operator=(std::istream *s)
00873 {
00874     delete stream;
00875     stream = s;
00876     return *this;
00877 }
00878 
00879 scoped_istream::~scoped_istream()
00880 {
00881     delete stream;
00882 }
00883 
00884 scoped_ostream& scoped_ostream::operator=(std::ostream *s)
00885 {
00886     delete stream;
00887     stream = s;
00888     return *this;
00889 }
00890 
00891 scoped_ostream::~scoped_ostream()
00892 {
00893     delete stream;
00894 }

Generated by doxygen 1.5.5 on 23 May 2008 for The Battle for Wesnoth
Gna! | Forum | Wiki | CIA | devdocs