From 122a856387c0eb08a0be5ea31977ed4f2827953a Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Wed, 15 Jan 2025 18:37:50 -0800 Subject: [PATCH] WIP: use fs::path for app Signed-off-by: Rosen Penev --- app/actions.cpp | 198 +++++++++++++++++++------------------- app/actions.hpp | 50 ++++++---- include/exiv2/basicio.hpp | 45 ++++++++- include/exiv2/futils.hpp | 17 ++++ include/exiv2/image.hpp | 23 ++++- src/basicio.cpp | 93 +++++++++++++++++- src/futils.cpp | 37 +++++++ src/image.cpp | 53 +++++++++- 8 files changed, 386 insertions(+), 130 deletions(-) diff --git a/app/actions.cpp b/app/actions.cpp index 334484b7c5..098f682be1 100644 --- a/app/actions.cpp +++ b/app/actions.cpp @@ -45,14 +45,6 @@ } while (false) #endif -#if __has_include() -#include -namespace fs = std::filesystem; -#else -#include -namespace fs = std::experimental::filesystem; -#endif - // ***************************************************************************** // local declarations namespace { @@ -97,7 +89,7 @@ std::string tm2Str(const tm* tm); be kept. @return 0 if successful, else an error code */ -int metacopy(const std::string& source, const std::string& tgt, Exiv2::ImageType targetType, bool preserve); +int metacopy(const fs::path& source, const fs::path& tgt, Exiv2::ImageType targetType, bool preserve); /*! @brief Rename a file according to a timestamp value. @@ -107,7 +99,7 @@ int metacopy(const std::string& source, const std::string& tgt, Exiv2::ImageType the file to. @return 0 if successful, -1 if the file was skipped, 1 on error. */ -int renameFile(std::string& path, const tm* tm); +int renameFile(fs::path& path, const tm* tm); /*! @brief Make a file path from the current file path, destination @@ -118,14 +110,14 @@ int renameFile(std::string& path, const tm* tm); @return 0 if successful, 1 if the new file exists and the user chose not to overwrite it. */ -std::string newFilePath(const std::string& path, const std::string& ext); +std::string newFilePath(const fs::path& path, const std::string& ext); /*! @brief Check if file \em path exists and whether it should be overwritten. Ask user if necessary. Return 1 if the file exists and shouldn't be overwritten, else 0. */ -int dontOverwrite(const std::string& path); +int dontOverwrite(const fs::path& path); /*! @brief Output a text with a given minimum number of chars, honoring @@ -137,7 +129,7 @@ int dontOverwrite(const std::string& path); std::ostream& operator<<(std::ostream& os, const std::pair& strAndWidth); //! Print image Structure information -int printStructure(std::ostream& out, Exiv2::PrintStructureOption option, const std::string& path); +int printStructure(std::ostream& out, Exiv2::PrintStructureOption option, const fs::path& path); } // namespace // ***************************************************************************** @@ -172,7 +164,7 @@ Task::UniquePtr TaskFactory::create(TaskType type) { return nullptr; } -static int setModeAndPrintStructure(Exiv2::PrintStructureOption option, const std::string& path, bool binary) { +static int setModeAndPrintStructure(Exiv2::PrintStructureOption option, const fs::path& path, bool binary) { int result = 0; if (binary && option == Exiv2::kpsIccProfile) { std::stringstream output(std::stringstream::out | std::stringstream::binary); @@ -201,7 +193,7 @@ static int setModeAndPrintStructure(Exiv2::PrintStructureOption option, const st return result; } -int Print::run(const std::string& path) { +int Print::run(const fs::path& path) { try { path_ = path; switch (Params::instance().printMode_) { @@ -224,17 +216,17 @@ int Print::run(const std::string& path) { } return 0; } catch (const Exiv2::Error& e) { - std::cerr << "Exiv2 exception in print action for file " << path << ":\n" << e << "\n"; + std::cerr << "Exiv2 exception in print action for file " << path.c_str() << ":\n" << e << "\n"; return 1; } catch (const std::overflow_error& e) { - std::cerr << "std::overflow_error exception in print action for file " << path << ":\n" << e.what() << "\n"; + std::cerr << "std::overflow_error exception in print action for file " << path.c_str() << ":\n" << e.what() << "\n"; return 1; } } int Print::printSummary() { if (!Exiv2::fileExists(path_)) { - std::cerr << path_ << ": " << _("Failed to open the file") << "\n"; + std::cerr << path_.c_str() << ": " << _("Failed to open the file") << "\n"; return -1; } @@ -245,7 +237,7 @@ int Print::printSummary() { // Filename printLabel(_("File name")); - std::cout << path_ << '\n'; + std::cout << path_.c_str() << '\n'; // Filesize printLabel(_("File size")); @@ -260,7 +252,7 @@ int Print::printSummary() { std::cout << image->pixelWidth() << " x " << image->pixelHeight() << '\n'; if (exifData.empty()) { - std::cerr << path_ << ": " << _("No Exif data found in the file") << "\n"; + std::cerr << path_.c_str() << ": " << _("No Exif data found in the file") << "\n"; return -3; } @@ -308,7 +300,7 @@ int Print::printSummary() { void Print::printLabel(const std::string& label) const { std::cout << std::setfill(' ') << std::left; if (Params::instance().files_.size() > 1) { - std::cout << std::setw(20) << path_ << " "; + std::cout << std::setw(20) << path_.c_str() << " "; } std::cout << std::pair(label, align_) << ": "; } @@ -353,7 +345,7 @@ int Print::printTag(const Exiv2::ExifData& exifData, EasyAccessFct easyAccessFct int Print::printList() { if (!Exiv2::fileExists(path_)) { - std::cerr << path_ << ": " << _("Failed to open the file") << "\n"; + std::cerr << path_.c_str() << ": " << _("Failed to open the file") << "\n"; return -1; } @@ -404,11 +396,11 @@ int Print::printMetadata(const Exiv2::Image* image) { // With -v, inform about the absence of any (requested) type of metadata if (Params::instance().verbose_) { if (noExif) - std::cerr << path_ << ": " << _("No Exif data found in the file") << "\n"; + std::cerr << path_.c_str() << ": " << _("No Exif data found in the file") << "\n"; if (noIptc) - std::cerr << path_ << ": " << _("No IPTC data found in the file") << "\n"; + std::cerr << path_.c_str() << ": " << _("No IPTC data found in the file") << "\n"; if (noXmp) - std::cerr << path_ << ": " << _("No XMP data found in the file") << "\n"; + std::cerr << path_.c_str() << ": " << _("No XMP data found in the file") << "\n"; } // With -g or -K, return -3 if no matching tags were found @@ -456,7 +448,7 @@ bool Print::printMetadatum(const Exiv2::Metadatum& md, const Exiv2::Image* pImag bool const manyFiles = Params::instance().files_.size() > 1; if (manyFiles) { - std::cout << std::setfill(' ') << std::left << std::setw(20) << path_ << " "; + std::cout << std::setfill(' ') << std::left << std::setw(20) << path_.c_str() << " "; } bool first = true; @@ -565,7 +557,7 @@ bool Print::printMetadatum(const Exiv2::Metadatum& md, const Exiv2::Image* pImag int Print::printComment() { if (!Exiv2::fileExists(path_)) { - std::cerr << path_ << ": " << _("Failed to open the file") << "\n"; + std::cerr << path_.c_str() << ": " << _("Failed to open the file") << "\n"; return -1; } @@ -580,7 +572,7 @@ int Print::printComment() { int Print::printPreviewList() { if (!Exiv2::fileExists(path_)) { - std::cerr << path_ << ": " << _("Failed to open the file") << "\n"; + std::cerr << path_.c_str() << ": " << _("Failed to open the file") << "\n"; return -1; } @@ -592,7 +584,7 @@ int Print::printPreviewList() { Exiv2::PreviewPropertiesList list = pm.getPreviewProperties(); for (const auto& pos : list) { if (manyFiles) { - std::cout << std::setfill(' ') << std::left << std::setw(20) << path_ << " "; + std::cout << std::setfill(' ') << std::left << std::setw(20) << path_.c_str() << " "; } std::cout << _("Preview") << " " << ++cnt << ": " << pos.mimeType_ << ", "; if (pos.width_ != 0 && pos.height_ != 0) { @@ -607,10 +599,10 @@ Task::UniquePtr Print::clone() const { return std::make_unique(*this); } -int Rename::run(const std::string& path) { +int Rename::run(const fs::path& path) { try { if (!Exiv2::fileExists(path)) { - std::cerr << path << ": " << _("Failed to open the file") << "\n"; + std::cerr << path.c_str() << ": " << _("Failed to open the file") << "\n"; return -1; } Timestamp ts; @@ -621,7 +613,7 @@ int Rename::run(const std::string& path) { image->readMetadata(); Exiv2::ExifData& exifData = image->exifData(); if (exifData.empty()) { - std::cerr << path << ": " << _("No Exif data found in the file") << "\n"; + std::cerr << path.c_str() << ": " << _("No Exif data found in the file") << "\n"; return -3; } auto md = exifData.findKey(Exiv2::ExifKey("Exif.Photo.DateTimeOriginal")); @@ -629,24 +621,25 @@ int Rename::run(const std::string& path) { md = exifData.findKey(Exiv2::ExifKey("Exif.Image.DateTime")); if (md == exifData.end()) { std::cerr << _("Neither tag") << " `Exif.Photo.DateTimeOriginal' " << _("nor") << " `Exif.Image.DateTime' " - << _("found in the file") << " " << path << "\n"; + << _("found in the file") << " " << path.c_str() << "\n"; return 1; } std::string v = md->toString(); if (v.empty() || v.front() == ' ') { - std::cerr << _("Image file creation timestamp not set in the file") << " " << path << "\n"; + std::cerr << _("Image file creation timestamp not set in the file") << " " << path.c_str() << "\n"; return 1; } tm tm; if (str2Tm(v, &tm) != 0) { - std::cerr << _("Failed to parse timestamp") << " `" << v << "' " << _("in the file") << " " << path << "\n"; + std::cerr << _("Failed to parse timestamp") << " `" << v << "' " << _("in the file") << " " << path.c_str() + << "\n"; return 1; } if (Params::instance().timestamp_ || Params::instance().timestampOnly_) { ts.read(&tm); } int rc = 0; - std::string newPath = path; + fs::path newPath = path; if (Params::instance().timestampOnly_) { if (Params::instance().verbose_) { std::cout << _("Updating timestamp to") << " " << v << '\n'; @@ -662,7 +655,7 @@ int Rename::run(const std::string& path) { } return rc; } catch (const Exiv2::Error& e) { - std::cerr << "Exiv2 exception in rename action for file " << path << ":\n" << e << "\n"; + std::cerr << "Exiv2 exception in rename action for file " << path.c_str() << ":\n" << e << "\n"; return 1; } } @@ -671,12 +664,12 @@ Task::UniquePtr Rename::clone() const { return std::make_unique(*this); } -int Erase::run(const std::string& path) { +int Erase::run(const fs::path& path) { try { path_ = path; if (!Exiv2::fileExists(path_)) { - std::cerr << path_ << ": " << _("Failed to open the file") << "\n"; + std::cerr << path_.c_str() << ": " << _("Failed to open the file") << "\n"; return -1; } Timestamp ts; @@ -717,7 +710,7 @@ int Erase::run(const std::string& path) { return rc; } catch (const Exiv2::Error& e) { - std::cerr << "Exiv2 exception in erase action for file " << path << ":\n" << e << "\n"; + std::cerr << "Exiv2 exception in erase action for file " << path.c_str() << ":\n" << e << "\n"; return 1; } } @@ -779,7 +772,7 @@ Task::UniquePtr Erase::clone() const { return std::make_unique(*this); } -int Extract::run(const std::string& path) { +int Extract::run(const fs::path& path) { try { path_ = path; int rc = 0; @@ -815,28 +808,28 @@ int Extract::run(const std::string& path) { } return rc; } catch (const Exiv2::Error& e) { - std::cerr << "Exiv2 exception in extract action for file " << path << ":\n" << e << "\n"; + std::cerr << "Exiv2 exception in extract action for file " << path.c_str() << ":\n" << e << "\n"; return 1; } } int Extract::writeThumbnail() const { if (!Exiv2::fileExists(path_)) { - std::cerr << path_ << ": " << _("Failed to open the file") << "\n"; + std::cerr << path_.c_str() << ": " << _("Failed to open the file") << "\n"; return -1; } auto image = Exiv2::ImageFactory::open(path_); image->readMetadata(); Exiv2::ExifData& exifData = image->exifData(); if (exifData.empty()) { - std::cerr << path_ << ": " << _("No Exif data found in the file") << "\n"; + std::cerr << path_.c_str() << ": " << _("No Exif data found in the file") << "\n"; return -3; } int rc = 0; Exiv2::ExifThumb exifThumb(exifData); std::string thumbExt = exifThumb.extension(); if (thumbExt.empty()) { - std::cerr << path_ << ": " << _("Image does not contain an Exif thumbnail") << "\n"; + std::cerr << path_.c_str() << ": " << _("Image does not contain an Exif thumbnail") << "\n"; } else { if ((Params::instance().target_ & Params::ctStdInOut) != 0) { Exiv2::DataBuf buf = exifThumb.copy(); @@ -857,7 +850,7 @@ int Extract::writeThumbnail() const { } rc = static_cast(exifThumb.writeFile(thumb)); if (rc == 0) { - std::cerr << path_ << ": " << _("Exif data doesn't contain a thumbnail") << "\n"; + std::cerr << path_.c_str() << ": " << _("Exif data doesn't contain a thumbnail") << "\n"; } } return rc; @@ -865,7 +858,7 @@ int Extract::writeThumbnail() const { int Extract::writePreviews() const { if (!Exiv2::fileExists(path_)) { - std::cerr << path_ << ": " << _("Failed to open the file") << "\n"; + std::cerr << path_.c_str() << ": " << _("Failed to open the file") << "\n"; return -1; } @@ -887,7 +880,7 @@ int Extract::writePreviews() const { } num--; if (num >= pvList.size()) { - std::cerr << path_ << ": " << _("Image does not have preview") << " " << num + 1 << "\n"; + std::cerr << path_.c_str() << ": " << _("Image does not have preview") << " " << num + 1 << "\n"; continue; } writePreviewFile(pvMgr.getPreviewImage(pvList[num]), num + 1); @@ -898,7 +891,7 @@ int Extract::writePreviews() const { int Extract::writeIccProfile(const std::string& target) const { int rc = 0; if (!Exiv2::fileExists(path_)) { - std::cerr << path_ << ": " << _("Failed to open the file") << "\n"; + std::cerr << path_.c_str() << ": " << _("Failed to open the file") << "\n"; rc = -1; } @@ -908,7 +901,7 @@ int Extract::writeIccProfile(const std::string& target) const { auto image = Exiv2::ImageFactory::open(path_); image->readMetadata(); if (!image->iccProfileDefined()) { - std::cerr << _("No embedded iccProfile: ") << path_ << '\n'; + std::cerr << _("No embedded iccProfile: ") << path_.c_str() << '\n'; rc = -2; } else { if (bStdout) { // -eC- @@ -941,7 +934,7 @@ void Extract::writePreviewFile(const Exiv2::PreviewImage& pvImg, size_t num) con } auto rc = pvImg.writeFile(pvFile); if (rc == 0) { - std::cerr << path_ << ": " << _("Image does not have preview") << " " << num << "\n"; + std::cerr << path_.c_str() << ": " << _("Image does not have preview") << " " << num << "\n"; } } @@ -949,12 +942,12 @@ Task::UniquePtr Extract::clone() const { return std::make_unique(*this); } -int Insert::run(const std::string& path) try { +int Insert::run(const fs::path& path) try { // -i{tgt}- reading from stdin? bool bStdin = (Params::instance().target_ & Params::ctStdInOut) != 0; if (!Exiv2::fileExists(path)) { - std::cerr << path << ": " << _("Failed to open the file") << "\n"; + std::cerr << path.c_str() << ": " << _("Failed to open the file") << "\n"; return -1; } @@ -993,11 +986,11 @@ int Insert::run(const std::string& path) try { ts.touch(path); return rc; } catch (const Exiv2::Error& e) { - std::cerr << "Exiv2 exception in insert action for file " << path << ":\n" << e << "\n"; + std::cerr << "Exiv2 exception in insert action for file " << path.c_str() << ":\n" << e << "\n"; return 1; } // Insert::run -int Insert::insertXmpPacket(const std::string& path, const std::string& xmpPath) { +int Insert::insertXmpPacket(const fs::path& path, const std::string& xmpPath) { int rc = 0; bool bStdin = xmpPath == "-"; if (bStdin) { @@ -1010,7 +1003,7 @@ int Insert::insertXmpPacket(const std::string& path, const std::string& xmpPath) rc = -1; } if (rc == 0 && !Exiv2::fileExists(path)) { - std::cerr << path << ": " << _("Failed to open the file") << "\n"; + std::cerr << path.c_str() << ": " << _("Failed to open the file") << "\n"; rc = -1; } if (rc == 0) { @@ -1022,7 +1015,7 @@ int Insert::insertXmpPacket(const std::string& path, const std::string& xmpPath) } // Insert::insertXmpPacket -int Insert::insertXmpPacket(const std::string& path, const Exiv2::DataBuf& xmpBlob, bool usePacket) { +int Insert::insertXmpPacket(const fs::path& path, const Exiv2::DataBuf& xmpBlob, bool usePacket) { std::string xmpPacket; for (size_t i = 0; i < xmpBlob.size(); i++) { xmpPacket += static_cast(xmpBlob.read_uint8(i)); @@ -1037,7 +1030,7 @@ int Insert::insertXmpPacket(const std::string& path, const Exiv2::DataBuf& xmpBl return 0; } -int Insert::insertIccProfile(const std::string& path, const std::string& iccPath) { +int Insert::insertIccProfile(const fs::path& path, const std::string& iccPath) { int rc = 0; // for path "foo.XXX", do a binary copy of "foo.icc" std::string iccProfilePath = newFilePath(path, ".icc"); @@ -1057,11 +1050,11 @@ int Insert::insertIccProfile(const std::string& path, const std::string& iccPath return rc; } // Insert::insertIccProfile -int Insert::insertIccProfile(const std::string& path, Exiv2::DataBuf&& iccProfileBlob) { +int Insert::insertIccProfile(const fs::path& path, Exiv2::DataBuf&& iccProfileBlob) { int rc = 0; // test path exists if (!Exiv2::fileExists(path)) { - std::cerr << path << ": " << _("Failed to open the file") << "\n"; + std::cerr << path.c_str() << ": " << _("Failed to open the file") << "\n"; rc = -1; } @@ -1080,14 +1073,14 @@ int Insert::insertIccProfile(const std::string& path, Exiv2::DataBuf&& iccProfil return rc; } // Insert::insertIccProfile -int Insert::insertThumbnail(const std::string& path) { +int Insert::insertThumbnail(const fs::path& path) { std::string thumbPath = newFilePath(path, "-thumb.jpg"); if (!Exiv2::fileExists(thumbPath)) { std::cerr << thumbPath << ": " << _("Failed to open the file") << "\n"; return -1; } if (!Exiv2::fileExists(path)) { - std::cerr << path << ": " << _("Failed to open the file") << "\n"; + std::cerr << path.c_str() << ": " << _("Failed to open the file") << "\n"; return -1; } auto image = Exiv2::ImageFactory::open(path); @@ -1103,10 +1096,10 @@ Task::UniquePtr Insert::clone() const { return std::make_unique(*this); } -int Modify::run(const std::string& path) { +int Modify::run(const fs::path& path) { try { if (!Exiv2::fileExists(path)) { - std::cerr << path << ": " << _("Failed to open the file") << "\n"; + std::cerr << path.c_str() << ": " << _("Failed to open the file") << "\n"; return -1; } Timestamp ts; @@ -1126,7 +1119,7 @@ int Modify::run(const std::string& path) { return rc; } catch (const Exiv2::Error& e) { - std::cerr << "Exiv2 exception in modify action for file " << path << ":\n" << e << "\n"; + std::cerr << "Exiv2 exception in modify action for file " << path.c_str() << ":\n" << e << "\n"; return 1; } } // Modify::run @@ -1308,14 +1301,14 @@ Task::UniquePtr Modify::clone() const { return std::make_unique(*this); } -int Adjust::run(const std::string& path) try { +int Adjust::run(const fs::path& path) try { adjustment_ = Params::instance().adjustment_; yearAdjustment_ = Params::instance().yodAdjust_[Params::yodYear].adjustment_; monthAdjustment_ = Params::instance().yodAdjust_[Params::yodMonth].adjustment_; dayAdjustment_ = Params::instance().yodAdjust_[Params::yodDay].adjustment_; if (!Exiv2::fileExists(path)) { - std::cerr << path << ": " << _("Failed to open the file") << "\n"; + std::cerr << path.c_str() << ": " << _("Failed to open the file") << "\n"; return -1; } Timestamp ts; @@ -1326,7 +1319,7 @@ int Adjust::run(const std::string& path) try { image->readMetadata(); Exiv2::ExifData& exifData = image->exifData(); if (exifData.empty()) { - std::cerr << path << ": " << _("No Exif data found in the file") << "\n"; + std::cerr << path.c_str() << ": " << _("No Exif data found in the file") << "\n"; return -3; } int rc = adjustDateTime(exifData, "Exif.Image.DateTime", path); @@ -1341,7 +1334,7 @@ int Adjust::run(const std::string& path) try { } return rc ? 1 : 0; } catch (const Exiv2::Error& e) { - std::cerr << "Exiv2 exception in adjust action for file " << path << ":\n" << e << "\n"; + std::cerr << "Exiv2 exception in adjust action for file " << path.c_str() << ":\n" << e << "\n"; return 1; } // Adjust::run @@ -1349,7 +1342,7 @@ Task::UniquePtr Adjust::clone() const { return std::make_unique(*this); } -int Adjust::adjustDateTime(Exiv2::ExifData& exifData, const std::string& key, const std::string& path) const { +int Adjust::adjustDateTime(Exiv2::ExifData& exifData, const std::string& key, const fs::path& path) const { Exiv2::ExifKey ek(key); auto md = exifData.findKey(ek); if (md == exifData.end()) { @@ -1358,7 +1351,8 @@ int Adjust::adjustDateTime(Exiv2::ExifData& exifData, const std::string& key, co } std::string timeStr = md->toString(); if (timeStr.empty() || timeStr[0] == ' ') { - std::cerr << path << ": " << _("Timestamp of metadatum with key") << " `" << ek << "' " << _("not set") << "\n"; + std::cerr << path.c_str() << ": " << _("Timestamp of metadatum with key") << " `" << ek << "' " << _("not set") + << "\n"; return 1; } if (Params::instance().verbose_) { @@ -1405,7 +1399,7 @@ int Adjust::adjustDateTime(Exiv2::ExifData& exifData, const std::string& key, co if (str2Tm(timeStr, &tm) != 0) { if (Params::instance().verbose_) std::cout << '\n'; - std::cerr << path << ": " << _("Failed to parse timestamp") << " `" << timeStr << "'\n"; + std::cerr << path.c_str() << ": " << _("Failed to parse timestamp") << " `" << timeStr << "'\n"; return 1; } @@ -1445,7 +1439,7 @@ int Adjust::adjustDateTime(Exiv2::ExifData& exifData, const std::string& key, co if (tm.tm_year > 9999 - 1900 || tm.tm_year < 1000 - 1900) { if (Params::instance().verbose_) std::cout << '\n'; - std::cerr << path << ": " << _("Can't adjust timestamp by") << " " << yearAdjustment + monOverflow << " " + std::cerr << path.c_str() << ": " << _("Can't adjust timestamp by") << " " << yearAdjustment + monOverflow << " " << _("years") << "\n"; return 1; } @@ -1459,10 +1453,10 @@ int Adjust::adjustDateTime(Exiv2::ExifData& exifData, const std::string& key, co return 0; } // Adjust::adjustDateTime -int FixIso::run(const std::string& path) { +int FixIso::run(const fs::path& path) { try { if (!Exiv2::fileExists(path)) { - std::cerr << path << ": " << _("Failed to open the file") << "\n"; + std::cerr << path.c_str() << ": " << _("Failed to open the file") << "\n"; return -1; } Timestamp ts; @@ -1473,7 +1467,7 @@ int FixIso::run(const std::string& path) { image->readMetadata(); Exiv2::ExifData& exifData = image->exifData(); if (exifData.empty()) { - std::cerr << path << ": " << _("No Exif data found in the file") << "\n"; + std::cerr << path.c_str() << ": " << _("No Exif data found in the file") << "\n"; return -3; } auto md = Exiv2::isoSpeed(exifData); @@ -1498,7 +1492,7 @@ int FixIso::run(const std::string& path) { return 0; } catch (const Exiv2::Error& e) { - std::cerr << "Exiv2 exception in fixiso action for file " << path << ":\n" << e << "\n"; + std::cerr << "Exiv2 exception in fixiso action for file " << path.c_str() << ":\n" << e << "\n"; return 1; } } // FixIso::run @@ -1507,10 +1501,10 @@ Task::UniquePtr FixIso::clone() const { return std::make_unique(*this); } -int FixCom::run(const std::string& path) { +int FixCom::run(const fs::path& path) { try { if (!Exiv2::fileExists(path)) { - std::cerr << path << ": " << _("Failed to open the file") << "\n"; + std::cerr << path.c_str() << ": " << _("Failed to open the file") << "\n"; return -1; } Timestamp ts; @@ -1521,7 +1515,7 @@ int FixCom::run(const std::string& path) { image->readMetadata(); Exiv2::ExifData& exifData = image->exifData(); if (exifData.empty()) { - std::cerr << path << ": " << _("No Exif data found in the file") << "\n"; + std::cerr << path.c_str() << ": " << _("No Exif data found in the file") << "\n"; return -3; } auto pos = exifData.findKey(Exiv2::ExifKey("Exif.Photo.UserComment")); @@ -1559,7 +1553,7 @@ int FixCom::run(const std::string& path) { return 0; } catch (const Exiv2::Error& e) { - std::cerr << "Exiv2 exception in fixcom action for file " << path << ":\n" << e << "\n"; + std::cerr << "Exiv2 exception in fixcom action for file " << path.c_str() << ":\n" << e << "\n"; return 1; } } // FixCom::run @@ -1668,7 +1662,7 @@ std::string tm2Str(const tm* tm) { return os.str(); } // tm2Str -std::string temporaryPath() { +fs::path temporaryPath() { static int count = 0; auto guard = std::scoped_lock(cs); @@ -1683,19 +1677,19 @@ std::string temporaryPath() { fs::remove(p); } - return p.string(); + return p; } -int metacopy(const std::string& source, const std::string& tgt, Exiv2::ImageType targetType, bool preserve) { +int metacopy(const fs::path& source, const fs::path& tgt, Exiv2::ImageType targetType, bool preserve) { #ifdef EXIV2_DEBUG_MESSAGES std::cerr << "actions.cpp::metacopy" - << " source = " << source << " target = " << tgt << '\n'; + << " source = " << source.c_str() << " target = " << tgt.c_str() << '\n'; #endif // read the source metadata int rc = -1; if (!Exiv2::fileExists(source)) { - std::cerr << source << ": " << _("Failed to open the file") << "\n"; + std::cerr << source.c_str() << ": " << _("Failed to open the file") << "\n"; return rc; } @@ -1718,7 +1712,7 @@ int metacopy(const std::string& source, const std::string& tgt, Exiv2::ImageType Action::Modify::applyCommands(sourceImage.get()); // Open or create the target file - std::string target(bStdout ? temporaryPath() : tgt); + fs::path target(bStdout ? temporaryPath() : tgt); std::unique_ptr targetImage; if (Exiv2::fileExists(target)) { @@ -1731,7 +1725,8 @@ int metacopy(const std::string& source, const std::string& tgt, Exiv2::ImageType // Copy each type of metadata if (Params::instance().target_ & Params::ctExif && !sourceImage->exifData().empty()) { if (Params::instance().verbose_ && !bStdout) { - std::cout << _("Writing Exif data from") << " " << source << " " << _("to") << " " << target << '\n'; + std::cout << _("Writing Exif data from") << " " << source.c_str() << " " << _("to") << " " << target.c_str() + << '\n'; } if (preserve) { for (const auto& exif : sourceImage->exifData()) { @@ -1743,7 +1738,8 @@ int metacopy(const std::string& source, const std::string& tgt, Exiv2::ImageType } if (Params::instance().target_ & Params::ctIptc && !sourceImage->iptcData().empty()) { if (Params::instance().verbose_ && !bStdout) { - std::cout << _("Writing IPTC data from") << " " << source << " " << _("to") << " " << target << '\n'; + std::cout << _("Writing IPTC data from") << " " << source.c_str() << " " << _("to") << " " << target.c_str() + << '\n'; } if (preserve) { for (const auto& iptc : sourceImage->iptcData()) { @@ -1755,7 +1751,8 @@ int metacopy(const std::string& source, const std::string& tgt, Exiv2::ImageType } if (Params::instance().target_ & (Params::ctXmp | Params::ctXmpRaw) && !sourceImage->xmpData().empty()) { if (Params::instance().verbose_ && !bStdout) { - std::cout << _("Writing XMP data from") << " " << source << " " << _("to") << " " << target << '\n'; + std::cout << _("Writing XMP data from") << " " << source.c_str() << " " << _("to") << " " << target.c_str() + << '\n'; } // #1148 use Raw XMP packet if there are no XMP modification commands @@ -1779,7 +1776,8 @@ int metacopy(const std::string& source, const std::string& tgt, Exiv2::ImageType } if (Params::instance().target_ & Params::ctComment && !sourceImage->comment().empty()) { if (Params::instance().verbose_ && !bStdout) { - std::cout << _("Writing JPEG comment from") << " " << source << " " << _("to") << " " << tgt << '\n'; + std::cout << _("Writing JPEG comment from") << " " << source.c_str() << " " << _("to") << " " << tgt.c_str() + << '\n'; } targetImage->setComment(sourceImage->comment()); } @@ -1788,7 +1786,7 @@ int metacopy(const std::string& source, const std::string& tgt, Exiv2::ImageType targetImage->writeMetadata(); rc = 0; } catch (const Exiv2::Error& e) { - std::cerr << tgt << ": " << _("Could not write metadata to file") << ": " << e << "\n"; + std::cerr << tgt.c_str() << ": " << _("Could not write metadata to file") << ": " << e << "\n"; rc = 1; } @@ -1819,9 +1817,9 @@ void replace(std::string& text, const std::string& searchText, const std::string } } -int renameFile(std::string& newPath, const tm* tm) { +int renameFile(fs::path& newPath, const tm* tm) { auto p = fs::path(newPath); - std::string path = newPath; + fs::path path = newPath; auto oldFsPath = fs::path(path); std::string format = Params::instance().format_; std::string filename = p.stem().string(); @@ -1837,7 +1835,7 @@ int renameFile(std::string& newPath, const tm* tm) { const size_t max = 1024; char basename[max] = {}; if (strftime(basename, max, format.c_str(), tm) == 0) { - std::cerr << _("Filename format yields empty filename for the file") << " " << path << "\n"; + std::cerr << _("Filename format yields empty filename for the file") << " " << path.c_str() << "\n"; return 1; } @@ -1891,7 +1889,7 @@ int renameFile(std::string& newPath, const tm* tm) { } if (Params::instance().verbose_) { - std::cout << _("Renaming file to") << " " << newPath; + std::cout << _("Renaming file to") << " " << newPath.c_str(); if (Params::instance().timestamp_) { std::cout << ", " << _("updating timestamp"); } @@ -1902,7 +1900,7 @@ int renameFile(std::string& newPath, const tm* tm) { return 0; } -std::string newFilePath(const std::string& path, const std::string& ext) { +std::string newFilePath(const fs::path& path, const std::string& ext) { auto p = fs::path(path); auto directory = fs::path(Params::instance().directory_); if (directory.empty()) @@ -1912,7 +1910,7 @@ std::string newFilePath(const std::string& path, const std::string& ext) { return (directory / (p.stem().string() + ext)).string(); } -int dontOverwrite(const std::string& path) { +int dontOverwrite(const fs::path& path) { if (path == "-") return 0; @@ -1936,9 +1934,9 @@ std::ostream& operator<<(std::ostream& os, const std::pair& st return os << std::setw(minChCount) << str; } -int printStructure(std::ostream& out, Exiv2::PrintStructureOption option, const std::string& path) { +int printStructure(std::ostream& out, Exiv2::PrintStructureOption option, const fs::path& path) { if (!Exiv2::fileExists(path)) { - std::cerr << path << ": " << _("Failed to open the file") << "\n"; + std::cerr << path.c_str() << ": " << _("Failed to open the file") << "\n"; return -1; } Exiv2::Image::UniquePtr image = Exiv2::ImageFactory::open(path); diff --git a/app/actions.hpp b/app/actions.hpp index cf31139645..0c947f74ea 100644 --- a/app/actions.hpp +++ b/app/actions.hpp @@ -16,6 +16,14 @@ #include +#if __has_include() +#include +namespace fs = std::filesystem; +#else +#include +namespace fs = std::experimental::filesystem; +#endif + // ***************************************************************************** // class declarations @@ -70,7 +78,7 @@ class Task { /// @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; + virtual int run(const fs::path& path) = 0; bool setBinary(bool b) { bool bResult = binary_; @@ -133,7 +141,7 @@ class TaskFactory { //! %Print the Exif (or other metadata) of a file to stdout class Print : public Task { public: - int run(const std::string& path) override; + int run(const fs::path& path) override; [[nodiscard]] Task::UniquePtr clone() const override; //! Print the Jpeg comment @@ -171,25 +179,25 @@ class Print : public Task { EasyAccessFct easyAccessFctFallback = nullptr) const; private: - std::string path_; + fs::path path_; int align_{0}; // for the alignment of the summary output }; /// @brief %Rename a file to its metadata creation timestamp, in the specified format. class Rename : public Task { public: - int run(const std::string& path) override; + int run(const fs::path& path) override; [[nodiscard]] Task::UniquePtr clone() const override; }; // class Rename //! %Adjust the Exif (or other metadata) timestamps class Adjust : public Task { public: - int run(const std::string& path) override; + int run(const fs::path& path) override; [[nodiscard]] Task::UniquePtr clone() const override; private: - int adjustDateTime(Exiv2::ExifData& exifData, const std::string& key, const std::string& path) const; + int adjustDateTime(Exiv2::ExifData& exifData, const std::string& key, const fs::path& path) const; int64_t adjustment_{0}; int64_t yearAdjustment_{0}; @@ -201,7 +209,7 @@ class Adjust : public Task { /// @brief %Erase the entire exif data or only the thumbnail section. class Erase : public Task { public: - int run(const std::string& path) override; + int run(const fs::path& path) override; [[nodiscard]] Task::UniquePtr clone() const override; /// @brief Delete the thumbnail image, incl IFD1 metadata from the file. @@ -223,13 +231,13 @@ class Erase : public Task { static int eraseIccProfile(Exiv2::Image* image); private: - std::string path_; + fs::path path_; }; /// @brief %Extract the entire exif data or only the thumbnail section. class Extract : public Task { public: - int run(const std::string& path) override; + int run(const fs::path& path) override; [[nodiscard]] Task::UniquePtr clone() const override; /*! @@ -252,13 +260,13 @@ class Extract : public Task { [[nodiscard]] int writeIccProfile(const std::string& target) const; private: - std::string path_; + fs::path path_; }; /// @brief %Insert the Exif data from corresponding *.exv files. class Insert : public Task { public: - int run(const std::string& path) override; + int run(const fs::path& path) override; [[nodiscard]] Task::UniquePtr clone() const override; /*! @@ -266,25 +274,25 @@ class Insert : public Task { The filename of the thumbnail is expected to be the image filename (\em path) minus its suffix plus "-thumb.jpg". */ - static int insertThumbnail(const std::string& path); + static int insertThumbnail(const fs::path& path); /// @brief Insert an XMP packet from a xmpPath into file \em path. - static int insertXmpPacket(const std::string& path, const std::string& xmpPath); + static int insertXmpPacket(const fs::path& path, const std::string& xmpPath); /// @brief Insert xmp from a DataBuf into file \em path. - static int insertXmpPacket(const std::string& path, const Exiv2::DataBuf& xmpBlob, bool usePacket = false); + static int insertXmpPacket(const fs::path& path, const Exiv2::DataBuf& xmpBlob, bool usePacket = false); /// @brief Insert an ICC profile from iccPath into file \em path. - static int insertIccProfile(const std::string& path, const std::string& iccPath); + static int insertIccProfile(const fs::path& path, const std::string& iccPath); /// @brief Insert an ICC profile from binary DataBuf into file \em path. - static int insertIccProfile(const std::string& path, Exiv2::DataBuf&& iccProfileBlob); + static int insertIccProfile(const fs::path& path, Exiv2::DataBuf&& iccProfileBlob); }; /// @brief %Modify the Exif data according to the commands in the modification table. class Modify : public Task { public: - int run(const std::string& path) override; + int run(const fs::path& path) override; [[nodiscard]] Task::UniquePtr clone() const override; //! Apply modification commands to the \em pImage, return 0 if successful. static int applyCommands(Exiv2::Image* pImage); @@ -303,11 +311,11 @@ class Modify : public Task { /// @brief %Copy ISO settings from any of the Nikon makernotes to the regular Exif tag, Exif.Photo.ISOSpeedRatings. class FixIso : public Task { public: - int run(const std::string& path) override; + int run(const fs::path& path) override; [[nodiscard]] Task::UniquePtr clone() const override; private: - std::string path_; + fs::path path_; }; /// @brief Fix the character encoding of Exif UNICODE user comments. @@ -315,11 +323,11 @@ class FixIso : public Task { /// Decodes the comment using the auto-detected or specified character encoding and writes it back in UCS-2. class FixCom : public Task { public: - int run(const std::string& path) override; + int run(const fs::path& path) override; [[nodiscard]] Task::UniquePtr clone() const override; private: - std::string path_; + fs::path path_; }; } // namespace Action diff --git a/include/exiv2/basicio.hpp b/include/exiv2/basicio.hpp index 485d0b913a..e7a8ca1e5f 100644 --- a/include/exiv2/basicio.hpp +++ b/include/exiv2/basicio.hpp @@ -292,8 +292,14 @@ class EXIV2API FileIo : public BasicIo { @param path The full path of a file */ explicit FileIo(const std::string& path); + #ifdef _WIN32 - explicit FileIo(const std::wstring& path); + /*! + @brief Like FileIo(const std::string& path) but accepts a + unicode path in an std::wstring. + @note This constructor is only available on Windows. + */ + explicit FileIo(const std::wstring& wpath); #endif //! Destructor. Flushes and closes an open file. @@ -680,6 +686,9 @@ class EXIV2API XPathIo : public MemIo { //@{ //! Default constructor XPathIo(const std::string& path); +#ifdef _WIN32 + XPathIo(const std::wstring& path); +#endif //@} private: /*! @@ -713,6 +722,15 @@ class EXIV2API XPathIo : public FileIo { //! Default constructor that reads data from stdin/data uri path and writes them to the temp file. explicit XPathIo(const std::string& orgPath); +#ifdef _WIN32 + /*! + @brief Like XPathIo(const std::string& path) but accepts a + unicode url in an std::wstring. + @note This constructor is only available on Windows. + */ + explicit XPathIo(const std::wstring& wpath); +#endif + //! Destructor. Releases all managed memory and removes the temp file. ~XPathIo() override; //@} @@ -739,6 +757,14 @@ class EXIV2API XPathIo : public FileIo { @throw Error if it fails. */ static std::string writeDataToFile(const std::string& orgPath); +#ifdef _WIN32 + /*! + @brief Like writeDataToFile(const std::string& orgPath) but accepts a + unicode url in an std::wstring. + @note This constructor is only available on Windows. + */ + static std::string writeDataToFile(const std::wstring& wOrgPath); +#endif //@} private: @@ -932,6 +958,15 @@ class EXIV2API HttpIo : public RemoteIo { */ explicit HttpIo(const std::string& url, size_t blockSize = 1024); +#ifdef _WIN32 + /*! + @brief Like HttpIo(const std::string& url, size_t blockSize = 1024) but accepts a + unicode url in an std::wstring. + @note This constructor is only available on Windows. + */ + explicit HttpIo(const std::wstring& wurl, size_t blockSize = 1024); +#endif + private: // Pimpl idiom class HttpImpl; @@ -956,6 +991,14 @@ class EXIV2API CurlIo : public RemoteIo { @throw Error if it is unable to init curl pointer. */ explicit CurlIo(const std::string& url, size_t blockSize = 0); +#ifdef _WIN32 + /*! + @brief Like CurlIo(const std::string& url, size_t blockSize = 0) but accepts a + unicode url in an std::wstring. + @note This constructor is only available on Windows. + */ + explicit CurlIo(const std::wstring& wurl, size_t blockSize = 0); +#endif /*! @brief Write access is only available for some protocols. This method diff --git a/include/exiv2/futils.hpp b/include/exiv2/futils.hpp index 46a29289ca..f682922810 100644 --- a/include/exiv2/futils.hpp +++ b/include/exiv2/futils.hpp @@ -70,6 +70,14 @@ EXIV2API size_t base64decode(const char* in, char* out, size_t out_size); */ EXIV2API Protocol fileProtocol(const std::string& path); +#ifdef _WIN32 +/*! + @brief Like fileProtocol() but accepts a unicode path in an std::wstring. + @note This function is only available on Windows. +*/ +EXIV2API Protocol fileProtocol(const std::wstring& path); +#endif + /*! @brief Test if a file exists. @@ -83,6 +91,15 @@ EXIV2API Protocol fileProtocol(const std::string& path); */ EXIV2API bool fileExists(const std::string& path); +#ifdef _WIN32 +/*! + @brief Like fileExists(const std::string& path) but + accepts a unicode path in an std::wstring. + @note This function is only available on Windows. +*/ +EXIV2API bool fileExists(const std::wstring& path); +#endif + /*! @brief Return a system error message and the error code (errno). See %strerror(3). diff --git a/include/exiv2/image.hpp b/include/exiv2/image.hpp index 273a42d153..0939e48a5c 100644 --- a/include/exiv2/image.hpp +++ b/include/exiv2/image.hpp @@ -522,9 +522,15 @@ class EXIV2API ImageFactory { read the remote file. */ static BasicIo::UniquePtr createIo(const std::string& path, bool useCurl = true); + #ifdef _WIN32 - static BasicIo::UniquePtr createIo(const std::wstring& path); + /*! + @brief Like createIo() but accepts a unicode path in an std::wstring. + @note This function is only available on Windows. + */ + static BasicIo::UniquePtr createIo(const std::wstring& wpath, bool useCurl = true); #endif + /*! @brief Create an Image subclass of the appropriate type by reading the specified file. %Image type is derived from the file @@ -539,9 +545,15 @@ class EXIV2API ImageFactory { unknown image type. */ static Image::UniquePtr open(const std::string& path, bool useCurl = true); + #ifdef _WIN32 - static Image::UniquePtr open(const std::wstring& path); + /*! + @brief Like open() but accepts a unicode path in an std::wstring. + @note This function is only available on Windows. + */ + static Image::UniquePtr open(const std::wstring& wpath, bool useCurl = true); #endif + /*! @brief Create an Image subclass of the appropriate type by reading the provided memory. %Image type is derived from the memory @@ -582,6 +594,13 @@ class EXIV2API ImageFactory { @throw Error If the image type is not supported. */ static Image::UniquePtr create(ImageType type, const std::string& path); +#ifdef _WIN32 + /*! + @brief Like create() but accepts a unicode path in an std::wstring. + @note This function is only available on Windows. + */ + static Image::UniquePtr create(ImageType type, const std::wstring& wpath); +#endif /*! @brief Create an Image subclass of the requested type by creating a new image in memory. diff --git a/src/basicio.cpp b/src/basicio.cpp index 3bbcded984..3e4dc65439 100644 --- a/src/basicio.cpp +++ b/src/basicio.cpp @@ -122,6 +122,7 @@ FileIo::Impl::Impl(std::string path) : path_(std::move(path)) { wpath_.assign(t, nw); #endif } + #ifdef _WIN32 FileIo::Impl::Impl(std::wstring path) : wpath_(std::move(path)) { char t[1024]; @@ -209,8 +210,9 @@ int FileIo::Impl::stat(StructStat& buf) const { FileIo::FileIo(const std::string& path) : p_(std::make_unique(path)) { } + #ifdef _WIN32 -FileIo::FileIo(const std::wstring& path) : p_(std::make_unique(path)) { +FileIo::FileIo(const std::wstring& wpath) : p_(std::make_unique(wpath)) { } #endif @@ -907,6 +909,18 @@ XPathIo::XPathIo(const std::string& path) { ReadDataUri(path); } +#ifdef _WIN32 +XPathIo::XPathIo(const std::wstring& wpath) { + std::string path; + path.assign(wpath.begin(), wpath.end()); + Protocol prot = fileProtocol(path); + if (prot == pStdin) + ReadStdin(); + else if (prot == pDataUri) + ReadDataUri(path); +} +#endif + void XPathIo::ReadStdin() { if (isatty(fileno(stdin))) throw Error(ErrorCode::kerInputDataReadFailed); @@ -947,6 +961,11 @@ void XPathIo::ReadDataUri(const std::string& path) { XPathIo::XPathIo(const std::string& orgPath) : FileIo(XPathIo::writeDataToFile(orgPath)), tempFilePath_(path()) { } +#ifdef _WIN32 +XPathIo::XPathIo(const std::wstring& worgPath) : FileIo(XPathIo::writeDataToFile(worgPath)), tempFilePath_(path()) { +} +#endif + XPathIo::~XPathIo() { if (isTemp_ && !fs::remove(tempFilePath_)) { // error when removing file @@ -1030,6 +1049,14 @@ std::string XPathIo::writeDataToFile(const std::string& orgPath) { return path; } +#ifdef _WIN32 +std::string XPathIo::writeDataToFile(const std::wstring& wOrgPath) { + std::string orgPath; + orgPath.assign(wOrgPath.begin(), wOrgPath.end()); + return XPathIo::writeDataToFile(orgPath); +} +#endif + #endif //! Internal Pimpl abstract structure of class RemoteIo. @@ -1037,6 +1064,9 @@ class RemoteIo::Impl { public: //! Constructor Impl(const std::string& url, size_t blockSize); +#ifdef _WIN32 + Impl(const std::wstring& wpath, size_t blockSize); +#endif //! Destructor. Releases all managed memory. virtual ~Impl(); @@ -1045,6 +1075,9 @@ class RemoteIo::Impl { // DATA std::string path_; //!< (Standard) path +#ifdef _WIN32 + std::wstring wpath_; //!< Unicode path +#endif size_t blockSize_; //!< Size of the block memory. BlockMap* blocksMap_{nullptr}; //!< An array contains all blocksMap size_t size_{0}; //!< The file size @@ -1096,6 +1129,12 @@ RemoteIo::Impl::Impl(const std::string& url, size_t blockSize) : path_(url), blockSize_(blockSize), protocol_(fileProtocol(url)) { } +#ifdef _WIN32 +RemoteIo::Impl::Impl(const std::wstring& wurl, size_t blockSize) : + wpath_(wurl), blockSize_(blockSize), protocol_(fileProtocol(wurl)) { +} +#endif + size_t RemoteIo::Impl::populateBlocks(size_t lowBlock, size_t highBlock) { // optimize: ignore all true blocks on left & right sides. while (!blocksMap_[lowBlock].isNone() && lowBlock < highBlock) @@ -1422,6 +1461,10 @@ class HttpIo::HttpImpl : public Impl { public: //! Constructor HttpImpl(const std::string& url, size_t blockSize); +#ifdef _WIN32 + //! Constructor accepting a unicode path in an std::wstring + HttpImpl(const std::wstring& wpath, size_t blockSize); +#endif Exiv2::Uri hostInfo_; //!< the host information extracted from the path // METHODS @@ -1461,6 +1504,16 @@ HttpIo::HttpImpl::HttpImpl(const std::string& url, size_t blockSize) : Impl(url, Exiv2::Uri::Decode(hostInfo_); } +#ifdef _WIN32 +HttpIo::HttpImpl::HttpImpl(const std::wstring& wurl, size_t blockSize) : Impl(wurl, blockSize) { + std::string url; + url.assign(wurl.begin(), wurl.end()); + path_ = url; + hostInfo_ = Exiv2::Uri::Parse(url); + Exiv2::Uri::Decode(hostInfo_); +} +#endif + int64_t HttpIo::HttpImpl::getFileLength() { Exiv2::Dictionary response; Exiv2::Dictionary request; @@ -1549,6 +1602,12 @@ void HttpIo::HttpImpl::writeRemote(const byte* data, size_t size, size_t from, s HttpIo::HttpIo(const std::string& url, size_t blockSize) { p_ = std::make_unique(url, blockSize); } + +#ifdef _WIN32 +HttpIo::HttpIo(const std::wstring& url, size_t blockSize) { + p_ = std::make_unique(url, blockSize); +} +#endif #endif #ifdef EXV_USE_CURL @@ -1557,6 +1616,10 @@ class CurlIo::CurlImpl : public Impl { public: //! Constructor CurlImpl(const std::string& url, size_t blockSize); +#ifdef _WIN32 + //! Constructor accepting a unicode path in an std::wstring + CurlImpl(const std::wstring& wpath, size_t blockSize); +#endif //! Destructor. Cleans up the curl pointer and releases all managed memory. ~CurlImpl() override; @@ -1620,6 +1683,28 @@ CurlIo::CurlImpl::CurlImpl(const std::string& url, size_t blockSize) : Impl(url, } } +#ifdef _WIN32 +CurlIo::CurlImpl::CurlImpl(const std::wstring& wurl, size_t blockSize) : + Impl(wurl, blockSize), curl_(curl_easy_init()) { + std::string url; + url.assign(wurl.begin(), wurl.end()); + path_ = url; + + // init curl pointer + curl_ = curl_easy_init(); + if (!curl_) { + throw Error(ErrorCode::kerErrorMessage, "Unable to init libcurl."); + } + + // The default block size for FTP is much larger than other protocols + // the reason is that getDataByRange() in FTP always creates the new connection, + // so we need the large block size to reduce the overhead of creating the connection. + if (blockSize_ == 0) { + blockSize_ = protocol_ == pFtp ? 102400 : 1024; + } +} +#endif + int64_t CurlIo::CurlImpl::getFileLength() { curl_easy_reset(curl_); // reset all options std::string response; @@ -1741,6 +1826,12 @@ CurlIo::CurlIo(const std::string& url, size_t blockSize) { p_ = std::make_unique(url, blockSize); } +#ifdef _WIN32 +CurlIo::CurlIo(const std::wstring& wurl, size_t blockSize) { + p_ = std::make_unique(wurl, blockSize); +} +#endif + #endif // ************************************************************************* diff --git a/src/futils.cpp b/src/futils.cpp index b907c4583d..cf002575f4 100644 --- a/src/futils.cpp +++ b/src/futils.cpp @@ -230,6 +230,30 @@ Protocol fileProtocol(const std::string& path) { return result; } // fileProtocol +#ifdef _WIN32 +Protocol fileProtocol(const std::wstring& path) { + Protocol result = pFile; + struct { + std::wstring name; + Protocol prot; + bool isUrl; // path.size() > name.size() + } prots[] = { + {L"http://", pHttp, true}, {L"https://", pHttps, true}, {L"ftp://", pFtp, true}, {L"sftp://", pSftp, true}, + {L"file://", pFileUri, true}, {L"data://", pDataUri, true}, {L"-", pStdin, false}, + }; + for (const auto& prot : prots) { + if (result != pFile) + break; + + if (path.rfind(prot.name, 0) == 0) + // URL's require data. Stdin == "-" and no further data + if (prot.isUrl ? path.size() > prot.name.size() : path.size() == prot.name.size()) + result = prot.prot; + } + return result; +} +#endif + bool fileExists(const std::string& path) { if (fileProtocol(path) != pFile) { return true; @@ -241,6 +265,19 @@ bool fileExists(const std::string& path) { #endif } +#ifdef _WIN32 +bool fileExists(const std::wstring& wpath) { + if (fileProtocol(wpath) != pFile) { + return true; + } +#ifdef EXV_ENABLE_FILESYSTEM + return fs::exists(wpath); +#else + return false; +#endif +} +#endif + std::string strError() { int error = errno; std::ostringstream os; diff --git a/src/image.cpp b/src/image.cpp index 7224d14389..9091a423a2 100644 --- a/src/image.cpp +++ b/src/image.cpp @@ -130,6 +130,14 @@ std::string pathOfFileUrl(const std::string& url) { size_t found = path.find('/'); return (found == std::string::npos) ? path : path.substr(found); } + +#ifdef _WIN32 +std::wstring pathOfFileUrl(const std::wstring& url) { + std::wstring path = url.substr(7); + size_t found = path.find('/'); + return (found == std::wstring::npos) ? path : path.substr(found); +} +#endif #endif } // namespace @@ -835,11 +843,28 @@ BasicIo::UniquePtr ImageFactory::createIo(const std::string& path, [[maybe_unuse } // ImageFactory::createIo #ifdef _WIN32 -BasicIo::UniquePtr ImageFactory::createIo(const std::wstring& path) { +BasicIo::UniquePtr ImageFactory::createIo(const std::wstring& wpath, [[maybe_unused]] bool useCurl) { + Protocol fProt = fileProtocol(wpath); + +#ifdef EXV_USE_CURL + if (useCurl && (fProt == pHttp || fProt == pHttps || fProt == pFtp)) { + return std::make_unique(wpath); // may throw + } +#endif + +#ifdef EXV_ENABLE_WEBREADY + if (fProt == pHttp) + return std::make_unique(wpath); // may throw +#endif #ifdef EXV_ENABLE_FILESYSTEM - return std::make_unique(path); + if (fProt == pFileUri) + return std::make_unique(pathOfFileUrl(wpath)); + if (fProt == pStdin || fProt == pDataUri) + return std::make_unique(wpath); // may throw + + return std::make_unique(wpath); #else - return nullptr; + throw Error(ErrorCode::kerFileAccessDisabled, wpath); #endif } #endif @@ -852,8 +877,8 @@ Image::UniquePtr ImageFactory::open(const std::string& path, bool useCurl) { } #ifdef _WIN32 -Image::UniquePtr ImageFactory::open(const std::wstring& path) { - auto image = open(ImageFactory::createIo(path)); // may throw +Image::UniquePtr ImageFactory::open(const std::wstring& path, bool useCurl) { + auto image = open(ImageFactory::createIo(path, useCurl)); // may throw if (!image) { char t[1024]; WideCharToMultiByte(CP_UTF8, 0, path.c_str(), -1, t, 1024, nullptr, nullptr); @@ -897,6 +922,24 @@ Image::UniquePtr ImageFactory::create(ImageType type, const std::string& path) { throw Error(ErrorCode::kerUnsupportedImageType, static_cast(type)); return image; } + +#ifdef _WIN32 +Image::UniquePtr ImageFactory::create(ImageType type, const std::wstring& wpath) { + auto fileIo = std::make_unique(wpath); + // Create or overwrite the file, then close it + if (fileIo->open("w+b") != 0) { + // throw WError(ErrorCode::kerFileOpenFailed, wpath, "w+b", strError()); + throw; + } + fileIo->close(); + + BasicIo::UniquePtr io(std::move(fileIo)); + auto image = create(type, std::move(io)); + if (!image) + throw Error(ErrorCode::kerUnsupportedImageType, static_cast(type)); + return image; +} +#endif #endif Image::UniquePtr ImageFactory::create(ImageType type) {