Files
mrw-cxx/mrw/file.hpp

402 lines
12 KiB
C++
Raw Normal View History

2005-01-07 00:35:17 +00:00
/** @file
$Id$
$Date$
$Author$
@copy © Marc Wäckerlin
@license LGPL, see file <a href="license.html">COPYING</a>
$Log$
2005-03-02 22:03:08 +00:00
Revision 1.5 2005/03/02 22:03:08 marc
some fixes for solaris
Revision 1.4 2005/02/28 07:17:24 marc
Dir is now usable and compilable, also added fixes for Solaris
2005-02-18 15:52:20 +00:00
Revision 1.3 2005/02/18 15:52:20 marc
correection in documentation
2005-01-28 07:49:32 +00:00
Revision 1.2 2005/01/28 07:49:32 marc
Save configuration using file.hpp
2005-01-07 00:35:17 +00:00
Revision 1.1 2005/01/07 00:31:38 marc
initial version
2005-01-28 07:49:32 +00:00
1 2 3 4 5 6 7 8
2005-01-07 00:35:17 +00:00
5678901234567890123456789012345678901234567890123456789012345678901234567890
2005-01-28 07:49:32 +00:00
*/
2005-01-07 00:35:17 +00:00
#ifndef __MRW_FILE_HPP__
#define __MRW_FILE_HPP__
#include <mrw/exception.hpp>
#include <mrw/errno.hpp>
2005-01-07 00:35:17 +00:00
#include <string>
#include <fstream>
2005-01-28 07:49:32 +00:00
#include <sys/types.h>
#include <dirent.h>
2005-01-07 00:35:17 +00:00
namespace mrw {
2005-01-28 07:49:32 +00:00
/** @defgroup sysFile File and System Utilities
@brief By now, some utilies for file handling, i.e. a copy command.
Some file and system specific utilities are missing in C++ and
often there is even no good alternative in C libraries.
*/
//@{
//============================================================================
/** @brief File handling utilities.
Utilities for file access in C++.
*/
2005-01-07 00:35:17 +00:00
class File {
2005-01-28 07:49:32 +00:00
//................................................................ methods
public:
/** @brief Copy a file.
Copy a file to a new location.
First the source file is fully read into memory, then written
to the destination. If the source file cannot be fully read,
the destination is left untouched.
@throw mrw::invalid_argument if read or write fails
@param from the source file name
@param to the destination file name
@note If the destination file already exists, it will be overwritten.
*/
static void copy(const std::string& from,
const std::string& to) throw(std::exception) {
std::ofstream os(to.c_str());
os<<read(from);
if (!os) throw mrw::invalid_argument("Cannot write file: '"+to+"'");
}
/** @brief Read a file fully into memory.
Read a file fully into memory.
@throw mrw::invalid_argument if read fails
@param filename the file to read
@return the file's contents
*/
static std::string read(const std::string& filename)
throw(std::exception) {
std::string contents; // declare on top to allow compiler optimization
std::ifstream is(filename.c_str());
std::string::size_type sz(size(is, filename));
contents.resize(sz); // make buffer long enough to store all
is.read(contents.begin().operator->(), sz); // hack to get the buffer
if (!is.good() && is.eof())
throw mrw::invalid_argument("Cannot read file: '"+filename+"'");
return contents;
};
/** @brief Save a string to a file.
A string is stored in a file, the whole file is overwritten by
the contents of the string. This is the counterpart of
read(const std::string&).
@throw mrw::invalid_argument if write fails
@param filename the name of the output file
@param contents the text to be written to the file
*/
static void save(const std::string& filename, const std::string& contents)
throw(std::exception) {
std::ofstream file(filename.c_str());
if (!(file<<contents))
throw mrw::invalid_argument("Cannot write file '"+filename+"'");
}
/** @brief Get the size of a file
Get the size of a file.
@throw mrw::invalid_argument if read fails
@param filename the file name
@return the file's size
*/
static std::string::size_type size(const std::string& filename)
throw(std::exception) {
std::ifstream is(filename.c_str());
if (!is)
throw mrw::invalid_argument("Cannot get size of file: '"
+filename+"'");
is.seekg(0, std::ios::end);
if (!is)
throw mrw::invalid_argument("Cannot get size of file: '"
+filename+"'");
return is.tellg();
}
/** @brief Get the size of a file
Get the size of a file.
@throw mrw::invalid_argument if read fails
@param file the filestream
@param filename the name of the file (required for the exception)
@return the file's size
*/
static std::string::size_type size(std::ifstream& file,
const std::string& filename = "")
throw(std::exception) {
if (!file)
throw mrw::invalid_argument("Cannot get size of file: '"
+filename+"'");
file.seekg(0, std::ios::end);
std::string::size_type sz(file.tellg());
file.seekg(0, std::ios::beg);
if (!file)
throw mrw::invalid_argument("Cannot get size of file: '"
+filename+"'");
return sz;
}
/** @brief Remove a file
Remove a file
@throw mrw::unix_error in case of failure,
i.e. if the file does not exist
@param file the file name
*/
static void remove(const std::string& file) throw(std::exception) {
if (unlink(file.c_str()))
throw mrw::unix_error("Cannot remove file "+file);
}
2005-01-28 07:49:32 +00:00
};
//============================================================================
/** @brief Directory access
Parse through directories:
@code
mrw::Dir dir(mrw::ifelse(getenv("HOME"), ""));
while (dir) {
std::cout<<"Found ["<<dir().typestr()<<"] "<<dir().name()<<std::endl;
}
@endcode
*/
2005-01-28 07:49:32 +00:00
class Dir {
//............................................................... typedefs
public:
//------------------------------------------------------------------ Entry
/** @brief a directory entry
A Directory Entry. */
2005-01-28 07:49:32 +00:00
class Entry {
//........................................................... typedefs
public:
/** @brief The type of the actual file.
The type of the actual file.
@note On some systems UNKNOWN is the only
value returned. This is i.e. true on
LINUX...
2005-01-28 07:49:32 +00:00
*/
enum FileType {
#ifdef _DIRENT_HAVE_D_TYPE
2005-01-28 07:49:32 +00:00
UNKNOWN = DT_UNKNOWN, ///< The type is unknown.
REGULAR = DT_REG, ///< A regular file.
DIR = DT_DIR, ///< A directory.
FIFO = DT_FIFO, ///< A named pipe, or FIFO.
SOCKET = DT_SOCK, ///< A local-domain socket.
CHAR = DT_CHR, ///< A character device.
BLOCK = DT_BLK ///< A block device.
#else
UNKNOWN, REGULAR, DIR, FIFO, SOCKET, CHAR, BLOCK // dummy
#endif
2005-01-28 07:49:32 +00:00
};
//............................................................ methods
public:
Entry(): _new(true) {}
/// Get the name of the actual file.
operator const std::string&() const throw(std::bad_exception) {
if (_new) { // only convert to string once
_new = false;
_name = _dirent.d.d_name;
}
return _name;
}
/// Get the name of the actual file.
const std::string& name() const throw(std::bad_exception) {
2005-01-28 07:49:32 +00:00
if (_new) { // only convert to string once
_new = false;
_name = _dirent.d.d_name;
}
return _name;
}
/// Get the name of the actual file.
const std::string& operator()() throw(std::bad_exception) {
return *this;
}
/// Get the type of the actual file.
FileType type() const throw() {
#ifdef _DIRENT_HAVE_D_TYPE
2005-01-28 07:49:32 +00:00
return FileType(_dirent.d.d_type);
#else
2005-03-02 22:03:08 +00:00
return UNKNOWN;
#endif
2005-01-28 07:49:32 +00:00
}
/// Get the type of the actual file as string.
const std::string& typestr() const throw() {
static const std::string UNKNOWN_("UNKNOWN");
static const std::string REGULAR_("REGULAR");
static const std::string DIR_("DIR");
static const std::string FIFO_("FIFO");
static const std::string SOCKET_("SOCKET");
static const std::string CHAR_("CHAR");
static const std::string BLOCK_("BLOCK");
2005-03-02 22:03:08 +00:00
#ifdef _DIRENT_HAVE_D_TYPE
switch (_dirent.d.d_type) {
case UNKNOWN: return UNKNOWN_;
case REGULAR: return REGULAR_;
case DIR: return DIR_;
case FIFO: return FIFO_;
case SOCKET: return SOCKET_;
case CHAR: return CHAR_;
case BLOCK: return BLOCK_;
}
abort(); // this line is never reached
#else
2005-03-02 22:03:08 +00:00
return UNKNOWN_;
#endif
}
2005-01-28 07:49:32 +00:00
//.......................................................... variables
private:
friend class Dir;
#ifdef NAME_MAX
2005-01-28 07:49:32 +00:00
union { // for portability reasons, see "info readdir_r"
dirent d;
char b[offsetof (struct dirent, d_name) + NAME_MAX + 1];
} _dirent;
#else
union { // for portability reasons, see "info readdir_r"
dirent d;
char b[offsetof (struct dirent, d_name) + 1000 + 1];
} _dirent;
#endif
2005-01-28 07:49:32 +00:00
mutable bool _new;
mutable std::string _name;
2005-01-28 07:49:32 +00:00
//............................................................ methods
private:
dirent& entry() throw() {
_new = true;
return _dirent.d;
}
};
//------------------------------------------------------------------------
//................................................................ methods
public:
/** @brief Open a directory for reading the file list.
Open a directory for reading the file list.
@throw mrw::invalid_argument if directory cannot be opened
@param dir name of the directory to open
@param ignoreDots ignore directories <code>.</code> and
2005-01-28 07:49:32 +00:00
<code>..</code> (self anf top)
*/
Dir(const std::string& dir, bool ignoreDots=true) throw(std::exception):
_ignoreDots(ignoreDots), _dir(opendir(dir.c_str())) {
if (!_dir)
throw mrw::invalid_argument("Cannot read directory: '"+dir+'\'');
}
/// Directory is closed automatically in the destructor.
~Dir() throw() {
closedir(_dir);
}
/** @brief Advance to the next directory entry.
Advance to the next directory entry.
@code
mrw::Dir dir("/home");
while (dir) ...
@endcode
@return
- @c true if an entry has been found
- @c false if there are no more entries left
@warning If you call this method again after @c false was
returned, behaviour is unspecified, but a crash
is probable.
*/
operator bool() throw() {
static const std::string D("."), DD("..");
static dirent* fake;
if (readdir_r(_dir, &_entry.entry(), &fake) || !fake) return false;
if (_ignoreDots
&& (_entry.type()==Entry::UNKNOWN || _entry.type()==Entry::DIR)
&& (D==_entry() || DD==_entry()))
2005-01-28 07:49:32 +00:00
return operator bool();
return true;
}
/** @brief Get the actual directory entry.
Returns the actual directory entry.
@code
mrw::Dir dir("/home");
while (dir) {
2005-02-18 15:52:20 +00:00
mrw::Dir::Entry entry(dir());
2005-01-28 07:49:32 +00:00
...
}
@endcode
@return the actual directory entry
@warning You must first call operator bool(), otherwise the
behaviour is undefined, but a crash is probable.
*/
const Entry& operator()() {
return _entry;
}
//.............................................................. variables
private:
bool _ignoreDots;
DIR* _dir;
Entry _entry;
2005-01-07 00:35:17 +00:00
};
2005-01-28 07:49:32 +00:00
//@}
2005-01-07 00:35:17 +00:00
}
#endif