From 7844b11e4b998924e1bcf3c8760c94abbf69b8d9 Mon Sep 17 00:00:00 2001 From: Robin Mills Date: Thu, 1 Aug 2019 17:36:10 +0100 Subject: [PATCH] [xmpsdk] Add XMP Initialisation/Cleanup code to all main() programs - update documentation - Removed redundant calls to Exiv2::XmpParser::terminate() (legacy of our discusssion with @tester0077). - this fixes #976 --- README.md | 37 +++++++++++++++++++++++++++++++++++- samples/conntest.cpp | 3 +++ samples/convert-test.cpp | 3 +++ samples/easyaccess-test.cpp | 3 +++ samples/exifcomment.cpp | 2 ++ samples/exifdata-test.cpp | 3 +++ samples/exifdata.cpp | 3 +++ samples/exifprint.cpp | 3 +++ samples/exifvalue.cpp | 3 +++ samples/exiv2json.cpp | 3 +++ samples/geotag.cpp | 3 +++ samples/getopt-test.cpp | 3 +++ samples/httptest.cpp | 3 +++ samples/ini-test.cpp | 9 +++++---- samples/iotest.cpp | 5 ++++- samples/iptceasy.cpp | 3 +++ samples/iptcprint.cpp | 3 +++ samples/iptctest.cpp | 3 +++ samples/key-test.cpp | 3 +++ samples/largeiptc-test.cpp | 3 +++ samples/metacopy.cpp | 3 +++ samples/mmap-test.cpp | 3 +++ samples/mrwthumb.cpp | 3 +++ samples/mt-test.cpp | 3 +++ samples/path-test.cpp | 3 +++ samples/prevtest.cpp | 3 +++ samples/remotetest.cpp | 3 +++ samples/stringto-test.cpp | 3 +++ samples/taglist.cpp | 3 +++ samples/tiff-test.cpp | 3 +++ samples/tiffaddpath-test.cpp | 3 +++ samples/toexv.cpp | 5 ++++- samples/werror-test.cpp | 3 +++ samples/write-test.cpp | 5 ++++- samples/write2-test.cpp | 5 ++++- samples/xmpdump.cpp | 3 +++ samples/xmpparse.cpp | 3 +++ samples/xmpparser-test.cpp | 3 +++ samples/xmpprint.cpp | 5 ++++- samples/xmpsample.cpp | 3 +++ src/exiv2.cpp | 3 +++ 41 files changed, 162 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index a3e1960e..5a2dfe18 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,8 @@ 11. [Debugging Exiv2](#2-11) 12. [Building Exiv2 with Clang and other build chains](#2-12) 13. [Building Exiv2 with ccache](#2-13) + 14. [Thread Safety](#2-14) + 15. [Library Initialisation and Cleanup](#2-15) 3. [License and Support](#3) 1. [License](#3-1) 2. [Support](#3-2) @@ -553,6 +555,39 @@ $ make Due to the way in which ccache is installed in Fedora (and other Linux distros), ccache effectively replaces the compiler. A default build or **-DBUILD\_WITH\_CCACHE=Off** is not effective and the environment variable CCACHE_DISABLE is required to disable ccache. [https://github.com/Exiv2/exiv2/issues/361](https://github.com/Exiv2/exiv2/issues/361) [TOC](#TOC) + +
+ +### 2.14 Thread Safety + +Exiv2 heavily relies on standard C++ containers. Static or global variables are used read-only, with the exception of the XMP namespace registration function (see below). Thus Exiv2 is thread safe in the same sense as C++ containers: +Different instances of the same class can safely be used concurrently in multiple threads. + +In order to use the same instance of a class concurrently in multiple threads the application must serialize all write access to the object. + +The level of thread safety within Exiv2 varies depending on the type of metadata: The Exif and IPTC code is reentrant. The XMP code uses the Adobe XMP toolkit (XMP SDK), which according to its documentation is thread-safe. It actually uses mutexes to serialize critical sections. However, the XMP SDK initialisation function is not mutex protected, thus Exiv2::XmpParser::initialize is not thread-safe. In addition, Exiv2::XmpProperties::registerNs writes to a static class variable, and is also not thread-safe. + +Therefore, multi-threaded applications need to ensure that these two XMP functions are serialized, e.g., by calling them from an initialization section which is run before any threads are started. + +[TOC](#TOC) + +
+ +### 2.15 Library Initialisation and Cleanup + +As discussed in the section on Thread Safety, Exiv2 classes for Exif and IPTC metadata are fully reentrant and require no initialisation or cleanup. + +Adobe's XMPsdk is generally thread-safe, however it has to be initialized and terminated before and after starting any threads to access XMP metadata. The Exiv2 library will initialize this if necessary, however it does not terminate the XMPsdk. + +The Exiv2 command-line and the sample applications call the following at the outset: + +``` + Exiv2::XmpParser::initialize(); + ::atexit(Exiv2::XmpParser::terminate); +``` + +[TOC](#TOC) +
## 3 License and Support @@ -895,4 +930,4 @@ Work in progress: [https://github.com/Exiv2/exiv2/issues/902](https://github.co Robin Mills -Revised: 2019-07-29 +Revised: 2019-08-03 diff --git a/samples/conntest.cpp b/samples/conntest.cpp index c7a8ee80..f67b5c1c 100644 --- a/samples/conntest.cpp +++ b/samples/conntest.cpp @@ -114,6 +114,9 @@ void sftpcon(const std::string& url) { int main(int argc,const char** argv) { + Exiv2::XmpParser::initialize(); + ::atexit(Exiv2::XmpParser::terminate); + if (argc < 2) { std::cout << "Usage: " << argv[0] << " url {-http1_0}" << std::endl; return 1; diff --git a/samples/convert-test.cpp b/samples/convert-test.cpp index 2c5723d3..30b05cba 100644 --- a/samples/convert-test.cpp +++ b/samples/convert-test.cpp @@ -10,6 +10,9 @@ int main(int argc, char* const argv[]) try { + Exiv2::XmpParser::initialize(); + ::atexit(Exiv2::XmpParser::terminate); + if (argc != 2) { std::cout << "Usage: " << argv[0] << " file\n"; return 1; diff --git a/samples/easyaccess-test.cpp b/samples/easyaccess-test.cpp index 7e0b2220..a6bf8cfc 100644 --- a/samples/easyaccess-test.cpp +++ b/samples/easyaccess-test.cpp @@ -43,6 +43,9 @@ static const EasyAccess easyAccess[] = { int main(int argc, char **argv) try { + Exiv2::XmpParser::initialize(); + ::atexit(Exiv2::XmpParser::terminate); + if (argc != 2) { std::cout << "Usage: " << argv[0] << " file\n"; return 1; diff --git a/samples/exifcomment.cpp b/samples/exifcomment.cpp index 7eede6f9..e395560c 100644 --- a/samples/exifcomment.cpp +++ b/samples/exifcomment.cpp @@ -19,6 +19,8 @@ // Main int main(int argc, char* const argv[]) try { + Exiv2::XmpParser::initialize(); + ::atexit(Exiv2::XmpParser::terminate); if (argc != 2) { std::cout << "Usage: " << argv[0] << " file\n"; diff --git a/samples/exifdata-test.cpp b/samples/exifdata-test.cpp index 268cc682..a631630f 100644 --- a/samples/exifdata-test.cpp +++ b/samples/exifdata-test.cpp @@ -24,6 +24,9 @@ void print(const std::string& file); int main(int argc, char* const argv[]) { try { + Exiv2::XmpParser::initialize(); + ::atexit(Exiv2::XmpParser::terminate); + if (argc != 2) { std::cout << "Usage: " << argv[0] << " file\n"; return 1; diff --git a/samples/exifdata.cpp b/samples/exifdata.cpp index 0c18d5b8..fb038679 100644 --- a/samples/exifdata.cpp +++ b/samples/exifdata.cpp @@ -152,6 +152,9 @@ std::string formatXML(Exiv2::ExifData& exifData) /////////////////////////////////////////////////////////////////////// int main(int argc,const char* argv[]) { + Exiv2::XmpParser::initialize(); + ::atexit(Exiv2::XmpParser::terminate); + format_t formats; formats["wolf"] = wolf; formats["csv" ] = csv ; diff --git a/samples/exifprint.cpp b/samples/exifprint.cpp index 5f4d5da1..113e4bb6 100644 --- a/samples/exifprint.cpp +++ b/samples/exifprint.cpp @@ -29,6 +29,9 @@ int _tmain(int argc, _tchar* const argv[]) try { + Exiv2::XmpParser::initialize(); + ::atexit(Exiv2::XmpParser::terminate); + const _tchar* prog = argv[0]; const _tchar* file = argv[1]; diff --git a/samples/exifvalue.cpp b/samples/exifvalue.cpp index 672772f2..000cbb4a 100644 --- a/samples/exifvalue.cpp +++ b/samples/exifvalue.cpp @@ -11,6 +11,9 @@ int main(int argc, char* const argv[]) { + Exiv2::XmpParser::initialize(); + ::atexit(Exiv2::XmpParser::terminate); + if (argc != 3) { std::cerr << "Usage: " << argv[0] << " file key\n"; return 1; diff --git a/samples/exiv2json.cpp b/samples/exiv2json.cpp index a33a383b..4f43280d 100644 --- a/samples/exiv2json.cpp +++ b/samples/exiv2json.cpp @@ -268,6 +268,9 @@ void fileSystemPush(const char* path,Jzon::Node& nfs) int main(int argc, char* const argv[]) { + Exiv2::XmpParser::initialize(); + ::atexit(Exiv2::XmpParser::terminate); + try { if (argc < 2 || argc > 3) { std::cout << "Usage: " << argv[0] << " [-option] file" << std::endl; diff --git a/samples/geotag.cpp b/samples/geotag.cpp index 666f5aeb..965b9600 100644 --- a/samples/geotag.cpp +++ b/samples/geotag.cpp @@ -764,6 +764,9 @@ bool mySort(const std::string& a, const std::string& b) int main(int argc,const char* argv[]) { + Exiv2::XmpParser::initialize(); + ::atexit(Exiv2::XmpParser::terminate); + int result=0; const char* program = argv[0]; diff --git a/samples/getopt-test.cpp b/samples/getopt-test.cpp index 96a82b4a..5b38a10b 100644 --- a/samples/getopt-test.cpp +++ b/samples/getopt-test.cpp @@ -83,6 +83,9 @@ public: int main(int argc, char** const argv) { + Exiv2::XmpParser::initialize(); + ::atexit(Exiv2::XmpParser::terminate); + int n; #ifdef EXV_HAVE_UNISTD_H diff --git a/samples/httptest.cpp b/samples/httptest.cpp index d8d267a9..3e325e75 100644 --- a/samples/httptest.cpp +++ b/samples/httptest.cpp @@ -20,6 +20,9 @@ static int testSyntax(const char* arg) int main(int argc,const char** argv) { + Exiv2::XmpParser::initialize(); + ::atexit(Exiv2::XmpParser::terminate); + if ( argc < 2 ) { std::cout << "usage : " << argv[0] << " [key value]+" << std::endl; std::cout << "example: " << argv[0] << " [[-url] url | -server clanmills.com -page /LargsPanorama.jpg] -header \"Range: bytes=0-200\"" << std::endl; diff --git a/samples/ini-test.cpp b/samples/ini-test.cpp index fc446f74..54581f10 100644 --- a/samples/ini-test.cpp +++ b/samples/ini-test.cpp @@ -9,14 +9,15 @@ Config loaded from : 'initest.ini' version=6, name=Bob Smith, email=bob@smith.co // Example that shows simple usage of the INIReader class -#include +#include -// #include -#include "config.h" -#include "ini.hpp" +#include int main() { + Exiv2::XmpParser::initialize(); + ::atexit(Exiv2::XmpParser::terminate); + int result = 0 ; const char* ini = "ini-test.ini"; Exiv2::INIReader reader(ini); diff --git a/samples/iotest.cpp b/samples/iotest.cpp index 6205714f..774c0c43 100644 --- a/samples/iotest.cpp +++ b/samples/iotest.cpp @@ -46,7 +46,10 @@ int WriteReadSeek(BasicIo &io); // Main int main(int argc, char* const argv[]) { -try { + Exiv2::XmpParser::initialize(); + ::atexit(Exiv2::XmpParser::terminate); + + try { if (argc != 4) { std::cout << "Usage: " << argv[0] << " filein fileout1 fileout2\n"; std::cout << "fileouts are overwritten and should match filein exactly\n"; diff --git a/samples/iptceasy.cpp b/samples/iptceasy.cpp index a5d0f4ce..cda07eff 100644 --- a/samples/iptceasy.cpp +++ b/samples/iptceasy.cpp @@ -10,6 +10,9 @@ int main(int argc, char* const argv[]) try { + Exiv2::XmpParser::initialize(); + ::atexit(Exiv2::XmpParser::terminate); + if (argc != 2) { std::cout << "Usage: " << argv[0] << " file\n"; return 1; diff --git a/samples/iptcprint.cpp b/samples/iptcprint.cpp index ee295752..83866c0f 100644 --- a/samples/iptcprint.cpp +++ b/samples/iptcprint.cpp @@ -11,6 +11,9 @@ int main(int argc, char* const argv[]) try { + Exiv2::XmpParser::initialize(); + ::atexit(Exiv2::XmpParser::terminate); + if (argc != 2) { std::cout << "Usage: " << argv[0] << " file\n"; return 1; diff --git a/samples/iptctest.cpp b/samples/iptctest.cpp index 23b90030..a3cb37e1 100644 --- a/samples/iptctest.cpp +++ b/samples/iptctest.cpp @@ -26,6 +26,9 @@ void processModify(const std::string& line, int num, IptcData &iptcData); // Main int main(int argc, char* const argv[]) { + Exiv2::XmpParser::initialize(); + ::atexit(Exiv2::XmpParser::terminate); + try { if (argc != 2) { diff --git a/samples/key-test.cpp b/samples/key-test.cpp index 93c841f0..1f4a4d18 100644 --- a/samples/key-test.cpp +++ b/samples/key-test.cpp @@ -19,6 +19,9 @@ using namespace Exiv2; int main() { + Exiv2::XmpParser::initialize(); + ::atexit(Exiv2::XmpParser::terminate); + int tc = 0; int rc = 0; diff --git a/samples/largeiptc-test.cpp b/samples/largeiptc-test.cpp index 6ae90b99..fb6db4b9 100644 --- a/samples/largeiptc-test.cpp +++ b/samples/largeiptc-test.cpp @@ -8,6 +8,9 @@ int main(int argc, char* const argv[]) try { + Exiv2::XmpParser::initialize(); + ::atexit(Exiv2::XmpParser::terminate); + if (argc != 3) { std::cout << "Usage: " << argv[0] << " image datafile\n"; return 1; diff --git a/samples/metacopy.cpp b/samples/metacopy.cpp index 23c1a742..1af51462 100644 --- a/samples/metacopy.cpp +++ b/samples/metacopy.cpp @@ -40,6 +40,9 @@ int main(int argc, char* const argv[]) { try { + Exiv2::XmpParser::initialize(); + ::atexit(Exiv2::XmpParser::terminate); + // Handle command line arguments Params params; if (params.getopt(argc, argv)) { diff --git a/samples/mmap-test.cpp b/samples/mmap-test.cpp index 8f24466c..db3d4ba1 100644 --- a/samples/mmap-test.cpp +++ b/samples/mmap-test.cpp @@ -11,6 +11,9 @@ using namespace Exiv2; int main(int argc, char* const argv[]) try { + Exiv2::XmpParser::initialize(); + ::atexit(Exiv2::XmpParser::terminate); + if (argc != 2) { std::cout << "Usage: " << argv[0] << " file\n"; return 1; diff --git a/samples/mrwthumb.cpp b/samples/mrwthumb.cpp index 2157ea98..3c2cdf47 100644 --- a/samples/mrwthumb.cpp +++ b/samples/mrwthumb.cpp @@ -11,6 +11,9 @@ int main(int argc, char* const argv[]) { + Exiv2::XmpParser::initialize(); + ::atexit(Exiv2::XmpParser::terminate); + try { if (argc != 2) { std::cout << "Usage: " << argv[0] << " file\n"; diff --git a/samples/mt-test.cpp b/samples/mt-test.cpp index 70e59a06..f316299e 100644 --- a/samples/mt-test.cpp +++ b/samples/mt-test.cpp @@ -62,6 +62,9 @@ void reportExifMetadataCount(int n,const char* argv[]) int main(int argc,const char* argv[]) { + Exiv2::XmpParser::initialize(); + ::atexit(Exiv2::XmpParser::terminate); + int result = 0; if ( argc < 2 ) { diff --git a/samples/path-test.cpp b/samples/path-test.cpp index b52f4679..a1bf8e1b 100644 --- a/samples/path-test.cpp +++ b/samples/path-test.cpp @@ -12,6 +12,9 @@ int main(int argc, char* const argv[]) { + Exiv2::XmpParser::initialize(); + ::atexit(Exiv2::XmpParser::terminate); + if (argc != 2) { std::cout << "Usage: " << argv[0] << " file\n"; return 1; diff --git a/samples/prevtest.cpp b/samples/prevtest.cpp index c6a98bb3..cd44c5c5 100644 --- a/samples/prevtest.cpp +++ b/samples/prevtest.cpp @@ -10,6 +10,9 @@ int main(int argc, char* const argv[]) try { + Exiv2::XmpParser::initialize(); + ::atexit(Exiv2::XmpParser::terminate); + if (argc != 2) { std::cout << "Usage: " << argv[0] << " file\n"; return 1; diff --git a/samples/remotetest.cpp b/samples/remotetest.cpp index 703eb136..30e1d4a7 100644 --- a/samples/remotetest.cpp +++ b/samples/remotetest.cpp @@ -12,6 +12,9 @@ int main(int argc, char* const argv[]) try { + Exiv2::XmpParser::initialize(); + ::atexit(Exiv2::XmpParser::terminate); + if (argc < 2) { std::cout << "Usage: " << argv[0] << " file {--nocurl | --curl}\n\n"; return 1; diff --git a/samples/stringto-test.cpp b/samples/stringto-test.cpp index e58a1bc1..b3669968 100644 --- a/samples/stringto-test.cpp +++ b/samples/stringto-test.cpp @@ -40,6 +40,9 @@ const char* testcases[] = { int main() { + Exiv2::XmpParser::initialize(); + ::atexit(Exiv2::XmpParser::terminate); + std::cout << std::setfill(' '); std::cout << std::setw(12) << std::left << "string"; diff --git a/samples/taglist.cpp b/samples/taglist.cpp index 21c4fb56..ee0d132f 100644 --- a/samples/taglist.cpp +++ b/samples/taglist.cpp @@ -18,6 +18,9 @@ using namespace Exiv2; int main(int argc, char* argv[]) { + Exiv2::XmpParser::initialize(); + ::atexit(Exiv2::XmpParser::terminate); + int rc = EXIT_SUCCESS; std::ostringstream out; try { diff --git a/samples/tiff-test.cpp b/samples/tiff-test.cpp index 92b0f0a5..ff59513d 100644 --- a/samples/tiff-test.cpp +++ b/samples/tiff-test.cpp @@ -19,6 +19,9 @@ void mini9(const char* path); int main(int argc, char* const argv[]) try { + Exiv2::XmpParser::initialize(); + ::atexit(Exiv2::XmpParser::terminate); + if (argc != 2) { std::cout << "Usage: " << argv[0] << " file\n"; return 1; diff --git a/samples/tiffaddpath-test.cpp b/samples/tiffaddpath-test.cpp index 1a843b7b..8a94f17e 100644 --- a/samples/tiffaddpath-test.cpp +++ b/samples/tiffaddpath-test.cpp @@ -64,6 +64,9 @@ std::string tiffTagName(uint32_t tag) // Main program int main(int argc, char* const argv[]) { + Exiv2::XmpParser::initialize(); + ::atexit(Exiv2::XmpParser::terminate); + if (argc != 3) { std::cout << "Usage: " << argv[0] << " tag group\n" << "Print the TIFF path for a tag and group (decimal numbers)\n"; diff --git a/samples/toexv.cpp b/samples/toexv.cpp index 3fa26af0..a245c3ba 100644 --- a/samples/toexv.cpp +++ b/samples/toexv.cpp @@ -45,7 +45,10 @@ static size_t exifMetadataCount(Exiv2::Image::AutoPtr& image) // Main int main(int argc, char* const argv[]) { - try { + Exiv2::XmpParser::initialize(); + ::atexit(Exiv2::XmpParser::terminate); + + try { // Handle command line arguments Params params(":iecCahsx"); if (params.getopt(argc, argv)) return params.usage(); diff --git a/samples/werror-test.cpp b/samples/werror-test.cpp index ddce978d..b2036507 100644 --- a/samples/werror-test.cpp +++ b/samples/werror-test.cpp @@ -8,6 +8,9 @@ int main() { + Exiv2::XmpParser::initialize(); + ::atexit(Exiv2::XmpParser::terminate); + try { throw Exiv2::Error(Exiv2::kerGeneralError, "ARG1", "ARG2", "ARG3"); } diff --git a/samples/write-test.cpp b/samples/write-test.cpp index 12290f6b..16bc6f27 100644 --- a/samples/write-test.cpp +++ b/samples/write-test.cpp @@ -39,7 +39,10 @@ void exifPrint(const ExifData& exifData); // Main int main(int argc, char* const argv[]) { -try { + Exiv2::XmpParser::initialize(); + ::atexit(Exiv2::XmpParser::terminate); + + try { if (argc != 3) { std::cout << "Usage: write-test file case\n\n" diff --git a/samples/write2-test.cpp b/samples/write2-test.cpp index d220956b..dd11c76f 100644 --- a/samples/write2-test.cpp +++ b/samples/write2-test.cpp @@ -23,7 +23,10 @@ void print(const std::string& file); // Main int main(int argc, char* const argv[]) { -try { + Exiv2::XmpParser::initialize(); + ::atexit(Exiv2::XmpParser::terminate); + + try { if (argc != 2) { std::cout << "Usage: " << argv[0] << " file\n"; return 1; diff --git a/samples/xmpdump.cpp b/samples/xmpdump.cpp index 5f035c46..8171af87 100644 --- a/samples/xmpdump.cpp +++ b/samples/xmpdump.cpp @@ -11,6 +11,9 @@ int main(int argc, char* const argv[]) { + Exiv2::XmpParser::initialize(); + ::atexit(Exiv2::XmpParser::terminate); + try { if (argc != 2) { std::cout << "Usage: " << argv[0] << " file\n"; diff --git a/samples/xmpparse.cpp b/samples/xmpparse.cpp index ab39f640..7469bfdc 100644 --- a/samples/xmpparse.cpp +++ b/samples/xmpparse.cpp @@ -10,6 +10,9 @@ int main(int argc, char* const argv[]) try { + Exiv2::XmpParser::initialize(); + ::atexit(Exiv2::XmpParser::terminate); + if (argc != 2) { std::cout << "Usage: " << argv[0] << " file\n"; return 1; diff --git a/samples/xmpparser-test.cpp b/samples/xmpparser-test.cpp index 71bc37d2..88ef032b 100644 --- a/samples/xmpparser-test.cpp +++ b/samples/xmpparser-test.cpp @@ -10,6 +10,9 @@ int main(int argc, char* const argv[]) try { + Exiv2::XmpParser::initialize(); + ::atexit(Exiv2::XmpParser::terminate); + if (argc != 2) { std::cout << "Usage: " << argv[0] << " file\n"; return 1; diff --git a/samples/xmpprint.cpp b/samples/xmpprint.cpp index 49f6436f..5dfa04a2 100644 --- a/samples/xmpprint.cpp +++ b/samples/xmpprint.cpp @@ -16,7 +16,10 @@ int main(int argc, char** argv) { -try + Exiv2::XmpParser::initialize(); + ::atexit(Exiv2::XmpParser::terminate); + + try { if (argc != 2) { diff --git a/samples/xmpsample.cpp b/samples/xmpsample.cpp index 9a4e7b9c..86049c6e 100644 --- a/samples/xmpsample.cpp +++ b/samples/xmpsample.cpp @@ -19,6 +19,9 @@ bool isEqual(float a, float b) int main() try { + Exiv2::XmpParser::initialize(); + ::atexit(Exiv2::XmpParser::terminate); + // The XMP property container Exiv2::XmpData xmpData; diff --git a/src/exiv2.cpp b/src/exiv2.cpp index df226f3e..1132603c 100644 --- a/src/exiv2.cpp +++ b/src/exiv2.cpp @@ -127,6 +127,9 @@ namespace { // Main int main(int argc, char* const argv[]) { + Exiv2::XmpParser::initialize(); + ::atexit(Exiv2::XmpParser::terminate); + #ifdef EXV_ENABLE_NLS setlocale(LC_ALL, ""); const std::string localeDir = Exiv2::getProcessPath() + EXV_LOCALEDIR;