utils.cc

Go to the documentation of this file.
00001 /*
00002  *  utils.cc - Common utility routines.
00003  *
00004  *  Copyright (C) 1998-1999  Jeffrey S. Freedman
00005  *  Copyright (C) 2000-2004  The Exult Team
00006  *
00007  *  This program is free software; you can redistribute it and/or modify
00008  *  it under the terms of the GNU General Public License as published by
00009  *  the Free Software Foundation; either version 2 of the License, or
00010  *  (at your option) any later version.
00011  *
00012  *  This program is distributed in the hope that it will be useful,
00013  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  *  GNU General Public License for more details.
00016  *
00017  *  You should have received a copy of the GNU General Public License
00018  *  along with this program; if not, write to the Free Software
00019  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00020  */
00021 
00022 #ifdef HAVE_CONFIG_H
00023 #  include <config.h>
00024 #endif
00025 
00026 #ifndef ALPHA_LINUX_CXX
00027 #  include <cctype>
00028 #  include <cstdio>
00029 #  include <cstdlib>
00030 #  include <cstring>
00031 #  include <fstream>
00032 #endif
00033 #include <map>
00034 #include <list>
00035 #ifdef MACOS
00036   #include <stat.h>
00037 #elif !defined(UNDER_CE)
00038   #include <sys/stat.h>
00039 #endif
00040 #include <unistd.h>
00041 
00042 #ifdef WIN32
00043 #include <windows.h>
00044 #endif
00045 
00046 #include <exception>
00047 #include "exceptions.h"
00048 #include "utils.h"
00049 
00050 
00051 using std::cerr;
00052 using std::string;
00053 using std::ios;
00054 
00055 // Function prototypes
00056 
00057 static void switch_slashes(string & name);
00058 static bool base_to_uppercase(string& str, int count);
00059 
00060 
00061 // Wrap a few functions
00062 inline int stat(const std::string &file_name, struct stat *buf)
00063 {
00064   return stat(file_name.c_str(),buf);
00065 }
00066 
00067 // Ugly hack for supporting different paths
00068 
00069 static std::map<string, string> path_map;
00070 static std::map<string, string> stored_path_map;
00071 
00072 void store_system_paths()
00073 {
00074   stored_path_map = path_map;
00075 }
00076 
00077 void reset_system_paths()
00078 {
00079   path_map = stored_path_map;
00080 }
00081 
00082 void add_system_path(const string& key, const string& value)
00083 {
00084   if (!value.empty()) {
00085     path_map[key] = value;
00086   } else {
00087     clear_system_path(key);
00088   }
00089 }
00090 
00091 void clone_system_path(const string& new_key, const string& old_key)
00092 {
00093   if (is_system_path_defined(old_key)) {
00094     path_map[new_key] = path_map[old_key];
00095   } else {
00096     clear_system_path(new_key);
00097   }
00098 }
00099 
00100 void clear_system_path(const string& key)
00101 {
00102   std::map<string, string>::iterator iter = path_map.find(key);
00103   if (iter != path_map.end())
00104     path_map.erase(iter);
00105 }
00106 
00107 /*
00108  *  Has a path been entered?
00109  */
00110 bool is_system_path_defined(const string& path)
00111 {
00112   return (path_map.find(path) != path_map.end());
00113 }
00114 
00115 /*
00116  *  Convert an exult path (e.g. "<DATA>/exult.flx") into a system path
00117  */
00118 
00119 string get_system_path(const string &path)
00120 {
00121   string new_path;
00122   string::size_type pos;
00123   string::size_type pos2;
00124   
00125 #if defined(__MORPHOS__) || defined(AMIGA)
00126   pos = path.find( "../" );
00127   if( pos != string::npos )
00128     new_path = path.substr( 0, pos )+path.substr( pos+2 );
00129   else
00130     new_path = path;
00131 
00132   pos = new_path.find( "./" );
00133   if( pos != string::npos )
00134     new_path = new_path.substr( 0, pos )+new_path.substr( pos+2 );
00135 
00136   pos = new_path.find('>');
00137   pos2 = new_path.find('<');
00138   // If there is no separator, return the path as is
00139   if(pos != string::npos && pos2 == 0)
00140   {
00141     pos += 1;
00142     // See if we can translate this prefix
00143     string syspath = new_path.substr(0, pos);
00144     if (is_system_path_defined(syspath)) {
00145       string new_prefix = path_map[syspath];
00146       new_path = new_prefix + new_path.substr(pos);
00147     }
00148   }
00149 #else
00150   pos = path.find('>');
00151   pos2 = path.find('<');
00152   // If there is no separator, return the path as is
00153   if(pos == string::npos || pos2 != 0) {
00154     new_path = path;
00155   }
00156   else
00157   {
00158     pos += 1;
00159     // See if we can translate this prefix
00160     string syspath = path.substr(0, pos);
00161     if (is_system_path_defined(syspath)) {
00162       string new_prefix = path_map[syspath];
00163       new_path = new_prefix + path.substr(pos);
00164     } else {
00165       new_path = path;
00166     }
00167   }
00168 #endif
00169   switch_slashes(new_path);
00170 #ifdef WIN32
00171   if (*(new_path.end()-1) == '/' || *(new_path.end()-1) == '\\') {
00172     std::cerr << "Trailing slash in path: \"" << new_path << "\"" << std::endl << "...compensating, but go complain to Colourless anyway" << std::endl;
00173     new_path += '.';
00174   }
00175 #ifdef NO_WIN32_PATH_SPACES
00176   pos = new_path.find('*');
00177   pos2 = new_path.find('?');
00178   string::size_type pos3 = new_path.find(' ');
00179   // pos and pos2 will equal each other if neither is found
00180   // and we'll only convert to short if a space is found
00181   // really, we don't need to do this, but hey, you never know
00182   if (pos == pos2 && pos3 != string::npos) {
00183     int num_chars = GetShortPathName(new_path.c_str(), NULL, 0);
00184     if (num_chars > 0) {
00185       char *short_path = new char [num_chars+1];
00186       GetShortPathName(new_path.c_str(), short_path, num_chars+1);
00187       new_path = short_path;
00188       delete [] short_path;
00189     }
00190     //else std::cerr << "Warning unable to get short path for: " << new_path << std::endl;
00191   }
00192 #endif
00193 #endif
00194   return new_path;
00195 }
00196 
00197 
00198 /*
00199  *  Convert a buffer to upper-case.
00200  *
00201  *  Output: ->original buffer, changed to upper case.
00202  */
00203 
00204 void to_uppercase(string &str)
00205 {
00206   for(string::iterator X = str.begin(); X != str.end(); ++X)
00207   {
00208 #if (defined(BEOS) || defined(OPENBSD) || defined(CYGWIN) || defined(__MORPHOS__))
00209     if ((*X >= 'a') && (*X <= 'z')) *X -= 32;
00210 #else
00211     *X = std::toupper(*X);
00212 #endif         
00213   }
00214 }
00215 
00216 string to_uppercase(const std::string &str)
00217 {
00218   string s(str);
00219   to_uppercase(s);
00220   return s;
00221 }
00222 
00223 /*
00224  *  Convert just the last 'count' parts of a filename to uppercase.
00225  *  returns false if there are less than 'count' parts
00226  */
00227 
00228 static bool base_to_uppercase(string& str, int count)
00229 {
00230   if (count <= 0) return true;
00231 
00232   int todo = count;
00233           // Go backwards.
00234   string::reverse_iterator X;
00235   for(X = str.rbegin(); X != str.rend(); ++X)
00236     {
00237           // Stop at separator.
00238     if (*X == '/' || *X == '\\' || *X == ':')
00239       todo--;
00240     if (todo <= 0)
00241       break;
00242 
00243 #if (defined(BEOS) || defined(OPENBSD) || defined(CYGWIN) || defined(__MORPHOS__))
00244     if ((*X >= 'a') && (*X <= 'z')) *X -= 32;
00245 #else
00246     *X = std::toupper(*X);
00247 #endif         
00248   }
00249   if (X == str.rend())
00250     todo--; // start of pathname counts as separator too
00251 
00252   // false if it didn't reach 'count' parts
00253   return (todo <= 0);
00254 }
00255 
00256 
00257 
00258 static void switch_slashes(
00259   string & name
00260   )
00261 {
00262 #ifdef WIN32
00263   for(string::iterator X = name.begin(); X != name.end(); ++X)
00264   {
00265     if(*X == '/' )
00266       *X =  '\\';
00267   }
00268 #elif defined(MACOS)
00269   // We use a component-wise algorithm (suggested by Yorick)
00270   // Basically, split the path along the "/" seperators
00271   // If a component is empty or '.', remove it. If it's '..', replace it
00272   // with the empty string. convert all / to :
00273   string::size_type begIdx, endIdx;;
00274   string  component;
00275   string  new_name;
00276   
00277   if( name.at(0) != '/' )
00278     new_name = ":";
00279   
00280   begIdx = name.find_first_not_of('/');
00281   while( begIdx != string::npos )
00282   {
00283     endIdx = name.find_first_of('/', begIdx);
00284     if( endIdx == std::string::npos )
00285       component = name.substr(begIdx);
00286     else
00287       component = name.substr(begIdx, endIdx-begIdx);
00288     if( component == ".." )
00289       new_name += ":";
00290     else if( !component.empty() && component != "." )
00291     {
00292       new_name += component;
00293       if( endIdx != std::string::npos )
00294         new_name += ":";
00295     }
00296     begIdx = name.find_first_not_of('/', endIdx);
00297   }
00298 
00299   name = new_name;
00300 #else
00301   // do nothing
00302 #endif
00303 }
00304 
00305 /*
00306  *  Open a file for input, 
00307  *  trying the original name (lower case), and the upper case version 
00308  *  of the name.  
00309  *
00310  *  Output: 0 if couldn't open.
00311  */
00312 
00313 bool U7open
00314   (
00315   std::ifstream& in,      // Input stream to open.
00316   const char *fname,      // May be converted to upper-case.
00317   bool is_text        // Should the file be opened in text mode
00318   )
00319 {
00320 #if defined(MACOS) || (__GNUG__ > 2)
00321   std::ios_base::openmode mode = std::ios::in;
00322   if (!is_text) mode |= std::ios::binary;
00323 #elif defined(XWIN)
00324   int mode = std::ios::in;
00325 #else
00326   int mode = std::ios::in;
00327   if (!is_text) mode |= std::ios::binary;
00328 #endif
00329   string name = get_system_path(fname);
00330   
00331   int uppercasecount = 0;
00332   do {
00333     // We first "clear" the stream object. This is done to prevent
00334     // problems when re-using stream objects
00335     in.clear();
00336     try {
00337       //std::cout << "trying: " << name << std::endl;
00338       in.open(name.c_str(), mode);    // Try to open
00339     } catch (std::exception &)
00340     {}
00341     if (in.good() && !in.fail()) {
00342       //std::cout << "got it!" << std::endl;
00343       return true; // found it!
00344     }
00345   } while (base_to_uppercase(name, ++uppercasecount));
00346 
00347   // file not found.
00348   throw (file_open_exception(get_system_path(fname)));
00349   return false;
00350 }
00351 
00352 #ifdef ALPHA_LINUX_CXX
00353 /*
00354  * Wraps around a bug in Compaq's cxx, which doesn't generate an external
00355  * symbol for this one implicitly because of is_text = false
00356  *
00357  * See function above for a functional description
00358  */
00359 bool U7open(std::ifstream& in,
00360       const char *fname)
00361 {
00362   return U7open(in, fname, false);
00363 }
00364 #endif
00365 
00366 /*
00367  *  Open a file for output,
00368  *  trying the original name (lower case), and the upper case version 
00369  *  of the name.  
00370  *
00371  *  Output: 0 if couldn't open.
00372  */
00373 
00374 bool U7open
00375   (
00376   std::ofstream& out,     // Output stream to open.
00377   const char *fname,      // May be converted to upper-case.
00378   bool is_text        // Should the file be opened in text mode
00379   )
00380 {
00381 #if defined(MACOS) || (__GNUG__ > 2)
00382   std::ios_base::openmode mode = std::ios::out | std::ios::trunc;
00383   if (!is_text) mode |= std::ios::binary;
00384 #elif defined(XWIN)
00385   int mode = std::ios::out | std::ios::trunc;
00386 #else
00387   int mode = std::ios::out | std::ios::trunc;
00388   if (!is_text) mode |= std::ios::binary;
00389 #endif
00390   string name = get_system_path(fname);
00391   
00392   // We first "clear" the stream object. This is done to prevent
00393   // problems when re-using stream objects
00394   out.clear();
00395 
00396   int uppercasecount = 0;
00397   do {
00398     out.open(name.c_str(), mode);   // Try to open
00399     if (out.good())
00400       return true; // found it!
00401     out.clear();  // Forget ye not
00402   } while (base_to_uppercase(name, ++uppercasecount));
00403 
00404   // file not found.
00405   throw (file_open_exception(get_system_path(fname)));
00406   return false;
00407 }
00408 
00409 #ifdef ALPHA_LINUX_CXX
00410 /*
00411  * Wraps around a bug in Compaq's cxx, which doesn't generate an external
00412  * symbol for this one implicitly because of is_text = false
00413  *
00414  * See function above for a functional description
00415  */
00416 bool U7open(std::ofstream& out,
00417       const char *fname)
00418 {
00419   return U7open(out, fname, false);
00420 }
00421 #endif
00422 
00423 /*
00424  *  Open a file with the access rights specified in mode,
00425  *  works just like fopen but in a system independant fashion.  
00426  *
00427  *  Output: A pointer to a FILE
00428  */
00429 
00430 std::FILE* U7open
00431   (
00432   const char *fname,       // May be converted to upper-case.
00433   const char *mode       // File access mode.
00434   )
00435 {
00436   std::FILE* f;
00437   string name = get_system_path(fname);
00438   
00439   int uppercasecount = 0;
00440   do {
00441     f = std::fopen(name.c_str(), mode); // Try to open
00442     if (f)
00443       return f; // found it!
00444   } while (base_to_uppercase(name, ++uppercasecount));
00445 
00446   // file not found.
00447   throw (file_open_exception(get_system_path(fname)));
00448   return 0;
00449 }
00450 
00451 /*
00452  *  Remove a file taking care of paths etc.
00453  *
00454  */
00455 
00456 void U7remove
00457   (
00458   const char *fname     // May be converted to upper-case.
00459   )
00460 {
00461   string name = get_system_path(fname);
00462 
00463 #if defined(WIN32) && defined(UNICODE)
00464   const char *n = name.c_str();
00465   int nLen = std::strlen(n)+1;
00466   LPTSTR lpszT = (LPTSTR) alloca(nLen*2);
00467   MultiByteToWideChar(CP_ACP, 0, n, -1, lpszT, nLen);
00468   DeleteFile(lpszT);
00469 #else
00470 
00471   bool exists;
00472   struct stat sbuf;
00473 
00474   int uppercasecount = 0;
00475   do {
00476     exists = (stat(name, &sbuf) == 0);
00477     if (exists) {
00478       std::remove(name.c_str());
00479     }
00480   } while (base_to_uppercase(name, ++uppercasecount));
00481   std::remove(name.c_str());
00482 #endif
00483 }
00484 
00485 /*
00486  *  See if a file exists.
00487  */
00488 
00489 bool U7exists
00490   (
00491   const char *fname     // May be converted to upper-case.
00492   )
00493 {
00494   string name = get_system_path(fname);
00495 
00496 #ifdef UNDER_CE // This is a bit of a hack for WinCE
00497   const char *n = name.c_str();
00498   int nLen = std::strlen(n)+1;
00499   LPTSTR lpszT = (LPTSTR) alloca(nLen*2);
00500   MultiByteToWideChar(CP_ACP, 0, n, -1, lpszT, nLen);
00501   return GetFileAttributes(lpszT) != 0xFFFFFFFF;
00502 #else
00503 
00504   bool  exists;
00505   struct stat sbuf;
00506 
00507   int uppercasecount = 0;
00508   do {
00509     exists = (stat(name, &sbuf) == 0);
00510     if (exists)
00511       return true; // found it!
00512   } while (base_to_uppercase(name, ++uppercasecount));
00513 
00514   // file not found
00515   return false;
00516 #endif
00517 }
00518 
00519 /*
00520  *  Create a directory
00521  */
00522 
00523 int U7mkdir
00524   (
00525   const char *dirname,  // May be converted to upper-case.
00526   int mode
00527   )
00528 {
00529   string name = get_system_path(dirname);
00530 #if (defined(MACOSX) || defined(BEOS))
00531   // remove any trailing slashes
00532   string::size_type pos = name.find_last_not_of('/');
00533   if (pos != string::npos)
00534     name.resize(pos+1);
00535 #endif
00536 #if defined(WIN32) && defined(UNICODE)
00537   const char *n = name.c_str();
00538   int nLen = std::strlen(n)+1;
00539   LPTSTR lpszT = (LPTSTR) alloca(nLen*2);
00540   MultiByteToWideChar(CP_ACP, 0, n, -1, lpszT, nLen);
00541   return CreateDirectory(lpszT, NULL);
00542 #elif defined(WIN32)
00543   return mkdir(name.c_str());
00544 #else
00545   return mkdir(name.c_str(), mode); // Create dir. if not already there.
00546 #endif
00547 }
00548 
00549 // These are not supported in WinCE (PocketPC) for now
00550 #ifndef UNDER_CE
00551 
00552 /*
00553  *  Change the current directory
00554  *
00555  *  TODO: Make this work in WinCE - Colourless
00556  */
00557 
00558 int U7chdir
00559   (
00560   const char *dirname // May be converted to upper-case.
00561   )
00562 {
00563 #ifdef MACOS
00564   string name(dirname);
00565   switch_slashes(name);
00566   return chdir(name.c_str());
00567 #else
00568   return chdir(dirname);
00569 #endif
00570 }
00571 
00572 /*
00573  *  Copy a file.  May throw an exception.
00574  *
00575  *  TODO: Make this work in WinCE - Colourless
00576  */
00577 void U7copy
00578   (
00579   const char *src,
00580   const char *dest
00581   )
00582   {
00583   std::ifstream in;
00584   std::ofstream out;
00585   try {
00586     U7open(in, src);
00587     U7open(out, dest);
00588   } catch (exult_exception& e) {
00589     in.close();
00590     out.close();
00591     throw (e);
00592   }
00593   const int bufsize = 0x8000;
00594   unsigned char *buf = new unsigned char[0x8000];
00595   in.seekg(0, ios::end);    // Get filesize.
00596   int filesize = in.tellg();
00597   in.seekg(0, ios::beg);
00598   while (filesize > 0)    // Copy.
00599     {
00600     int toread = bufsize < filesize ? bufsize : filesize;
00601     in.read(reinterpret_cast<char *>(buf), toread);
00602     out.write(reinterpret_cast<char *>(buf), toread);
00603     filesize -= toread;
00604     }
00605   out.flush();
00606   delete [] buf;
00607   bool inok = in.good(), outok = out.good();
00608   in.close();
00609   out.close();
00610   if (!inok)
00611     throw (file_read_exception((const char *)src));
00612   if (!outok)
00613     throw (file_write_exception((const char *)dest));
00614 
00615   return;
00616   }
00617 #endif //UNDER_CE
00618 
00619 /*
00620  *  Take log2 of a number.
00621  *
00622  *  Output: Log2 of n (0 if n==0).
00623  */
00624 
00625 int Log2
00626   (
00627   uint32 n
00628   )
00629 {
00630   int result = 0;
00631   for (n = n>>1; n; n = n>>1)
00632     result++;
00633   return result;
00634 }
00635 
00636 /*
00637  *  Replacement for non-standard strdup function.
00638  */
00639 
00640 char *newstrdup(const char *s)
00641 {
00642   if(!s)
00643     throw (std::invalid_argument("NULL pointer passed to newstrdup"));
00644   char *ret=new char[std::strlen(s)+1];
00645   std::strcpy(ret,s);
00646   return ret;
00647 }

Generated on Mon Jul 9 14:42:45 2007 for ExultEngine by  doxygen 1.5.1