diff --git a/src/actions.cpp b/src/actions.cpp new file mode 100644 index 00000000..87a1dec2 --- /dev/null +++ b/src/actions.cpp @@ -0,0 +1,423 @@ +// ***************************************************************** -*- C++ -*- +/* + * Copyright (C) 2004 Andreas Huggel + * + * This program is part of the Exiv2 distribution. + * + * This program is free 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 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +/* + File: actions.cpp + Version: $Name: $ $Revision: 1.1 $ + Author(s): Andreas Huggel (ahu) + History: 08-Dec-03, ahu: created + */ +// ***************************************************************************** +#include "rcsid.hpp" +EXIV2_RCSID("@(#) $Name: $ $Revision: 1.1 $ $RCSfile: actions.cpp,v $") + +// ***************************************************************************** +// included header files + +#include "actions.hpp" +#include "exiv2.hpp" +#include "utils.hpp" +#include "exif.hpp" + +// + standard includes +#include +#include +#include +#include +#include +#include +#include +#include + +// ***************************************************************************** +// local declarations +namespace { + + // Convert a string "YYYY:MM:DD HH:MI:SS" to a struct tm type, + // returns 0 if successful + int str2Tm(const std::string& timeStr, struct tm* tm); + + // Convert a string "YYYY:MM:DD HH:MI:SS" to a time type, -1 on error + time_t str2Time(const std::string& timeStr); + + // Convert a time type to a string "YYYY:MM:DD HH:MI:SS", "" on error + std::string time2Str(time_t time); + + // Return an error message for the return code of Exif::ExifData::read + std::string exifReadError(int rc, const std::string& path); + + // Return an error message for the return code of Exif::ExifData::write + std::string exifWriteError(int rc, const std::string& path); + +} + +// ***************************************************************************** +// class member definitions +namespace Action { + + Task::AutoPtr Task::clone() const + { + return AutoPtr(clone_()); + } + + TaskFactory* TaskFactory::instance_ = 0; + + TaskFactory& TaskFactory::instance() + { + if (0 == instance_) { + instance_ = new TaskFactory; + } + return *instance_; + } // TaskFactory::instance + + void TaskFactory::registerTask(TaskType type, Task::AutoPtr task) + { + Registry::iterator i = registry_.find(type); + if (i != registry_.end()) { + delete i->second; + } + registry_[type] = task.release(); + } // TaskFactory::registerTask + + TaskFactory::TaskFactory() + { + // Register a prototype of each known task + registerTask(adjust, Task::AutoPtr(new Adjust)); + registerTask(print, Task::AutoPtr(new Print)); + registerTask(rename, Task::AutoPtr(new Rename)); + } // TaskFactory c'tor + + Task::AutoPtr TaskFactory::create(TaskType type) + { + Registry::const_iterator i = registry_.find(type); + if (i != registry_.end() && i->second != 0) { + Task* t = i->second; + return t->clone(); + } + return Task::AutoPtr(0); + } // TaskFactory::create + + int Print::run(const std::string& path) + try { + Exif::ExifData exifData; + int rc = exifData.read(path); + if (rc) { + std::cerr << exifReadError(rc, path) << "\n"; + return rc; + } + Exif::ExifData::const_iterator md; + for (md = exifData.begin(); md != exifData.end(); ++md) { + std::cout << "0x" << std::setw(4) << std::setfill('0') << std::right + << std::hex << md->tag() << " " + << std::setw(9) << std::setfill(' ') << std::left + << md->ifdItem() << " " + << std::setw(27) << std::setfill(' ') << std::left + << md->tagName() << " " + << std::dec << md->value() << "\n"; + } + return 0; + } + catch(const Exif::Error& e) + { + std::cerr << "Exif exception in print action for file " + << path << ":\n" << e << "\n"; + return 1; + } // Print::run + + Print::AutoPtr Print::clone() const + { + return AutoPtr(dynamic_cast(clone_())); + } + + Task* Print::clone_() const + { + return new Print(*this); + } + + int Rename::run(const std::string& path) + try { + Exif::ExifData exifData; + int rc = exifData.read(path); + if (rc) { + std::cerr << exifReadError(rc, path) << "\n"; + return rc; + } + std::string key = "Image.DateTime.DateTimeOriginal"; + Exif::ExifData::iterator md = exifData.findKey(key); + if (md == exifData.end()) { + std::cerr << "Metadatum with key `" << key << "' " + << "not found in the file " << path << "\n"; + return 1; + } + std::string v = md->toString(); + if (v.length() == 0 || v[0] == ' ') { + std::cerr << "Image file creation timestamp not set in the file " + << path << "\n"; + return 1; + } + // Assemble the new filename from the timestamp + struct tm tm; + if (str2Tm(v, &tm) != 0) { + std::cerr << "Failed to parse timestamp `" << v + << "' in the file " << path << "\n"; + return 1; + } + const size_t max = 1024; + char basename[max]; + memset(basename, 0x0, max); + if (strftime(basename, max, Params::instance().format_.c_str(), &tm) == 0) { + std::cerr << "Filename format yields empty filename for the file " + << path << "\n"; + return 1; + } + std::string newPath + = Util::dirname(path) + "/" + basename + Util::suffix(path); + if ( Util::dirname(newPath) == Util::dirname(path) + && Util::basename(newPath) == Util::basename(path)) { + if (Params::instance().verbose_) { + std::cout << "This file already has the correct name\n"; + } + return 0; + } + if (Params::instance().verbose_) { + std::cout << "Renaming file to " << newPath << "\n"; + } + if (!Params::instance().force_ && Util::fileExists(newPath)) { + std::cout << Params::instance().progname() + << ": Overwrite `" << newPath << "'? "; + std::string s; + std::cin >> s; + if (s[0] != 'y' && s[0] != 'Y') return 0; + } + if (::rename(path.c_str(), newPath.c_str()) == -1) { + std::cerr << Params::instance().progname() + << ": Failed to rename " + << path << " to " << newPath << ": " + << Util::strError() << "\n"; + return 1; + } + return 0; + } + catch(const Exif::Error& e) + { + std::cerr << "Exif exception in rename action for file " << path + << ":\n" << e << "\n"; + return 1; + } // Rename::run + + Rename::AutoPtr Rename::clone() const + { + return AutoPtr(dynamic_cast(clone_())); + } + + Task* Rename::clone_() const + { + return new Rename(*this); + } + + int Adjust::run(const std::string& path) + try { + adjustment_ = Params::instance().adjustment_; + + Exif::ExifData exifData; + int rc = exifData.read(path); + if (rc) { + std::cerr << exifReadError(rc, path) << "\n"; + return rc; + } + rc = adjustDateTime(exifData, "Image.OtherTags.DateTime", path); + rc += adjustDateTime(exifData, "Image.DateTime.DateTimeOriginal", path); + rc += adjustDateTime(exifData, "Image.DateTime.DateTimeDigitized", path); + if (rc) return 1; + rc = exifData.write(path); + if (rc) { + std::cerr << exifWriteError(rc, path) << "\n"; + } + return rc; + } + catch(const Exif::Error& e) + { + std::cerr << "Exif exception in adjust action for file " << path + << ":\n" << e << "\n"; + return 1; + } // Adjust::run + + Adjust::AutoPtr Adjust::clone() const + { + return AutoPtr(dynamic_cast(clone_())); + } + + Task* Adjust::clone_() const + { + return new Adjust(*this); + } + + int Adjust::adjustDateTime(Exif::ExifData& exifData, + const std::string& key, + const std::string& path) const + { + Exif::ExifData::iterator md = exifData.findKey(key); + if (md == exifData.end()) { + // Key not found. That's ok, we do nothing. + return 0; + } + std::string timeStr = md->toString(); + if (timeStr == "" || timeStr[0] == ' ') { + std::cerr << path << ": Timestamp of metadatum with key `" + << key << "' not set\n"; + return 1; + } + time_t time = str2Time(timeStr); + if (time == (time_t)-1) { + std::cerr << path << ": Failed to parse or convert timestamp `" + << timeStr << "'\n"; + return 1; + } + if (Params::instance().verbose_) { + std::cout << path << ": Adjusting timestamp by" + << (adjustment_ < 0 ? " " : " +") + << adjustment_ << " seconds to "; + } + time += adjustment_; + timeStr = time2Str(time); + if (Params::instance().verbose_) { + std::cout << timeStr << "\n"; + } + md->setValue(timeStr); + return 0; + } // Adjust::adjustDateTime + +} // namespace Action + +// ***************************************************************************** +// local definitions +namespace { + + int str2Tm(const std::string& timeStr, struct tm* tm) + { + if (timeStr.length() == 0 || timeStr[0] == ' ') return 1; + if (timeStr.length() < 19) return 2; + if ( timeStr[4] != ':' || timeStr[7] != ':' || timeStr[10] != ' ' + || timeStr[13] != ':' || timeStr[16] != ':') return 3; + if (0 == tm) return 4; + ::memset(tm, 0x0, sizeof(struct tm)); + + long tmp; + if (!Util::strtol(timeStr.substr(0,4).c_str(), tmp)) return 5; + tm->tm_year = tmp - 1900; + if (!Util::strtol(timeStr.substr(5,2).c_str(), tmp)) return 6; + tm->tm_mon = tmp - 1; + if (!Util::strtol(timeStr.substr(8,2).c_str(), tmp)) return 7; + tm->tm_mday = tmp; + if (!Util::strtol(timeStr.substr(11,2).c_str(), tmp)) return 8; + tm->tm_hour = tmp; + if (!Util::strtol(timeStr.substr(14,2).c_str(), tmp)) return 9; + tm->tm_min = tmp; + if (!Util::strtol(timeStr.substr(17,2).c_str(), tmp)) return 10; + tm->tm_sec = tmp; + + return 0; + } // str2Tm + + time_t str2Time(const std::string& timeStr) + { + struct tm tm; + if (str2Tm(timeStr, &tm) != 0) return (time_t)-1; + return ::mktime(&tm); + } + + std::string time2Str(time_t time) + { + struct tm tm; + ::memset(&tm, 0x0, sizeof(struct tm)); + + if (0 == ::localtime_r(&time, &tm)) return ""; + + std::ostringstream os; + os << std::setfill('0') + << tm.tm_year + 1900 << ":" + << std::setw(2) << tm.tm_mon + 1 << ":" + << std::setw(2) << tm.tm_mday << " " + << std::setw(2) << tm.tm_hour << ":" + << std::setw(2) << tm.tm_min << ":" + << std::setw(2) << tm.tm_sec; + + return os.str(); + } // time2Str + + std::string exifReadError(int rc, const std::string& path) + { + std::string error; + switch (rc) { + case -1: + error = "Couldn't open file `" + path + "'"; + break; + case 1: + error = "Couldn't read from the input stream"; + break; + case 2: + error = "This does not look like a JPEG image"; + break; + case 3: + error = "No Exif data found in the file"; + break; + case -99: + error = "Unsupported Exif or GPS data found in IFD 1"; + break; + default: + error = "Reading Exif data failed, rc = " + Exif::toString(rc); + break; + } + return error; + } // exifReadError + + std::string exifWriteError(int rc, const std::string& path) + { + std::string error; + switch (rc) { + case -1: + error = "Couldn't open file `" + path + "'"; + break; + case -2: + error = "Couldn't open temporary file"; + break; + case -3: + error = "Renaming temporary file failed"; + break; + case 1: + error = "Couldn't read from the input stream"; + break; + case 2: + error = "This does not look like a JPEG image"; + break; + case 3: + error = "No JFIF APP0 or Exif APP1 segment found in the file"; + break; + case 4: + error = "Writing to the output stream failed"; + break; + default: + error = "Reading Exif data failed, rc = " + Exif::toString(rc); + break; + } + return error; + } // exifWriteError + +} diff --git a/src/actions.hpp b/src/actions.hpp new file mode 100644 index 00000000..146b2aee --- /dev/null +++ b/src/actions.hpp @@ -0,0 +1,187 @@ +// ***************************************************************** -*- C++ -*- +/* + * Copyright (C) 2004 Andreas Huggel + * + * This program is part of the Exiv2 distribution. + * + * This program is free 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 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +/*! + @file actions.hpp + @brief Implements base class Task, TaskFactory and the various supported + actions (derived from Task). + @version $Name: $ $Revision: 1.1 $ + @author Andreas Huggel (ahu) + ahuggel@gmx.net + @date 11-Dec-03, ahu: created + */ +#ifndef ACTIONS_HPP_ +#define ACTIONS_HPP_ + +// ***************************************************************************** +// included header files + +// + standard includes +#include +#include + +// ***************************************************************************** +// class declarations + +namespace Exif { + class ExifData; +} + +// ***************************************************************************** +// namespace extensions +/*! + @brief Contains all action classes (task subclasses). + */ +namespace Action { + + //! Enumerates all tasks + enum TaskType { none, adjust, print, rename }; + +// ***************************************************************************** +// class definitions + + /*! + @brief Base class for all concrete actions. + + Task provides a simple interface that actions must implement and a few + commonly used helpers. + */ + class Task { + public: + //! Shortcut for an auto pointer. + typedef std::auto_ptr AutoPtr; + //! Virtual copy construction. + AutoPtr clone() const; + /*! + @brief Application interface to perform a task. + + @param path Path of the file to process. + @return 0 if successful. + */ + virtual int run(const std::string& path) =0; + + private: + //! Internal virtual copy constructor. + virtual Task* clone_() const =0; + + }; + + /*! + @brief Task factory. + + Creates an instance of the task of the requested type. The factory is + implemented as a singleton, which can be accessed only through the static + member function instance(). + */ + class TaskFactory { + public: + /*! + @brief Get access to the task factory. + + Clients access the task factory exclusively through + this method. + */ + static TaskFactory& instance(); + + /*! + @brief Create a task. + + @param type Identifies the type of task to create. + @return An auto pointer that owns a task of the requested type. If + the task type is not supported, the pointer is 0. + @remark The caller of the function should check the content of the + returned auto pointer and take appropriate action (e.g., throw + an exception) if it is 0. + */ + Task::AutoPtr create(TaskType type); + + /*! + @brief Register a task prototype together with its type. + + The task factory creates new tasks of a given type by cloning its + associated prototype. Additional tasks can be registered. If called + for a type which already exists in the list, the corresponding + prototype is replaced. + + @param type Task type. + @param task Pointer to the prototype. Ownership is transfered to the + task factory. That's what the auto pointer indicates. + */ + void registerTask(TaskType type, Task::AutoPtr task); + + private: + //! Prevent construction other than through instance(). + TaskFactory(); + //! Prevent copy construction: not implemented. + TaskFactory(const TaskFactory& rhs); + + //! Pointer to the one and only instance of this class. + static TaskFactory* instance_; + //! Type used to store Task prototype classes + typedef std::map Registry; + //! List of task types and corresponding prototypes. + Registry registry_; + + }; // class TaskFactory + + //! %Print the %Exif (or other metadata) of a file to stdout + class Print : public Task { + public: + virtual int run(const std::string& path); + typedef std::auto_ptr AutoPtr; + AutoPtr clone() const; + + private: + virtual Task* clone_() const; + }; + + /*! + @brief %Rename a file to its metadate creation timestamp, + in the specified format. + */ + class Rename : public Task { + public: + virtual int run(const std::string& path); + typedef std::auto_ptr AutoPtr; + AutoPtr clone() const; + + private: + virtual Task* clone_() const; + }; + + //! %Adjust the %Exif (or other metadata) timestamps + class Adjust : public Task { + public: + virtual int run(const std::string& path); + typedef std::auto_ptr AutoPtr; + AutoPtr clone() const; + + private: + virtual Task* clone_() const; + int adjustDateTime(Exif::ExifData& exifData, + const std::string& key, + const std::string& path) const; + + long adjustment_; + }; + +} // namespace Action + +#endif // #ifndef ACTIONS_HPP_ diff --git a/src/exiv2.cpp b/src/exiv2.cpp new file mode 100644 index 00000000..cfb79f1c --- /dev/null +++ b/src/exiv2.cpp @@ -0,0 +1,285 @@ +// ***************************************************************** -*- C++ -*- +/* + * Copyright (C) 2004 Andreas Huggel + * + * This program is part of the Exiv2 distribution. + * + * This program is free 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 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +/* + Abstract: Command line program to display and manipulate image %Exif data + + File: exiv2.cpp + Version: $Name: $ $Revision: 1.1 $ + Author(s): Andreas Huggel (ahu) + History: 10-Dec-03, ahu: created + */ +// ***************************************************************************** +#include "rcsid.hpp" +EXIV2_RCSID("@(#) $Name: $ $Revision: 1.1 $ $RCSfile: exiv2.cpp,v $") + +// ***************************************************************************** +// included header files +#include "exiv2.hpp" +#include "actions.hpp" +#include "utils.hpp" + +#include +#include +#include + +#include +#include + +// ********************************************************************* +// local declarations +namespace { + + // Evaluate [-]HH[:MM[:SS]], returns true and sets time to the value + // in seconds if successful, else returns false. + bool parseTime(const std::string& ts, long& time); +} + +// ***************************************************************************** +// Main +int main(int argc, char* const argv[]) +{ + // Handle command line arguments + Params& params = Params::instance(); + if (params.getopt(argc, argv)) { + params.usage(); + return 1; + } + if (params.help_) { + params.help(); + return 0; + } + if (params.version_) { + params.version(); + return 0; + } + + // Create the required action class + Action::TaskFactory& taskFactory = Action::TaskFactory::instance(); + Action::Task::AutoPtr task + = taskFactory.create(Action::TaskType(params.action_)); + assert(task.get()); + + // Process all files + int n = 1; + int s = params.files_.size(); + int w = s > 9 ? s > 99 ? 3 : 2 : 1; + Params::Files::const_iterator e = params.files_.end(); + for (Params::Files::const_iterator i = params.files_.begin(); i != e; ++i) { + if (params.verbose_) { + std::cout << "File " << std::setw(w) << n++ << "/" << s << ": " + << *i << "\n"; + } + task->run(*i); + } + return 0; +} // main + +// ***************************************************************************** +// class Params +Params* Params::instance_ = 0; + +Params& Params::instance() +{ + if (0 == instance_) { + instance_ = new Params; + } + return *instance_; +} + +void Params::version(std::ostream& os) const +{ + os << "Exiv2 version 0.3, " + << "Copyright (C) 2004 Andreas Huggel.\n\n" + << "This is free software; see the source for copying conditions. " + << "There is NO \nwarranty; not even for MERCHANTABILITY or FITNESS FOR " + << "A PARTICULAR PURPOSE.\n"; +} + +void Params::usage(std::ostream& os) const +{ + os << "Usage: " << progname() + << " [ -hVvf ][ -a time ][ -r format ] action file ...\n\n" + << "Manipulate the Exif metadata of images.\n"; +} + +void Params::help(std::ostream& os) const +{ + usage(os); + os << "\nOptions:\n" + << " -h Display this help and exit.\n" + << " -V Show the program version and exit.\n" + << " -v Be more verbose during the program run.\n" + << " -f Do not prompt before overwriting existing files (force).\n" + << " -a time Time adjustment in the format [-]HH[:MM[:SS]]. This option\n" + << " is only used with the `adjust' action.\n" + << " -r fmt Filename format for the `rename' action. The format string\n" + << " follows strftime(3). Default filename format is " + << format_ << ".\n" + << "Actions:\n" + << " adjust Adjust the metadata timestamp by the given time. This action\n" + << " requires the option -a time.\n" + << " print Print the Exif (or other) image metadata.\n" + << " rename Rename files according to the metadata create timestamp. The\n" + << " filename format can be set with the option -r format.\n\n"; +} // Params::help + +int Params::option(int opt, const std::string& optarg, int optopt) +{ + int rc = 0; + switch (opt) { + case 'h': + help_ = true; + break; + + case 'V': + version_ = true; + break; + + case 'v': + verbose_ = true; + break; + + case 'f': + force_ = true; + break; + + case 'a': + adjust_ = parseTime(optarg, adjustment_); + if (!adjust_) { + std::cerr << progname() << ": Error parsing -a option argument `" + << optarg << "'\n"; + rc = 1; + } + break; + + case 'r': + format_ = optarg; + break; + + case ':': + std::cerr << progname() << ": Option -" << static_cast(optopt) + << " requires an argument\n"; + rc = 1; + break; + + case '?': + std::cerr << progname() << ": Unrecognized option -" + << static_cast(optopt) << "\n"; + rc = 1; + break; + + default: + std::cerr << progname() + << ": getopt returned unexpected character code " + << std::hex << opt << "\n"; + rc = 1; + } + + return rc; +} // Params::option + +int Params::nonoption(const std::string& argv) +{ + int rc = 0; + if (first_) { + // The first non-option argument must be the action + first_ = false; + if (argv == "adjust") action_ = Action::adjust; + if (argv == "print") action_ = Action::print; + if (argv == "rename") action_ = Action::rename; + if (action_ == Action::none) { + std::cerr << progname() << ": Unrecognized action `" + << argv << "'\n"; + rc = 1; + } + } + else { + files_.push_back(argv); + } + return rc; +} // Params::nonoption + +int Params::getopt(int argc, char* const argv[]) +{ + int rc = Util::Getopt::getopt(argc, argv, optstring_); + // Further consistency checks + if (help_ || version_) return 0; + if (action_ == Action::none) { + std::cerr << progname() << ": An action must be specified\n"; + rc = 1; + } + if (action_ == Action::adjust && !adjust_) { + std::cerr << progname() + << ": Adjust action requires option -a time\n"; + rc = 1; + } + if (0 == files_.size()) { + std::cerr << progname() << ": At least one file is required\n"; + rc = 1; + } + return rc; +} // Params::getopt + +// ********************************************************************* +// local implementations +namespace { + + bool parseTime(const std::string& ts, long& time) + { + std::string hstr, mstr, sstr; + char *cts = new char[ts.length() + 1]; + strcpy(cts, ts.c_str()); + char *tmp = ::strtok(cts, ":"); + if (tmp) hstr = tmp; + tmp = ::strtok(0, ":"); + if (tmp) mstr = tmp; + tmp = ::strtok(0, ":"); + if (tmp) sstr = tmp; + delete[] cts; + + int sign = 1; + long hh(0), mm(0), ss(0); + // [-]HH part + if (!Util::strtol(hstr.c_str(), hh)) return false; + if (hh < 0) { + sign = -1; + hh *= -1; + } + // check for the -0 special case + if (hh == 0 && hstr.find('-') != std::string::npos) sign = -1; + // MM part, if there is one + if (mstr != "") { + if (!Util::strtol(mstr.c_str(), mm)) return false; + if (mm > 59) return false; + if (mm < 0) return false; + } + // SS part, if there is one + if (sstr != "") { + if (!Util::strtol(sstr.c_str(), ss)) return false; + if (ss > 59) return false; + if (ss < 0) return false; + } + + time = sign * (hh * 3600 + mm * 60 + ss); + return true; + } // parseTime + +} diff --git a/src/exiv2.hpp b/src/exiv2.hpp new file mode 100644 index 00000000..86237175 --- /dev/null +++ b/src/exiv2.hpp @@ -0,0 +1,157 @@ +// ***************************************************************** -*- C++ -*- +/* + * Copyright (C) 2004 Andreas Huggel + * + * This program is part of the Exiv2 distribution. + * + * This program is free 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 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +/*! + @file exiv2.hpp + @brief Defines class Params, used for the command line handling of exiv2 + @version $Name: $ $Revision: 1.1 $ + @author Andreas Huggel (ahu) + ahuggel@gmx.net + @date 08-Dec-03, ahu: created + */ +#ifndef EXIV2_HPP_ +#define EXIV2_HPP_ + +// ***************************************************************************** +// included header files +#include "utils.hpp" + +// + standard includes +#include +#include +#include + +// ***************************************************************************** +// class definitions +/*! + @brief Implements the command line handling for the program. + + Derives from Util::Getopt to use the command line argument parsing + functionalty provided there. This class is implemented as a Singleton, + i.e., there is only one global instance of it, which can be accessed + from everywhere. + + Usage example:
+ @code + #include "params.h" + + int main(int argc, char* const argv[]) + { + Params& params = Params::instance(); + if (params.getopt(argc, argv)) { + params.usage(); + return 1; + } + if (params.help_) { + params.help(); + return 0; + } + if (params.version_) { + params.version(); + return 0; + } + + // do something useful here... + + return 0; + } + @endcode + */ +class Params : public Util::Getopt { +private: + std::string optstring_; + +public: + /*! + @brief Controls all access to the global Params instance. + @return Reference to the global Params instance. + */ + static Params& instance(); + + bool help_; //!< Help option flag. + bool version_; //!< Version option flag. + bool verbose_; //!< Verbose (talkative) option flag. + bool force_; //!< Force overwrites flag. + bool adjust_; //!< Adjustment flag. + //! %Action (integer rather than TaskType to avoid dependency). + int action_; + + long adjustment_; //!< Adjustment in seconds. + std::string format_; //!< Filename format (-r option arg). + + //! Container to store filenames. + typedef std::vector Files; + + Files files_; //!< List of non-option arguments. + +private: + /*! + @brief Default constructor. Note that optstring_ is initialized here. + Private to force instantiation through instance(). + */ + Params() : optstring_(":hVvfa:r:"), + help_(false), + version_(false), + verbose_(false), + force_(false), + adjust_(false), + action_(0), + adjustment_(0), + format_("%Y%m%d_%H%M%S"), + first_(true) {} + + //! Prevent copy-construction: not implemented. + Params(const Params& rhs); + + //! Pointer to the global Params object. + static Params* instance_; + + bool first_; + +public: + /*! + @brief Call Getopt::getopt() with optstring, to inititate command line + argument parsing, perform consistency checks after all command line + arguments are parsed. + + @param argc Argument count as passed to main() on program invocation. + @param argv Argument array as passed to main() on program invocation. + + @return 0 if successful, >0 in case of errors. + */ + int getopt(int argc, char* const argv[]); + + //! Handle options and their arguments. + virtual int option(int opt, const std::string& optarg, int optopt); + + //! Handle non-option parameters. + virtual int nonoption(const std::string& argv); + + //! Print a minimal usage note to an output stream. + void usage(std::ostream& os =std::cout) const; + + //! Print further usage explanations to an output stream. + void help(std::ostream& os =std::cout) const; + + //! Print version information to an output stream. + void version(std::ostream& os =std::cout) const; +}; // class Params + +#endif // #ifndef EXIV2_HPP_