From 7fca6f5f31bbb4a9443d4deee5bc81ec4a51c0b1 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 2 Jul 2018 20:29:34 +0200 Subject: [PATCH] Non-Qt implementations of file utils - needs testing. --- src/tl/tl/tlFileUtils.cc | 285 ++++++++++++++++++--------- src/tl/tl/tlFileUtils.h | 21 +- src/tl/tl/tlStream.cc | 323 +------------------------------ src/tl/tl/tlStream.h | 317 +++++++++++++++++++++++++++++- src/tl/tl/tlString.cc | 48 +++-- src/tl/tl/tlString.h | 15 ++ src/tl/unit_tests/tlFileUtils.cc | 6 + 7 files changed, 586 insertions(+), 429 deletions(-) diff --git a/src/tl/tl/tlFileUtils.cc b/src/tl/tl/tlFileUtils.cc index ce3cc2f61..9d18e0a97 100644 --- a/src/tl/tl/tlFileUtils.cc +++ b/src/tl/tl/tlFileUtils.cc @@ -21,6 +21,7 @@ */ #include "tlFileUtils.h" +#include "tlStream.h" #include "tlLog.h" #include "tlInternational.h" @@ -28,6 +29,7 @@ #include #include +#include namespace tl { @@ -191,35 +193,140 @@ is_parent_path (const std::string &parent, const std::string &path) return (is_same_file (parent, tl::combine_path (tl::join (parts, ""), ""))); } -bool rm_dir_recursive (const std::string &path) +std::vector dir_entries (const std::string &s, bool with_files, bool with_dirs, bool without_dotfiles) { -#if 0 // @@@ - QDir dir (path); + std::vector ee; - QStringList entries = dir.entryList (QDir::NoDotAndDotDot | QDir::Files | QDir::Dirs); - for (QStringList::const_iterator e = entries.begin (); e != entries.end (); ++e) { - QFileInfo fi (dir.absoluteFilePath (*e)); - if (fi.isDir ()) { - if (! rm_dir_recursive (fi.filePath ())) { - return false; +#if defined(_WIN32) + + struct _wfinddata_t fileinfo; + + intptr_t h = _wfindfirst (tl::to_wstring (s + "\\*.*").c_str (), &fileinfo); + if (h != -1) { + + do { + + std::string e = tl::to_string fileinfo.name); + if (e.empty () || e == "." || e == "..") { + continue; } - } else if (fi.isFile ()) { - if (! dir.remove (*e)) { - tl::error << tr ("Unable to remove file: %1").arg (dir.absoluteFilePath (*e)); + + bool is_dir = ((fileinfo.attrib & _A_SUBDIR) != 0); + if ((e[0] != '.' || !without_dotfiles) && ((is_dir && with_dirs) || (!is_dir && with_files))) { + ee.push_back (e); + } + + } while (_wfindnext (h, &fileinfo) == 0); + + } + + _wfindclose (h); + +#else + + DIR *h = opendir (tl::to_local (s).c_str ()); + if (h) { + + struct dirent *d; + while ((d = readdir (h)) != NULL) { + + std::string e = tl::to_string_from_local (d->d_name); + if (e.empty () || e == "." || e == "..") { + continue; + } + + bool is_dir = (d->d_type == DT_DIR); + if ((e[0] != '.' || !without_dotfiles) && ((is_dir && with_dirs) || (!is_dir && with_files))) { + ee.push_back (e); + } + + } + + closedir (h); + + } + +#endif + + return ee; +} + +bool mkdir (const std::string &path) +{ +#if defined(_WIN32) + return _wunlink (tl::to_wstring (path).c_str ()) == 0; +#else + return unlink (tl::to_local (path).c_str ()) == 0; +#endif +} + +bool mkpath (const std::string &p) +{ + std::vector parts = split_path (absolute_file_path (p)); + + size_t i = 0; + std::string front; + if (! parts.empty () && is_drive (parts.front ())) { + front = parts.front (); + ++i; + } + + while (i < parts.size ()) { + front += parts[i++]; + if (! file_exists (front)) { + if (! mkdir (front)) { + tl::error << tr ("Unable to create directory: ") << front; return false; } } } - QString name = dir.dirName (); - if (dir.cdUp ()) { - if (! dir.rmdir (name)) { - tl::error << tr ("Unable to remove directory: %1").arg (dir.absoluteFilePath (name)); + return true; +} + +bool rm_file (const std::string &path) +{ +#if defined(_WIN32) + return _wunlink (tl::to_wstring (path).c_str ()) == 0; +#else + return unlink (tl::to_local (path).c_str ()) == 0; +#endif +} + +bool rm_dir (const std::string &path) +{ +#if defined(_WIN32) + return _wrmdir (tl::to_wstring (path).c_str ()) == 0; +#else + return rmdir (tl::to_local (path).c_str ()) == 0; +#endif +} + +bool rm_dir_recursive (const std::string &p) +{ + std::vector entries; + std::string path = tl::absolute_file_path (p); + + entries = dir_entries (path, false /*without_files*/, true /*with_dirs*/); + for (std::vector::const_iterator e = entries.begin (); e != entries.end (); ++e) { + if (! rm_dir_recursive (tl::combine_path (path, *e))) { return false; } } -#endif + entries = dir_entries (path, true /*with_files*/, false /*without_dirs*/); + for (std::vector::const_iterator e = entries.begin (); e != entries.end (); ++e) { + std::string tc = tl::combine_path (path, *e); + if (! rm_file (tc)) { + tl::error << tr ("Unable to remove file: ") << tc; + return false; + } + } + + if (! rm_dir (path)) { + tl::error << tr ("Unable to remove directory: ") << path; + return false; + } return true; } @@ -227,72 +334,47 @@ bool rm_dir_recursive (const std::string &path) bool cp_dir_recursive (const std::string &source, const std::string &target) { -#if 0 // @@@ - QDir dir (source); - QDir dir_target (target); + std::vector entries; + std::string path = tl::absolute_file_path (source); + std::string path_to = tl::absolute_file_path (target); - QStringList entries = dir.entryList (QDir::NoDotAndDotDot | QDir::Files | QDir::Dirs); - for (QStringList::const_iterator e = entries.begin (); e != entries.end (); ++e) { + entries = dir_entries (path, false /*without_files*/, true /*with_dirs*/); + for (std::vector::const_iterator e = entries.begin (); e != entries.end (); ++e) { + std::string tc = tl::combine_path (path_to, *e); + if (! mkpath (tc)) { + tl::error << tr ("Unable to create target directory: ") << tc; + return false; + } + if (! cp_dir_recursive (tl::combine_path (path, *e), tc)) { + return false; + } + } - QFileInfo fi (dir.absoluteFilePath (*e)); - QFileInfo fi_target (dir_target.absoluteFilePath (*e)); - - if (fi.isDir ()) { - - // Copy subdirectory - if (! fi_target.exists ()) { - if (! dir_target.mkdir (*e)) { - tl::error << tr ("Unable to create target directory: %1").arg (dir_target.absoluteFilePath (*e)); - return false; - } - } else if (! fi_target.isDir ()) { - tl::error << tr ("Unable to create target directory (is a file already): %1").arg (dir_target.absoluteFilePath (*e)); - return false; - } - if (! cp_dir_recursive (fi.filePath (), fi_target.filePath ())) { - return false; - } + entries = dir_entries (path, true /*with_files*/, false /*without_dirs*/); + for (std::vector::const_iterator e = entries.begin (); e != entries.end (); ++e) { // TODO: leave symlinks symlinks? How to copy symlinks with Qt? - } else if (fi.isFile ()) { - QFile file (fi.filePath ()); - QFile file_target (fi_target.filePath ()); + // copy the files + try { - if (! file.open (QIODevice::ReadOnly)) { - tl::error << tr ("Unable to open source file for reading: %1").arg (fi.filePath ()); - return false; - } - if (! file_target.open (QIODevice::WriteOnly)) { - tl::error << tr ("Unable to open target file for writing: %1").arg (fi_target.filePath ()); - return false; - } - - size_t chunk_size = 64 * 1024; - - while (! file.atEnd ()) { - QByteArray data = file.read (chunk_size); - file_target.write (data); - } - - file.close (); - file_target.close (); + tl::OutputFile os_file (tl::combine_path (path_to, *e)); + tl::OutputStream os (os_file); + tl::InputFile is_file (tl::combine_path (path, *e)); + tl::InputStream is (is_file); + is.copy_to (os); + } catch (tl::Exception &ex) { + tl::error << tr ("Unable to copy file ") << tl::combine_path (path_to, *e) << tr (" to ") << tl::combine_path (path, *e) + << tr ("(Error ") << ex.msg () << ")"; + return false; } } -#endif - return true; } -bool mkpath (const std::string &path) -{ - // @@@ - return false; -} - std::string absolute_path (const std::string &s) { std::vector parts = split_path (absolute_file_path (s)); @@ -305,39 +387,58 @@ std::string absolute_path (const std::string &s) std::string current_dir () { - char *cwd; #if defined(_WIN32) - cwd = _getcwd (NULL, 0); -#else - cwd = getcwd (NULL, 0); -#endif + wchar_t *cwd = _wgetcwd (NULL, 0); if (cwd == NULL) { return std::string (); } else { - std::string cwds (cwd); + std::string cwds (tl::to_string (std::wstring (cwd))); free (cwd); return cwds; } + +#else + + char *cwd = getcwd (NULL, 0); + if (cwd == NULL) { + return std::string (); + } else { + std::string cwds (tl::to_string_from_local (cwd)); + free (cwd); + return cwds; + } + +#endif } static std::pair absolute_path_of_existing (const std::string &s) { - char *fp; #if defined (_WIN32) - fp = _fullpath (NULL, s.c_str (), 0); -#else - fp = realpath (s.c_str (), NULL); -#endif + wchar_t *fp = _wfullpath (NULL, tl::to_wstring (s).c_str (), 0); if (fp == NULL) { return std::make_pair (std::string (), false); } else { - std::string fps (fp); + std::string fps (tl::to_string (std::wstring (fp))); free (fp); return std::make_pair (fps, true); } + +#else + + char *fp; + fp = realpath (tl::to_local (s).c_str (), NULL); + if (fp == NULL) { + return std::make_pair (std::string (), false); + } else { + std::string fps (tl::to_string_from_local (fp)); + free (fp); + return std::make_pair (fps, true); + } + +#endif } std::string absolute_file_path (const std::string &s) @@ -436,16 +537,25 @@ std::string extension (const std::string &s) return tl::join (fnp, "."); } +static int stat_func (const std::string &s, struct stat &st) +{ +#if defined(_WIN32) + return _wstat (tl::to_wstring (s).c_str (), &st); +#else + return stat (tl::to_local (s).c_str (), &st); +#endif +} + bool file_exists (const std::string &p) { struct stat st; - return stat (p.c_str (), &st) == 0; + return stat_func (p, st) == 0; } bool is_dir (const std::string &p) { struct stat st; - if (stat (p.c_str (), &st) != 0) { + if (stat_func (p, st) != 0) { return false; } else { return !S_ISREG (st.st_mode); @@ -466,8 +576,8 @@ bool is_same_file (const std::string &a, const std::string &b) #if defined(_WIN32) - HANDLE h1 = ::CreateFile (a.c_str (), 0, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - HANDLE h2 = ::CreateFile (b.c_str (), 0, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + HANDLE h1 = ::CreateFileW (tl::to_wstring (a).c_str (), 0, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + HANDLE h2 = ::CreateFileW (tl::to_wstring (b).c_str (), 0, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); bool result = false; @@ -493,10 +603,7 @@ bool is_same_file (const std::string &a, const std::string &b) #else struct stat sta, stb; - if (stat (a.c_str (), &sta) != 0) { - return false; - } - if (stat (b.c_str (), &stb) != 0) { + if (stat_func (a, sta) != 0 || stat_func (b, stb) != 0) { return false; } diff --git a/src/tl/tl/tlFileUtils.h b/src/tl/tl/tlFileUtils.h index 9c70f3703..f7d0a5cd8 100644 --- a/src/tl/tl/tlFileUtils.h +++ b/src/tl/tl/tlFileUtils.h @@ -96,6 +96,25 @@ bool TL_PUBLIC file_exists (const std::string &s); */ bool TL_PUBLIC is_dir (const std::string &s); +/** + * @brief Gets the directory entries for the given directory + * This method will NEVER return the ".." entry. + * @param with_files Includes all files + * @param with_dirs Include all directories + * @param without_dotfiles Exclude all ".*" files + */ +std::vector TL_PUBLIC dir_entries (const std::string &s, bool with_files = true, bool with_dirs = true, bool without_dotfiles = false); + +/** + * @brief Removes the given file and returns true on success + */ +bool TL_PUBLIC rm_file (const std::string &path); + +/** + * @brief Removes the given directory and returns true on success + */ +bool TL_PUBLIC rm_dir (const std::string &path); + /** * @brief Returns true, if the given path is the same directory of file than the other one */ @@ -120,7 +139,7 @@ std::string TL_PUBLIC current_dir (); * added if the path terminates with a separator (like "C:\" or "/home/user/"). * The idea is that the last element is the file name part. */ -static std::vector split_path (const std::string &p); +std::vector split_path (const std::string &p); } diff --git a/src/tl/tl/tlStream.cc b/src/tl/tl/tlStream.cc index 0cad3e585..c17ee84bf 100644 --- a/src/tl/tl/tlStream.cc +++ b/src/tl/tl/tlStream.cc @@ -124,185 +124,6 @@ public: { } }; -// --------------------------------------------------------------------------------- -// Input file delegate implementations - declaration and implementation - -/** - * @brief A zlib input file delegate - * - * Implements the reader for a zlib stream - */ -class InputZLibFile - : public InputStreamBase -{ -public: - /** - * @brief Open a file with the given path - * - * Opening a file is a prerequisite for reading from the - * object. open() will throw a FileOpenErrorException if - * an error occurs. - * - * @param path The (relative) path of the file to open - */ - InputZLibFile (const std::string &path); - - /** - * @brief Close the file - * - * The destructor will automatically close the file. - */ - virtual ~InputZLibFile (); - - /** - * @brief Read from a file - * - * Implements the basic read method. - * Will throw a ZLibReadErrorException if an error occurs. - */ - virtual size_t read (char *b, size_t n); - - virtual void reset (); - - virtual void close (); - - virtual std::string source () const - { - return m_source; - } - - virtual std::string absolute_path () const; - - virtual std::string filename () const; - -private: - std::string m_source; - gzFile m_zs; -}; - -/** - * @brief A simple input file delegate - * - * Implements the reader for ordinary files. - */ -class InputFile - : public InputStreamBase -{ -public: - /** - * @brief Open a file with the given path - * - * Opening a file is a prerequisite for reading from the - * object. open() will throw a FileOpenErrorException if - * an error occurs. - * - * @param path The (relative) path of the file to open - * @param read True, if the file should be read, false on write. - */ - InputFile (const std::string &path); - - /** - * @brief Close the file - * - * The destructor will automatically close the file. - */ - virtual ~InputFile (); - - virtual size_t read (char *b, size_t n); - - virtual void reset (); - - virtual void close (); - - virtual std::string source () const - { - return m_source; - } - - virtual std::string absolute_path () const; - - virtual std::string filename () const; - -private: - std::string m_source; - int m_fd; -}; - -/** - * @brief A simple pipe input delegate - * - * Implements the reader for pipe streams - */ -class InputPipe - : public InputStreamBase -{ -public: - /** - * @brief Open a stream by connecting with the stdout of a given command - * - * Opening a pipe is a prerequisite for reading from the - * object. open() will throw a FilePOpenErrorException if - * an error occurs - commonly if the command cannot be executed. - * This implementation is based on popen (). - * - * @param cmd The command to execute - * @param read True, if the file should be read, false on write. - */ - InputPipe (const std::string &path); - - /** - * @brief Close the pipe - * - * The destructor will automatically close the pipe. - */ - virtual ~InputPipe (); - - /** - * @brief Read from the pipe - * - * Implements the basic read method. - * Will throw a FilePReadErrorException if an error occurs. - */ - virtual size_t read (char *b, size_t n); - - /** - * @brief Reset to the beginning of the file - */ - virtual void reset (); - - /** - * @brief Closes the pipe - */ - virtual void close (); - - /** - * @brief Get the source specification (the file name) - * - * Returns an empty string if no file name is available. - */ - virtual std::string source () const - { - // No source (in the sense of a file name) is available .. - return std::string (); - } - - virtual std::string absolute_path () const - { - // No source (in the sense of a file name) is available .. - return std::string (); - } - - virtual std::string filename () const - { - // No source (in the sense of a file name) is available .. - return std::string (); - } - -private: - FILE *m_file; - std::string m_source; -}; - // --------------------------------------------------------------- // InputStream implementation @@ -490,8 +311,7 @@ InputStream::read_all () return str; } -void -InputStream::copy_to (tl::OutputStream &os) +void InputStream::copy_to(tl::OutputStream &os) { const size_t chunk = 65536; char b [chunk]; @@ -794,147 +614,6 @@ InputZLibFile::filename () const return tl::filename (m_source); } -// --------------------------------------------------------------------------------- -// OutputStreamBase implementations - declarations and implementation - -/** - * @brief A zlib output file delegate - * - * Implements the writer for a zlib stream - */ -class OutputZLibFile - : public OutputStreamBase -{ -public: - /** - * @brief Open a file with the given path - * - * Opening a file is a prerequisite for reading from the - * object. open() will throw a FileOpenErrorException if - * an error occurs. - * - * @param path The (relative) path of the file to open - */ - OutputZLibFile (const std::string &path); - - /** - * @brief Close the file - * - * The destructor will automatically close the file. - */ - virtual ~OutputZLibFile (); - - /** - * @brief Write to a file - * - * Implements the basic write method. - * Will throw a ZLibWriteErrorException if an error occurs. - */ - virtual void write (const char *b, size_t n); - -private: - std::string m_source; - gzFile m_zs; -}; - -/** - * @brief A simple output file delegate - * - * Implements the writer for ordinary files. - */ -class OutputFile - : public OutputStreamBase -{ -public: - /** - * @brief Open a file with the given path - * - * Opening a file is a prerequisite for reading from the - * object. open() will throw a FileOpenErrorException if - * an error occurs. - * - * @param path The (relative) path of the file to open - * @param read True, if the file should be read, false on write. - */ - OutputFile (const std::string &path); - - /** - * @brief Close the file - * - * The destructor will automatically close the file. - */ - virtual ~OutputFile (); - - /** - * @brief Seek to the specified position - * - * Writing continues at that position after a seek. - */ - virtual void seek (size_t s); - - /** - * @brief Returns a value indicating whether that stream supports seek - */ - bool supports_seek () - { - return true; - } - - /** - * @brief Write to a file - * - * Implements the basic write method. - * Will throw a FileWriteErrorException if an error occurs. - */ - virtual void write (const char *b, size_t n); - -private: - std::string m_source; - int m_fd; -}; - -/** - * @brief A simple pipe output delegate - * - * Implements the writer for pipe streams - */ -class OutputPipe - : public OutputStreamBase -{ -public: - /** - * @brief Open a stream by connecting with the stdout of a given command - * - * Opening a pipe is a prerequisite for reading from the - * object. open() will throw a FilePOpenErrorException if - * an error occurs - commonly if the command cannot be executed. - * This implementation is based on popen (). - * - * @param cmd The command to execute - * @param read True, if the file should be read, false on write. - */ - OutputPipe (const std::string &path); - - /** - * @brief Close the pipe - * - * The destructor will automatically close the pipe. - */ - virtual ~OutputPipe (); - - /** - * @brief Write to a file - * - * Implements the basic write method. - * Will throw a FilePWriteErrorException if an error occurs. - */ - virtual void write (const char *b, size_t n); - -private: - FILE *m_file; - std::string m_source; -}; - // --------------------------------------------------------------- // OutputStream implementation diff --git a/src/tl/tl/tlStream.h b/src/tl/tl/tlStream.h index 373059af0..7ac4c19d4 100644 --- a/src/tl/tl/tlStream.h +++ b/src/tl/tl/tlStream.h @@ -166,6 +166,182 @@ private: size_t m_length, m_pos; }; +/** + * @brief A zlib input file delegate + * + * Implements the reader for a zlib stream + */ +class InputZLibFile + : public InputStreamBase +{ +public: + /** + * @brief Open a file with the given path + * + * Opening a file is a prerequisite for reading from the + * object. open() will throw a FileOpenErrorException if + * an error occurs. + * + * @param path The (relative) path of the file to open + */ + InputZLibFile (const std::string &path); + + /** + * @brief Close the file + * + * The destructor will automatically close the file. + */ + virtual ~InputZLibFile (); + + /** + * @brief Read from a file + * + * Implements the basic read method. + * Will throw a ZLibReadErrorException if an error occurs. + */ + virtual size_t read (char *b, size_t n); + + virtual void reset (); + + virtual void close (); + + virtual std::string source () const + { + return m_source; + } + + virtual std::string absolute_path () const; + + virtual std::string filename () const; + +private: + std::string m_source; + gzFile m_zs; +}; + +/** + * @brief A simple input file delegate + * + * Implements the reader for ordinary files. + */ +class InputFile + : public InputStreamBase +{ +public: + /** + * @brief Open a file with the given path + * + * Opening a file is a prerequisite for reading from the + * object. open() will throw a FileOpenErrorException if + * an error occurs. + * + * @param path The (relative) path of the file to open + * @param read True, if the file should be read, false on write. + */ + InputFile (const std::string &path); + + /** + * @brief Close the file + * + * The destructor will automatically close the file. + */ + virtual ~InputFile (); + + virtual size_t read (char *b, size_t n); + + virtual void reset (); + + virtual void close (); + + virtual std::string source () const + { + return m_source; + } + + virtual std::string absolute_path () const; + + virtual std::string filename () const; + +private: + std::string m_source; + int m_fd; +}; + +/** + * @brief A simple pipe input delegate + * + * Implements the reader for pipe streams + */ +class InputPipe + : public InputStreamBase +{ +public: + /** + * @brief Open a stream by connecting with the stdout of a given command + * + * Opening a pipe is a prerequisite for reading from the + * object. open() will throw a FilePOpenErrorException if + * an error occurs - commonly if the command cannot be executed. + * This implementation is based on popen (). + * + * @param cmd The command to execute + * @param read True, if the file should be read, false on write. + */ + InputPipe (const std::string &path); + + /** + * @brief Close the pipe + * + * The destructor will automatically close the pipe. + */ + virtual ~InputPipe (); + + /** + * @brief Read from the pipe + * + * Implements the basic read method. + * Will throw a FilePReadErrorException if an error occurs. + */ + virtual size_t read (char *b, size_t n); + + /** + * @brief Reset to the beginning of the file + */ + virtual void reset (); + + /** + * @brief Closes the pipe + */ + virtual void close (); + + /** + * @brief Get the source specification (the file name) + * + * Returns an empty string if no file name is available. + */ + virtual std::string source () const + { + // No source (in the sense of a file name) is available .. + return std::string (); + } + + virtual std::string absolute_path () const + { + // No source (in the sense of a file name) is available .. + return std::string (); + } + + virtual std::string filename () const + { + // No source (in the sense of a file name) is available .. + return std::string (); + } + +private: + FILE *m_file; + std::string m_source; +}; + // --------------------------------------------------------------------------------- /** @@ -248,8 +424,9 @@ public: /** * @brief Copies the content of the stream to the output stream + * Throws an exception on error. */ - void copy_to (tl::OutputStream &os); + void copy_to(tl::OutputStream &os); /** * @brief Enable uncompression of the following DEFLATE-compressed block @@ -604,6 +781,144 @@ private: std::ostringstream m_stream; }; +/** + * @brief A zlib output file delegate + * + * Implements the writer for a zlib stream + */ +class OutputZLibFile + : public OutputStreamBase +{ +public: + /** + * @brief Open a file with the given path + * + * Opening a file is a prerequisite for reading from the + * object. open() will throw a FileOpenErrorException if + * an error occurs. + * + * @param path The (relative) path of the file to open + */ + OutputZLibFile (const std::string &path); + + /** + * @brief Close the file + * + * The destructor will automatically close the file. + */ + virtual ~OutputZLibFile (); + + /** + * @brief Write to a file + * + * Implements the basic write method. + * Will throw a ZLibWriteErrorException if an error occurs. + */ + virtual void write (const char *b, size_t n); + +private: + std::string m_source; + gzFile m_zs; +}; + +/** + * @brief A simple output file delegate + * + * Implements the writer for ordinary files. + */ +class OutputFile + : public OutputStreamBase +{ +public: + /** + * @brief Open a file with the given path + * + * Opening a file is a prerequisite for reading from the + * object. open() will throw a FileOpenErrorException if + * an error occurs. + * + * @param path The (relative) path of the file to open + * @param read True, if the file should be read, false on write. + */ + OutputFile (const std::string &path); + + /** + * @brief Close the file + * + * The destructor will automatically close the file. + */ + virtual ~OutputFile (); + + /** + * @brief Seek to the specified position + * + * Writing continues at that position after a seek. + */ + virtual void seek (size_t s); + + /** + * @brief Returns a value indicating whether that stream supports seek + */ + bool supports_seek () + { + return true; + } + + /** + * @brief Write to a file + * + * Implements the basic write method. + * Will throw a FileWriteErrorException if an error occurs. + */ + virtual void write (const char *b, size_t n); + +private: + std::string m_source; + int m_fd; +}; + +/** + * @brief A simple pipe output delegate + * + * Implements the writer for pipe streams + */ +class OutputPipe + : public OutputStreamBase +{ +public: + /** + * @brief Open a stream by connecting with the stdout of a given command + * + * Opening a pipe is a prerequisite for reading from the + * object. open() will throw a FilePOpenErrorException if + * an error occurs - commonly if the command cannot be executed. + * This implementation is based on popen (). + * + * @param cmd The command to execute + * @param read True, if the file should be read, false on write. + */ + OutputPipe (const std::string &path); + + /** + * @brief Close the pipe + * + * The destructor will automatically close the pipe. + */ + virtual ~OutputPipe (); + + /** + * @brief Write to a file + * + * Implements the basic write method. + * Will throw a FilePWriteErrorException if an error occurs. + */ + virtual void write (const char *b, size_t n); + +private: + FILE *m_file; + std::string m_source; +}; + // --------------------------------------------------------------------------------- /** diff --git a/src/tl/tl/tlString.cc b/src/tl/tl/tlString.cc index dbcd854e6..e3f16b69e 100644 --- a/src/tl/tl/tlString.cc +++ b/src/tl/tl/tlString.cc @@ -40,9 +40,9 @@ static std::locale c_locale ("C"); // ------------------------------------------------------------------------- // Conversion of UTF8 to wchar_t -static std::vector utf8_to_wchar (const std::string &s) +std::wstring to_wstring (const std::string &s) { - std::vector ws; + std::wstring ws; const char *cpe = s.c_str () + s.size (); for (const char *cp = s.c_str (); cp < cpe; ) { @@ -61,10 +61,10 @@ static std::vector utf8_to_wchar (const std::string &s) if (c32 >= 0x10000) { c32 -= 0x10000; - ws.push_back (wchar_t (0xd800 + (c32 >> 10))); - ws.push_back (wchar_t (0xdc00 + (c32 & 0x3ff))); + ws += wchar_t (0xd800 + (c32 >> 10)); + ws += wchar_t (0xdc00 + (c32 & 0x3ff)); } else { - ws.push_back (wchar_t (c32)); + ws += wchar_t (c32); } } @@ -72,11 +72,11 @@ static std::vector utf8_to_wchar (const std::string &s) return ws; } -static std::string wchar_to_utf8 (const std::vector &ws) +std::string to_string (const std::wstring &ws) { std::string s; - for (std::vector::const_iterator c = ws.begin (); c != ws.end (); ++c) { + for (std::wstring::const_iterator c = ws.begin (); c != ws.end (); ++c) { uint32_t c32 = *c; if (c32 >= 0xd800 && c + 1 < ws.end ()) { @@ -140,20 +140,36 @@ std::string tl::db_to_string (double d) std::string tl::to_upper_case (const std::string &s) { - std::vector ws = utf8_to_wchar (s); - for (std::vector::iterator c = ws.begin (); c != ws.end (); ++c) { + std::wstring ws = to_wstring (s); + for (std::wstring::iterator c = ws.begin (); c != ws.end (); ++c) { *c = towupper (*c); } - return wchar_to_utf8 (ws); + return to_string (ws); } std::string tl::to_lower_case (const std::string &s) { - std::vector ws = utf8_to_wchar (s); - for (std::vector::iterator c = ws.begin (); c != ws.end (); ++c) { + std::wstring ws = to_wstring (s); + for (std::wstring::iterator c = ws.begin (); c != ws.end (); ++c) { *c = towlower (*c); } - return wchar_to_utf8 (ws); + return to_string (ws); +} + +std::string to_local (const std::string &s) +{ + std::auto_ptr buffer (new char [MB_CUR_MAX]); // MB_CUR_MAX isn't a constant + std::string ls; + + std::wstring ws = to_wstring (s); + for (std::wstring::const_iterator c = ws.begin (); c != ws.end (); ++c) { + int length = wctomb (buffer.get (), *c); + for (int i = 0; i < length; ++i) { + ls += buffer.get ()[i]; + } + } + + return ls; } std::string to_string_from_local (const char *cp) @@ -161,7 +177,7 @@ std::string to_string_from_local (const char *cp) mbstate_t state; memset ((void *) &state, 0, sizeof (mbstate_t)); - std::vector ws; + std::wstring ws; size_t max = strlen (cp); @@ -172,12 +188,12 @@ std::string to_string_from_local (const char *cp) if (length < 1) { break; } - ws.push_back (wc); + ws += wc; cp += length; max -= length; } - return wchar_to_utf8 (ws); + return to_string (ws); } // ------------------------------------------------------------------------- diff --git a/src/tl/tl/tlString.h b/src/tl/tl/tlString.h index 4b2c7817f..e4a2ecb53 100644 --- a/src/tl/tl/tlString.h +++ b/src/tl/tl/tlString.h @@ -34,7 +34,9 @@ #include "tlException.h" #include "tlVariant.h" +#if defined(HAVE_QT) class QImage; +#endif namespace tl { @@ -262,6 +264,7 @@ TL_PUBLIC std::string to_string (float d, int prec); TL_PUBLIC std::string to_string (const unsigned char *cp, int length); TL_PUBLIC std::string to_string (const char *cp, int length); TL_PUBLIC std::string to_string_from_local (const char *cp); +TL_PUBLIC std::string to_local (const std::string &s); template inline std::string to_string (const T &o) { return o.to_string (); } template <> inline std::string to_string (const double &d) { return to_string (d, 12); } @@ -287,8 +290,20 @@ template <> inline std::string to_string (const std::string &s) { return s; } // to a variant object template <> inline std::string to_string (const tl::Variant &v) { return v.to_parsable_string (); } +#if defined(HAVE_QT) // some dummy conversions provided for tl::Variant implementation template <> inline std::string to_string (const QImage &) { return std::string (); } +#endif + +/** + * @brief Converts UTF-8 to wide character string + */ +std::wstring TL_PUBLIC to_wstring (const std::string &s); + +/** + * @brief Converts a wide character string to UTF-8 + */ +std::string TL_PUBLIC to_string (const std::wstring &ws); /** * @brief Convert to a quoted string diff --git a/src/tl/unit_tests/tlFileUtils.cc b/src/tl/unit_tests/tlFileUtils.cc index 146b24959..8d2831a10 100644 --- a/src/tl/unit_tests/tlFileUtils.cc +++ b/src/tl/unit_tests/tlFileUtils.cc @@ -24,6 +24,10 @@ #include "tlFileUtils.h" #include "tlUnitTest.h" +#if defined(HAVE_QT) + +// A few things we cross-check against Qt + #include #include #include @@ -161,3 +165,5 @@ TEST (3) file.close (); } } + +#endif