diff options
Diffstat (limited to 'lib/file-opener.cc')
-rw-r--r-- | lib/file-opener.cc | 321 |
1 files changed, 321 insertions, 0 deletions
diff --git a/lib/file-opener.cc b/lib/file-opener.cc new file mode 100644 index 0000000..c0ccdd8 --- /dev/null +++ b/lib/file-opener.cc @@ -0,0 +1,321 @@ +// file-opener.cc -- dealing with files when doing multi-image scans +// Copyright (C) 2008 SEIKO EPSON CORPORATION +// +// This file is part of the 'iscan' program. +// +// The 'iscan' program is free-ish software. +// You can redistribute it and/or modify it under the terms of the GNU +// General Public License as published by the Free Software Foundation; +// either version 2 of the License or at your option any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of FITNESS +// FOR A PARTICULAR PURPOSE or MERCHANTABILITY. +// See the GNU General Public License for more details. +// +// You should have received a verbatim copy of the GNU General Public +// License along with this program; if not, write to: +// +// Free Software Foundation, Inc. +// 59 Temple Place, Suite 330 +// Boston, MA 02111-1307 USA +// +// As a special exception, the copyright holders give permission +// to link the code of this program with the esmod library and +// distribute linked combinations including the two. You must obey +// the GNU General Public License in all respects for all of the +// code used other then esmod. + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "file-opener.hh" + +#include <cerrno> +#include <cstdlib> +#include <cstring> +#include <list> +#include <iomanip> +#include <sstream> +#include <stdexcept> +#include <sys/types.h> +#include <sys/stat.h> + +namespace iscan +{ + static string tempfile (const string& dirname = string ()); + + //! Opening one or more files in a temporary file location. + file_opener::file_opener (bool collate) + : _collate (collate), _filename (string ()), _tempfile (string ()), + _fp (NULL), _pattern (NULL) + { + } + + //! Opening a file by \a name. + file_opener::file_opener (const string& name) + : _collate (true), _filename (string ()), _tempfile (string ()), + _fp (NULL), _pattern (NULL) + { + common_init (name); + } + + //! Opening files following a naming \a pattern. + file_opener::file_opener (const string& pattern, unsigned int start_index) + : _collate (false), _filename (string ()), _tempfile (string ()), + _fp (NULL), _pattern (NULL) + { + common_init (pattern); + + string::size_type hash = _pattern->basename.find_last_not_of (hash_mark); + + if (string::npos == hash) + { + delete _pattern; // new'd in common_init() + throw std::invalid_argument ("invalid file name pattern"); + } + ++hash; // first hash_mark + + _pattern->digits = _pattern->basename.length () - hash; + _pattern->basename = _pattern->basename.substr (0, hash); + _pattern->index = start_index; + } + + file_opener::~file_opener (void) + { + if (_fp) close (); + if (_tempfile != _filename) + rename (); + delete _pattern; + } + + //! Returns the C \c FILE pointer associated with a name(). + file_opener::operator FILE * (void) + { + if (_filename.empty ()) set_names (); + if (!_fp) open (); + return _fp; + } + + //! Returns the pathname of the output destination. + const string& + file_opener::name (void) const + { + if (_filename.empty ()) + const_cast<file_opener *> (this)->set_names (); + return _filename; + } + + //! Returns the pathname of the interim output destination. + /*! \deprecated Flaming hack to quickly fix our TIFF support. + */ + const string& + file_opener::temp (void) const + { + if (_filename.empty ()) + const_cast<file_opener *> (this)->set_names (); + return _tempfile; + } + + //! Returns the file's extension (without extension separator). + string + file_opener::extension (void) const + { + if (!_pattern) return null_ext; + return _pattern->extension.substr (ext_sep.size ()); + } + + //! Opens the next file, unless the file_opener is_collating(). + file_opener& + file_opener::operator++ (void) + { + if (_collate) return *this; + + if (_fp) close (); + if (_tempfile != _filename) + rename (); + set_names (true); + open (); + + return *this; + } + + //! Tells whether or not output will be collated. + bool + file_opener::is_collating (void) const + { + return _collate; + } + + //! Removes the output destination. + void + file_opener::remove (void) + { + if (_fp) + { + close (); + if (0 != ::remove (_tempfile.c_str ())) + throw std::ios_base::failure (strerror (errno)); + } + _tempfile = string (); + _filename = string (); + } + + const string file_opener::dir_sep = "/"; + const string file_opener::ext_sep = "."; + const char file_opener::hash_mark = '#'; + + const string file_opener::null_ext = string (); + + //! Handles common part of the constructors taking a string. + void + file_opener::common_init (const string& s) + { + string::size_type sep = s.rfind (dir_sep); + string::size_type dot = s.rfind (ext_sep); + + if (string::npos != sep && dot < sep) + dot = string::npos; + + _pattern = new struct pattern; + if (string::npos != sep) + { + ++sep; + _pattern->dirname = s.substr (0, sep); + _pattern->basename = s.substr (sep, dot - sep); + } + else + { + _pattern->dirname = string (); + _pattern->basename = s.substr (0, dot); + } + + if (string::npos != dot) + { + _pattern->extension = s.substr (dot); + } + else + { + _pattern->extension = null_ext; + } + _pattern->digits = 0; + _pattern->index = 0; + } + + void + file_opener::set_names (bool next) + { + using std::stringstream; + using std::setfill; + using std::setw; + + if (!_pattern) + { + _tempfile = tempfile (); + _filename = _tempfile; + } + else + { + if (next) ++_pattern->index; + + stringstream ss; + ss << _pattern->dirname + << _pattern->basename; + if (_pattern->digits) + { + ss << setfill ('0') + << setw (_pattern->digits) + << _pattern->index; + } + ss << _pattern->extension; + + _filename = ss.str (); + _tempfile = (!_pattern->digits + ? _filename : tempfile (_pattern->dirname.empty () + ? "." : _pattern->dirname)); + } + } + + //! Error handling wrapper around the C fopen() call. + void + file_opener::open (void) + { + _fp = fopen (_tempfile.c_str (), "wb"); + if (!_fp) + throw std::ios_base::failure (strerror (errno)); + } + + //! Error handling wrapper around the C fclose() call. + void + file_opener::close (void) + { + if (!_fp) return; + + int rv = fclose (_fp); + _fp = NULL; + + if (0 != rv) + throw std::ios_base::failure (strerror (errno)); + } + + void + file_opener::rename (void) + { + if (0 != ::rename (_tempfile.c_str (), _filename.c_str ())) + throw std::ios_base::failure (strerror (errno)); + + _tempfile = _filename; + } + + + //! Creates a temporary file in a secure way. + static string + tempfile (const string& dirname) + { + using std::list; + + list<string> dirs; + if (!dirname.empty ()) dirs.push_back (dirname); + if (getenv ("TMPDIR")) dirs.push_back (getenv ("TMPDIR")); +#ifdef P_tmpdir + dirs.push_back (P_tmpdir); // C library default +#endif + dirs.push_back ("/tmp"); // last resort + + string filename; + list<string>::iterator it = dirs.begin (); + + while (dirs.end () != it && filename.empty ()) + { + string ts = (*it + file_opener::dir_sep + + (!dirname.empty () ? "." : "") + + PACKAGE_TARNAME "XXXXXX"); + char *tc = new char [ts.length() + 1]; + + ts.copy (tc, ts.length ()); + tc[ts.length ()] = '\0'; + + mode_t um = umask (0); + umask (dirname.empty () ? 0077 : um); // use safe permissions + if (0 <= mkstemp (tc)) + filename = tc; + umask (um); + + // mkstemp from glibc 2.0.7 and later always uses 0600, (try + // to) revert here + if (!dirname.empty ()) + { + chmod (filename.c_str (), ( S_IRUSR | S_IRGRP | S_IROTH + | S_IWUSR | S_IWGRP | S_IWOTH) & ~um); + } + + delete [] tc; + ++it; + } + + return filename; + } + +} // namespace iscan |