file.h

Go to the documentation of this file.
00001 /*
00002  * file.h
00003  *
00004  * Copyright (C) 2003-2010  Thomas A. Vaughan
00005  * All rights reserved.
00006  *
00007  *
00008  * Redistribution and use in source and binary forms, with or without
00009  * modification, are permitted provided that the following conditions are met:
00010  *     * Redistributions of source code must retain the above copyright
00011  *       notice, this list of conditions and the following disclaimer.
00012  *     * Redistributions in binary form must reproduce the above copyright
00013  *       notice, this list of conditions and the following disclaimer in the
00014  *       documentation and/or other materials provided with the distribution.
00015  *     * Neither the name of the <organization> nor the
00016  *       names of its contributors may be used to endorse or promote products
00017  *       derived from this software without specific prior written permission.
00018  *
00019  * THIS SOFTWARE IS PROVIDED BY THOMAS A. VAUGHAN ''AS IS'' AND ANY
00020  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00021  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
00022  * DISCLAIMED. IN NO EVENT SHALL THOMAS A. VAUGHAN BE LIABLE FOR ANY
00023  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
00024  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00025  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
00026  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00027  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00028  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00029  *
00030  *
00031  * Helpful file/filename utilities
00032  */
00033 
00034 #ifndef WAVEPACKET_UTIL_FILE_H__
00035 #define WAVEPACKET_UTIL_FILE_H__
00036 
00037 
00038 // includes --------------------------------------------------------------------
00039 #include "common/common.h"
00040 
00041 #ifdef WIN32
00042 
00043 // win32 filesystem headers
00044 #include <io.h>
00045 
00046 #else   // WIN32
00047 
00048 // posix filesystem headers
00049 #include <dirent.h>
00050 #include <unistd.h>
00051 #include <sys/errno.h>
00052 #include <sys/file.h>
00053 
00054 #endif  // WIN32
00055 
00056 
00057 // doxygen block
00058 
00059 /// \ingroup general
00060 /*@{*/
00061 
00062 ////////////////////////////////////////////////////////////////////////////////
00063 ///
00064 /// \defgroup util Utility Library
00065 ///
00066 /// Very low-level utility APIs and objects.  More or less a grab bag of
00067 /// parsing, file, and date-based helper methods. 
00068 ///     
00069 ////////////////////////////////////////////////////////////////////////////////
00070 /*@{*/
00071 
00072 
00073 /*
00074  * Filename APIs
00075  */
00076 
00077 
00078 // GetExtension() -- gets the extension for the specified filename.  Returns
00079 // NULL if there is no extension (no period in the name).  Can return an
00080 // empty string if the filename ends in a period.
00081 const char * GetExtension(IN const char * filename) throw();
00082 
00083 
00084 
00085 /// returns true if the given filename ends with the specified string (NOT
00086 ///     case-sensitive)
00087 bool hasExtension(IN const char * filename, IN const char * extension) throw();
00088 
00089 
00090 // GetFileRoot() -- gets the root name from the given filename (strips out
00091 // any path information, and the filename extension)
00092 void GetFileRoot(IN const char * filename,
00093                 OUT std::string& file_root);
00094 
00095 // GetParentDirectory() -- given a full path, return the parent directory
00096 // string will be empty if this is the root directory.
00097 void GetParentDirectory(IN const char * filename,
00098         OUT std::string& directory) throw();
00099 
00100 /// retrieves the highest known folder for the given file.
00101 /// returns a pointer to the beginning of the filename now relative to
00102 ///     the top directory
00103 const char * getTopDirectory(IN const char * filename,
00104                 OUT std::string& topDir);
00105 
00106 
00107 /// given a starting filename, and then a path relative to that, return
00108 ///     the full path of the file.  Examples:
00109 ///  - getPathRelativeTo("foo/bar.txt", "hi.txt") --> "foo/hi.txt"
00110 ///  - getPathRelativeTo("foo/bar.txt", "x/hi.txt") --> "foo/x/hi.txt"
00111 ///  - getPathRelativeTo("foo/bar.txt", "../hi.txt") --> "hi.txt"
00112 ///  - getPathRelativeTo("foo/models/x.model", "../textures/y.texture")
00113 ///     --> "foo/textures/y.texture"
00114 std::string getPathRelativeTo(IN const char * startFilename,
00115                                 IN const char * relFilename);
00116 
00117 
00118 /// given a starting path (directory), append another piece of the path
00119 void appendPath(IN const char * parentDirectory,
00120                                 IN const char * nextPath,
00121                                 OUT std::string& path);
00122 
00123 
00124 // GetFilename() -- given a full path, return just the tail filename
00125 const char * GetFilename(IN const char * path) throw();
00126 
00127 // getTempfile() - given a full path, return a temp file in the same directory
00128 void getTempfile(IN const char * path,
00129                 OUT std::string& tempfile) throw();
00130 
00131 // getUserHomePath() - determines the path to the user's home directory
00132 void getUserHomePath(OUT std::string& path);
00133 
00134 // ContainsWhitespace() - does this string contain whitespace?
00135 bool ContainsWhitespace(IN const char * test) throw();
00136 
00137 // doesPathExist() - does this path exist?
00138 bool doesPathExist(IN const char * path) throw();
00139 
00140 // createEmptyFileIfDoesNotExist() -- yup, creates empty file if none already there
00141 void createEmptyFileIfDoesNotExist(IN const char * path) throw();
00142 
00143 
00144 /// does a recursive directory walk, and returns all paths relative to root
00145 void walkDirectoryTree(IN const char * rootDirectory,
00146                                 IN const char * matchExtension, ///< can be null
00147                                 OUT VecString& paths);
00148 
00149 #ifdef WIN32
00150 // my own win32 wrappers for opendir(), readdir(), closedir()
00151 struct DIR {
00152         DIR(void) throw() { this->clear(); }
00153         void clear(void) throw() {
00154                         path = "";
00155                         h = NULL;
00156                 }
00157 
00158         std::string     path;
00159         HANDLE          h;
00160 };
00161 
00162 DIR * opendir(IN const char * path);
00163 struct dirent {
00164         char d_name[MAX_PATH];
00165 };
00166 struct dirent * readdir(IO DIR * dir);
00167 int readdir_r(IO DIR * dir, IN struct dirent * entry,
00168         OUT struct dirent ** result);
00169 int closedir(IN DIR * dir);
00170 char * strerror_r(IN int error, IN char * buf, IN int bufsize);
00171 #endif // WIN32
00172 
00173 
00174 
00175 /// throws a (hopefully) descriptive error based on errno passed in
00176 #define THROW_ERROR( error , args )                                     \
00177         {                                                               \
00178                 if (error) {                                            \
00179                         char buffer[512];                               \
00180                         strerror_r(error, buffer, sizeof(buffer));      \
00181                         WAVE_EX(wex);                                   \
00182                         wex << "Encountered an error (" << error;       \
00183                         wex << "): " << buffer;                         \
00184                         wex << "\n" << args ;                           \
00185                 }                                                       \
00186         }
00187 
00188 
00189 
00190 /// smart file descriptor -- only use this for low-level objects!  You probably
00191 ///     want to use nstream or iostreams instead.
00192 class smart_fd {
00193 public:
00194         // constructor, destructor ---------------------------------------------
00195         smart_fd(void) throw() : m_fd(-1) { }
00196         smart_fd(IN int fd) throw() : m_fd(fd) { }
00197         ~smart_fd(void) throw() { this->clear(); }
00198 
00199         // accessors -----------------------------------------------------------
00200         void clear(void) throw() {
00201                         if (m_fd > -1) {
00202 #ifdef WIN32
00203                                 if (_close(m_fd)) {
00204 #else   // WIN32
00205                                 if (close(m_fd)) {
00206 #endif  // WIN32
00207                                         DPRINTF("Failed to close descriptor");
00208                                 }
00209                                 m_fd = -1;
00210                         }
00211                 }
00212 
00213         bool open(IN const char * path, IN int flags) {
00214                         this->clear();
00215 #ifdef WIN32
00216                         m_fd = _open(path, flags);
00217 #else   // WIN32
00218                         m_fd = ::open(path, flags);
00219 #endif  // WIN32
00220                         return (m_fd > -1);
00221                 }
00222 
00223         operator int(void) const throw() { return m_fd; }
00224 
00225         operator bool(void) const throw() { return (m_fd > -1); }
00226         bool operator !(void) const throw() { return (m_fd < 0); }
00227 
00228 private:
00229         // we do not want to allow copy construction--who closes?
00230         smart_fd(IN const smart_fd&);
00231 
00232         // private member data -------------------------------------------------
00233         int             m_fd;   // file descriptor
00234 };
00235 
00236 
00237 /// smart directory handle.  You probably don't want to use this!  Use the
00238 /// nstream interfaces instead.  This is for low-level filesystem access.
00239 ///
00240 /// Why no constructor that takes a path?  Because opening a directory can fail!
00241 /// Always use the open() method, and always check the result.
00242 class smart_dir {
00243 public:
00244         // constructor, destructor ---------------------------------------------
00245         smart_dir(void) throw() : m_dir(NULL) { }
00246         ~smart_dir(void) throw() { this->clear(); }
00247 
00248         // smart_dir class interface methods -----------------------------------
00249         void clear(void) throw() {
00250                         if (m_dir) {
00251                                 closedir(m_dir);
00252                                 m_dir = NULL;
00253                         }
00254                 }
00255 
00256         operator bool(void) const throw() { return !!m_dir; }
00257 
00258         void open(IN const char * path) {
00259                         this->clear();
00260                         m_dir = opendir(path);
00261                         if (!m_dir) {
00262                                 THROW_ERROR(errno,
00263                                     "Failed to open directory: " << path);
00264                         }
00265                 }
00266 
00267         void close(void) throw() { this->clear(); }
00268 
00269         bool getNextEntry(OUT struct dirent& entry) {
00270                         ASSERT(m_dir, "need to open directory first!");
00271                         struct dirent * result = NULL;
00272                         THROW_ERROR(readdir_r(m_dir, &entry, &result),
00273                             "Failed to read next directory entry");
00274                         return (result != NULL);
00275                 }
00276 
00277 private:
00278         // hidden constructors -------------------------------------------------
00279         smart_dir(IN const smart_dir&);
00280 
00281         // private member data -------------------------------------------------
00282         DIR *           m_dir;  // directory handle
00283 };
00284 
00285 
00286 
00287 /// advisory locking object
00288 /// \b WARNING: has no effect in Windows
00289 class AdvisoryLock {
00290 public:
00291         typedef int error_t;
00292 
00293         AdvisoryLock(void) throw() : m_fd(-1) { }
00294         ~AdvisoryLock(void) throw() {
00295                 this->unlock();
00296                 }
00297 
00298         bool attemptLock(IN const char * filename,
00299                         IN int operation) throw() {
00300                         ASSERT(filename, "NULL filename");
00301                         ASSERT(-1 == m_fd, "lock object already used");
00302 
00303 #ifdef WIN32
00304                         operation = operation;  // ignore parameter
00305                         m_fd = 0;
00306 #else   // WIN32
00307                         // open file descriptor
00308                         m_fd = open(filename, O_RDONLY);
00309                         if (-1 == m_fd) {
00310                                 error_t error = errno;
00311                                 DPRINTF("Could not open file: %s", filename);
00312                                 DPRINTF("Open failed with error %d : %s",
00313                                     error, strerror(error));
00314                                 return false;
00315                         }
00316 
00317                         // acquire lock
00318                         int result = flock(m_fd, operation);
00319                         if (-1 == result) {
00320                                 close(m_fd);
00321                                 m_fd = -1;
00322                                 error_t error = errno;
00323                                 DPRINTF("Lock failed with error %d : %s",
00324                                     error, strerror(error));
00325                                 return false;
00326                         }
00327 #endif  // WIN32
00328                         // success!
00329                         return true;
00330                 }
00331 
00332         void unlock(void) throw() {
00333 #ifndef WIN32
00334                         if (-1 != m_fd) {
00335                                 flock(m_fd, LOCK_UN);
00336                                 close(m_fd);
00337                                 m_fd = -1;
00338                         }
00339 #endif  // WIN32
00340                 }
00341 
00342 private:
00343         int             m_fd;   // file descriptor
00344 };
00345 
00346 
00347 /*@}*/  // end of documentation block
00348 
00349 #endif  // WAVEPACKET_UTIL_FILE_H__
00350