FileResource.h

Go to the documentation of this file.
00001 /*
00002  * The information in this file is
00003  * Copyright(c) 2007 Ball Aerospace & Technologies Corporation
00004  * and is subject to the terms and conditions of the
00005  * GNU Lesser General Public License Version 2.1
00006  * The license text is available from   
00007  * http://www.gnu.org/licenses/lgpl.html
00008  */
00009 
00010 #ifndef FILERESOURCE_H
00011 #define FILERESOURCE_H
00012 
00013 #include "AppConfig.h"
00014 #include "Resource.h"
00015 #include <string>
00016 
00017 #include <stdio.h>
00018 #include <stdlib.h>
00019 #include <errno.h>
00020 #include <sys/types.h>
00021 #include <sys/stat.h>
00022 #include <vector>
00023 
00024 #if defined(WIN_API)
00025 #include <io.h>
00026 #include <fcntl.h>
00027 #include <limits>
00028 #define WIN32_LEAN_AND_MEAN
00029 #include <windows.h>
00030 #else
00031 #include <fcntl.h>
00032 #include <sys/fcntl.h>
00033 #include <sys/statvfs.h>
00034 #include <unistd.h>
00035 #endif
00036 
00037 /**
00038  * The %FileObject is a trait object for use with the %Resource template.
00039  *
00040  * It provides capability for opening and closing files.
00041  *
00042  * @see        FileResource
00043  */
00044 class FileObject
00045 {
00046 public:
00047    /**
00048     * This is an implementation detail of the %FileObject class.
00049     *
00050     * It is used for passing the parameters required by fopen().
00051     */
00052    class Args
00053    {
00054    public:
00055       Args(std::string filename, std::string access = "r", bool deleteOnClose = false) :
00056          mFilename(filename),
00057          mAccess(access),
00058          mDeleteOnClose(deleteOnClose)
00059       {}
00060 
00061       std::string mFilename;
00062       std::string mAccess;
00063       bool mDeleteOnClose;
00064    };
00065 
00066    FILE* obtainResource(const Args& args) const
00067    {
00068       FILE* pFile = fopen(args.mFilename.c_str(), args.mAccess.c_str()); // standard open
00069       if (args.mAccess.find("r") >= 0 && pFile == NULL)
00070       { // opening for reads - original failed - check uppercase/lowercase
00071          std::string fname = args.mFilename.c_str();
00072          for (size_t i = 0; i < fname.size(); ++i)
00073          {
00074             fname[i] = (char) toupper(fname[i]);
00075          }
00076          pFile = fopen(fname.c_str(), args.mAccess.c_str());
00077          if (pFile == NULL)
00078          { // try lowercase
00079             for (size_t i = 0; i < fname.size(); ++i)
00080             {
00081                fname[i] = (char) tolower(fname[i]);
00082             }
00083             pFile = fopen(fname.c_str(), args.mAccess.c_str());
00084          }
00085       }
00086       return pFile;
00087    }
00088    void releaseResource(const Args& args, FILE* pStream) const
00089    {
00090       if (pStream != NULL)
00091       {
00092          fclose(pStream);
00093       }
00094 
00095       if ((args.mDeleteOnClose) && (args.mFilename.empty() == false))
00096       {
00097          remove(args.mFilename.c_str());
00098       }
00099    }
00100 };
00101 
00102 /**
00103  * This is a %Resource class that opens and closes files and optionally deletes
00104  * them after close.
00105  *
00106  * This class has a conversion operator to allow a %FileResource object to be
00107  * used wherever a FILE* would normally be used.
00108 */
00109 class FileResource : public Resource<FILE, FileObject>
00110 {
00111 public:
00112    /**
00113     * Constructs a Resource object that wraps a FILE*.
00114     *
00115     * Opens the specified file using the specified access modes.
00116     *
00117     * @param   pFilename
00118     *          The name of the file to open.
00119     * @param   pAccess
00120     *          The access mode to open the file with.  These match the modes
00121     *          used with fopen().
00122     * @param   deleteOnClose
00123     *          If \c true, the file will be deleted from the file system after
00124     *          it is closed.  If \c false, the file will simply be closed.
00125     */
00126    FileResource(const char* pFilename, const char* pAccess, bool deleteOnClose = false) :
00127       Resource<FILE, FileObject>(FileObject::Args(pFilename, pAccess, deleteOnClose))
00128    {}
00129 
00130    /**
00131     * Returns a pointer to the underlying FILE.
00132     *
00133     * This operator allows the %FileResource object to be used wherever a
00134     * FILE* would normally be used.
00135     *
00136     * @return  A pointer to the underlying FILE.
00137     */
00138    operator FILE*()
00139    {
00140       return get();
00141    }
00142 
00143    /**
00144     * Returns whether the file will be deleted after it is closed.
00145     *
00146     * @return  Returns \c true if the file will be deleted after it is closed,
00147     *          or \c false if the file will simply be closed.
00148     */
00149    bool getDeleteOnClose() const
00150    {
00151       const FileObject::Args& args = getArgs();
00152       return args.mDeleteOnClose;
00153    }
00154 
00155    /**
00156     * Sets whether the file will be deleted after it is closed.
00157     *
00158     * @param   deleteOnClose
00159     *          If set to \c true, the file will be deleted after it is closed.
00160     *          If set to \c false, the file will simply close without being
00161     *          deleted.
00162     */
00163    void setDeleteOnClose(bool deleteOnClose)
00164    {
00165       FileObject::Args& args = getArgs();
00166       args.mDeleteOnClose = deleteOnClose;
00167    }
00168 };
00169 
00170 class LargeFileResource
00171 {
00172 public:
00173    /**
00174     * Construct a object to represent a large file
00175     * regardless of platform.
00176     *
00177     * @param bResource
00178     *        if true, this object will own any opened file and
00179     *        will ensure it is closed when this object is destroyed.
00180     *        If false, the caller is responsible for closing any opened
00181     *        file.
00182     */
00183    LargeFileResource(bool bResource = true)
00184    {
00185       mbOwned = bResource;
00186       mHandle = -1;
00187    }
00188 
00189    /**
00190     * Destructs this object and will close any open file if
00191     * this object is a resource.
00192     */
00193    ~LargeFileResource()
00194    {
00195       if (mbOwned)
00196       {
00197          LargeFileResource::close();
00198       }
00199    }
00200 
00201    /**
00202     *  Copy-constructs this instance from an existing object. The new
00203     *  instance takes ownership of the file from the existing object.
00204     *
00205     *  @param   source
00206     *           The object to construct from. After this call, the source no
00207     *           longer owns the file.
00208     */
00209    LargeFileResource(LargeFileResource& source)
00210    {
00211       mHandle = source.mHandle;
00212       mbOwned = source.mbOwned;
00213 
00214       source.release(); // the source no longer owns the file
00215    }
00216 
00217    /**
00218     *  Sets this object to another one. The object assigned to
00219     *  takes ownership of the file from the source object.
00220     *
00221     *  @param   source
00222     *           The object to assign from.
00223     *           After this call, the source no longer owns the file.
00224     */
00225    LargeFileResource& operator=(LargeFileResource& source)
00226    {
00227       if (mbOwned) // a file is owned and is being overwritten, so we need some cleanup!
00228       {
00229          LargeFileResource::close();
00230       }
00231       mHandle = source.mHandle;
00232       mbOwned = source.mbOwned;
00233 
00234       source.release(); // the source no longer owns the file
00235       return *this;
00236    }
00237 
00238    /**
00239     * Opens the given filename with the given settings.
00240     *
00241     * @param filename
00242     *        The file to be opened.
00243     * @param openType
00244     *        This method does not support opening with type flag \b O_TEXT on Windows due to
00245     *        problems implementing a cross-platform readLine() method. Therefore \em openType
00246     *        should not include \b O_TEXT on Windows. The file will be opened in binary mode 
00247     *        by default on Windows platforms.
00248     *        Please see the documentation on _open or open64 for details on other
00249     *        open type flags.
00250     * @param permissionFlag
00251     *        Please see the documentation on _open or open64 for details on
00252     *        permission flags.
00253     *
00254     * @return true if the file could be opened with the requested settings,
00255     *         false otherwise.
00256     */
00257    bool open(std::string filename, int openType, int permissionFlag)
00258    {
00259       if (mbOwned)
00260       {
00261          //only close any file we have open if we own it, otherwise let it leak.
00262          LargeFileResource::close();
00263       }
00264 
00265 #if defined(WIN_API)
00266       // check for attempt to open in text mode under windows
00267       if (openType & O_TEXT)
00268       {
00269          return false;
00270       }
00271       
00272       // be sure openType has O_BINARY set so open mode isn't dependent on value of global variable _fmode
00273       openType |= O_BINARY;
00274 
00275       mHandle = _open(filename.c_str(), openType, permissionFlag );
00276 #else
00277       mHandle = open64(filename.c_str(), openType, permissionFlag | O_LARGEFILE );
00278 #endif
00279 
00280       return validHandle();
00281    }
00282 
00283    /**
00284     * Reserve a file of the requested size with the requested name.
00285     * This file will be open after being reserved and will close any
00286     * open file that is owned by this object.
00287     *
00288     * The file will be reserved using the following open and permission flags:
00289     *   - O_RDWR | O_CREAT | O_BINARY
00290     *   - S_IREAD | S_IWRITE | S_IEXEC
00291     *
00292     * @param filename
00293     *        The name of the file to reserve.
00294     * @param reserveSize
00295     *        The size of the file in bytes to reserve.
00296     *
00297     * @return \c True if the file was successfully reserved, \c false otherwise.
00298     */
00299    bool reserve(std::string filename, int64_t reserveSize)
00300    {
00301       if (!open(filename, O_RDWR | O_CREAT | O_BINARY, S_IREAD | S_IWRITE | S_IEXEC))
00302       {
00303          return false;
00304       }
00305 
00306 #if defined(UNIX_API)
00307       struct statvfs sbuf;
00308       if (statvfs(filename.c_str(), &sbuf) ==0)
00309       {
00310          // Be sure there is enough space in the file system
00311          if ((reserveSize + sbuf.f_bsize - 1) / sbuf.f_bsize < sbuf.f_bfree)
00312          {
00313             // seek here
00314             if (seek(reserveSize - 1, SEEK_SET) != (reserveSize - 1) || // seek failed, OR
00315                 write(filename.c_str(), 1) != 1)  // write failed
00316             {
00317                return false;
00318             }
00319          }
00320       }
00321 #elif defined(WIN_API)
00322       if (seek(reserveSize, SEEK_SET) != (reserveSize))
00323       {
00324          return false;
00325       }
00326       HANDLE winHandle = reinterpret_cast<HANDLE>(_get_osfhandle(mHandle));
00327       if (!::SetEndOfFile(winHandle))
00328       {
00329          return false;
00330       }
00331 #endif
00332       return true;
00333    }
00334 
00335    /**
00336     * Close the opened file, regardless of file ownership.
00337     *
00338     * @return See the documentation on _close or ::close
00339     */
00340    int close()
00341    {
00342       int retVal = -1;
00343       if (validHandle())
00344       {
00345 #if defined(WIN_API)
00346          retVal = _close(mHandle);
00347 #else
00348          retVal = ::close(mHandle);
00349 #endif
00350          mHandle = -1;
00351       }
00352 
00353       return retVal;
00354    }
00355 
00356    /**
00357     * Release ownership of the file.  This means any
00358     * opened file will not be closed during destruction of this object.
00359     */
00360    void release()
00361    {
00362       mbOwned = false;
00363    }
00364 
00365    /**
00366     * Take ownership of the file.  This means any opened file
00367     * will be closed during destruction of this object.
00368     */
00369    void lock()
00370    {
00371       mbOwned = true;  // grab ownership
00372    }
00373 
00374    /**
00375     * Create a file with the given name and permissions.
00376     * This file will be opened after creation and close any
00377     * open file that is owned by this object.
00378     *
00379     * @param filename
00380     *        The name of the file to create.
00381     * @param perm
00382     *        See the documentation on _creat or ::creat
00383     *
00384     * @return See the documentation on _creat or ::creat
00385     */
00386    int creat(std::string filename, int perm)
00387    {
00388       if (mbOwned)
00389       {
00390          //only close any file we have open if we own it, otherwise leak it.
00391          LargeFileResource::close();
00392       }
00393 #if defined(WIN_API)
00394       mHandle = _creat(filename.c_str(), perm);
00395 #else
00396       mHandle = ::creat(filename.c_str(), perm);
00397 #endif
00398 
00399       return mHandle;
00400    }
00401 
00402    /**
00403     * Determine if the current position is the end of the file.
00404     *
00405     * @return 1 if the position is the end of file, 0 if it is not, -1 on error.
00406     */
00407    int eof()
00408    {
00409       if (!validHandle())
00410       {
00411          return -1;
00412       }
00413 #if defined(WIN_API)
00414       return _eof(mHandle);
00415 #else
00416       return (tell() >= fileLength() ? 1 : 0);
00417 #endif
00418    }
00419 
00420    /**
00421     * Determine the length of the opened file in bytes.
00422     *
00423     * @return The length of the opened file in bytes,
00424     *         of -1 if no file is opened.
00425     */
00426    int64_t fileLength()
00427    {
00428       if (!validHandle())
00429       {
00430          return -1;
00431       }
00432 #if defined(WIN_API)
00433       return _filelengthi64(mHandle);
00434 #else
00435       int64_t curPos = tell();
00436       seek(0, SEEK_END);
00437       int64_t fileSize = tell();
00438       seek(curPos, SEEK_SET);
00439       return fileSize;
00440 #endif
00441    }
00442 
00443    /**
00444     * Move the current position in the file the given offset in the
00445     * given direction for the opened file.
00446     *
00447     * @param offset
00448     *        The number of bytes to move the current position
00449     *        relative to the origin.
00450     * @param direction
00451     *        What the offset is relative to, can be one of the following:
00452     *        SEEK_SET - beginning of file.
00453     *        SEEK_CUR - current position in the file.
00454     *        SEEK_END - end of the file. (all three are defined in stdio.h)
00455     *
00456     * @return The new position in the file, or -1 if an error occurred.
00457     */
00458    int64_t seek(int64_t offset, int direction)
00459    {
00460       if (!validHandle())
00461       {
00462          return -1;
00463       }
00464 #if defined(WIN_API)
00465       return _lseeki64(mHandle, offset, direction);
00466 #else
00467       return lseek64(mHandle, offset, direction);
00468 #endif
00469    }
00470 
00471    /**
00472     * Reads the given number of bytes from the opened file into the buffer
00473     * pMem.  The current position in the file is updated.
00474     *
00475     * @param pMem
00476     *        Pointer to the buffer to put the contents of what is read
00477     *        from the file.  The buffer must be created at least as
00478     *        large as bytesToRead.
00479     * @param bytesToRead
00480     *        The number of bytes to read from the file.
00481     *
00482     * @return The number of bytes actually read from the file.  May be less
00483     *         than bytesToRead if end of file was encountered during read, or
00484     *         if file was opened in text mode and newline conversions were
00485     *         performed, -1 on error.
00486     */
00487    int64_t read(void* pMem, int64_t bytesToRead)
00488    {
00489       if ((pMem == NULL) || (bytesToRead == 0) || !validHandle())
00490       {
00491          return -1;
00492       }
00493 #if defined(WIN_API)
00494       int64_t bytesRead = 0;
00495       while (bytesToRead >= std::numeric_limits<unsigned int>::max())
00496       {
00497          int64_t r = _read(mHandle, pMem, std::numeric_limits<unsigned int>::max()) ;
00498          bytesRead += r;
00499          if (r < std::numeric_limits<unsigned int>::max())
00500          {
00501             return bytesRead;
00502          }
00503          pMem = reinterpret_cast<char*>(pMem) + std::numeric_limits<unsigned int>::max();
00504          bytesToRead -= std::numeric_limits<unsigned int>::max();
00505       }
00506       bytesRead += _read(mHandle, pMem, static_cast<unsigned int>(bytesToRead));
00507       return bytesRead;
00508 #else
00509       return ::read(mHandle, pMem, bytesToRead);
00510 #endif
00511    }
00512 
00513    /**
00514     * Returns the current position in the opened file.
00515     *
00516     * @return Returns the current position in the opened file, -1 on error.
00517     */
00518    int64_t tell()
00519    {
00520       if (!validHandle())
00521       {
00522          return -1;
00523       }
00524 #if defined(WIN_API)
00525       return _telli64(mHandle);
00526 #else
00527       return lseek64(mHandle, 0, SEEK_CUR);
00528 #endif
00529    }
00530 
00531    /**
00532     * Writes the given number of bytes from pMem to the opened file.
00533     * The curent position in the file is updated.
00534     *
00535     * @param pMem
00536     *        Pointer to the data to be written to the file.  The buffer
00537     *        must be at least bytesToWrite large.
00538     * @param bytesToWrite
00539     *        The number of bytes to copy from pMem into the file.
00540     *
00541     * @return the number of bytes actually written to the file, -1 on error.
00542     */
00543    int64_t write(const void* pMem, int64_t bytesToWrite)
00544    {
00545       if ((pMem == NULL) || (bytesToWrite == 0) || !validHandle())
00546       {
00547          return -1;
00548       }
00549 #if defined(WIN_API)
00550       int64_t bytesWritten = 0;
00551       while (bytesToWrite >= std::numeric_limits<unsigned int>::max())
00552       {
00553          int64_t wrote = _write(mHandle, pMem, std::numeric_limits<unsigned int>::max()) ;
00554          bytesWritten += wrote;
00555          if (wrote < std::numeric_limits<unsigned int>::max())
00556          {
00557             return bytesWritten;
00558          }
00559          pMem = reinterpret_cast<const char*>(pMem) + std::numeric_limits<unsigned int>::max();
00560          bytesToWrite -= std::numeric_limits<unsigned int>::max();
00561       }
00562       bytesWritten += _write(mHandle, pMem, static_cast<unsigned int>(bytesToWrite));
00563       return bytesWritten;
00564 #else
00565       return ::write(mHandle, pMem, bytesToWrite);
00566 #endif
00567    }
00568 
00569    /**
00570     * Returns true if this object has a file open.
00571     *
00572     * @return True if this object has a file open, false otherwise.
00573     */
00574    bool validHandle()
00575    {
00576       return (mHandle >= 0);
00577    }
00578 
00579    /**
00580     * Read in a line from the file.
00581     *
00582     * This is not a buffered read and may be inefficient.
00583     * This function will not successfully read lines greater than 4k characters in length.
00584     *
00585     * @param pError
00586     *        If NULL, this is ignored. If non-NULL, this will contain false if the
00587     *        read was successful, true if there was an error.
00588     *
00589     * @return The line read in with the newline character stripped.
00590     */
00591    std::string readLine(bool* pError = NULL)
00592    {
00593       std::string line;
00594       int64_t pos = tell();
00595       std::vector<char> buffer(4096, 0);
00596       int64_t bytesRead = read(&buffer.front(), buffer.size());
00597       if (bytesRead <= 0)
00598       {
00599          if (pError != NULL)
00600          {
00601             *pError = true;
00602          }
00603          return line;
00604       }
00605       std::string tmp(&buffer.front(), static_cast<std::string::size_type>(bytesRead));
00606       std::string::size_type eolLoc = tmp.find('\n');
00607       if (eolLoc == std::string::npos)
00608       {
00609          if (pError != NULL)
00610          {
00611             *pError = false;
00612          }
00613          return tmp;
00614       }
00615       seek(pos + eolLoc + 1, SEEK_SET);
00616       if (eolLoc != 0 && tmp[eolLoc-1] == '\r') // DOS-style EOL & non DOS-text I/O
00617       {
00618          --eolLoc;
00619       }
00620       line = tmp.substr(0, eolLoc);
00621       if (pError != NULL)
00622       {
00623          *pError = false;
00624       }
00625       return line;
00626    }
00627 
00628 private:
00629    int mHandle;
00630    bool mbOwned;
00631 };
00632 
00633 #endif

Software Development Kit - Opticks 4.9.0 Build 16218