diff --git a/include/exiv2/actions.hpp b/include/exiv2/actions.hpp index 804e86a6..44cdd2ba 100644 --- a/include/exiv2/actions.hpp +++ b/include/exiv2/actions.hpp @@ -172,6 +172,8 @@ namespace Action { int printList(); //! Return true if key should be printed, else false bool grepTag(const std::string& key); + //! Return true if key should be printed, else false + bool keyTag(const std::string& key); //! Print all metadata in a user defined format int printMetadata(const Exiv2::Image* image); //! Print a metadatum in a user defined format diff --git a/src/actions.cpp b/src/actions.cpp index 129ef5d6..52dbc788 100644 --- a/src/actions.cpp +++ b/src/actions.cpp @@ -556,7 +556,7 @@ namespace Action { if (xmpData.empty()) sMissing = "XMP" ; } - bool bTagFilterGiven = !Params::instance().keys_.empty(); // were tag filters given with -g? + bool bTagFilterGiven = !Params::instance().greps_.empty(); // were tag filters given with -g? int result = ( sMissing.empty() || bTagFilterGiven ) ? 0 : -3; if ( result ) { std::cerr << path_ << ": " << "(No " << sMissing << " data found in the file)\n"; @@ -566,22 +566,34 @@ namespace Action { bool Print::grepTag(const std::string& key) { - bool result=Params::instance().keys_.empty(); - for (Params::Keys::const_iterator k = Params::instance().keys_.begin(); - !result && k != Params::instance().keys_.end(); ++k) + bool result=Params::instance().greps_.empty(); + for (Params::Greps::const_iterator g = Params::instance().greps_.begin(); + !result && g != Params::instance().greps_.end(); ++g) { #if EXV_HAVE_REGEX - result = regexec( &(*k), key.c_str(), 0, NULL, REG_NOTBOL | REG_NOTEOL) == 0 ; + result = regexec( &(*g), key.c_str(), 0, NULL, REG_NOTBOL | REG_NOTEOL) == 0 ; #else - result = key.find(*k) != std::string::npos; + result = key.find(*g) != std::string::npos; #endif } return result ; } + bool Print::keyTag(const std::string& key) + { + bool result=Params::instance().keys_.empty(); + for (Params::Keys::const_iterator k = Params::instance().keys_.begin(); + !result && k != Params::instance().keys_.end(); ++k) + { + result = key.compare(*k) == 0; + } + return result ; + } + void Print::printMetadatum(const Exiv2::Metadatum& md, const Exiv2::Image* pImage) { if (!grepTag(md.key())) return; + if (!keyTag (md.key())) return; if ( Params::instance().unknown_ && md.tagName().substr(0, 2) == "0x") { diff --git a/src/exiv2.1 b/src/exiv2.1 index 993da896..50ba111a 100644 --- a/src/exiv2.1 +++ b/src/exiv2.1 @@ -3,7 +3,7 @@ .\" First parameter, NAME, should be all caps .\" Second parameter, SECTION, should be 1-8, maybe w/ subsection .\" other parameters are allowed: see man(7), man(1) -.TH EXIV2 1 "Mar 27, 2015" +.TH EXIV2 1 "Apr 7, 2015" .\" Please adjust this date whenever revising the manpage. .\" .\" Some roff macros, for reference: @@ -118,6 +118,68 @@ Fix the character encoding of Exif Unicode user comments. Decodes the comment using the auto-detected or specified character encoding and writes it back in UCS-2. Use option \fB\-n\fP to specify the current encoding of the comment if necessary. +.br +.ne 40 +.SH COMMAND SUMMARY +.sp 1 +.nf +exiv2 [ opt [arg] ]+ [ act ] file ... +.sp 1 +option [arg] long option description +-a tim --adjust Modify time stamps. [+|-]HH[:MM[:SS[.mmm]]] +-b --binary Show large binary values (default is to suppress them). +-c txt --comment JPEG comment string to set in the image ('modify' action). ... +-d tgt --delete Delete target(s) for the 'delete' action. ... +-D +-n --days Time adjustment by a positive or negative number of days ... +-e tgt --extract Extract target(s) for the 'extract' action. +-f --force Do not prompt before overwriting existing files ... +-F --Force Do not prompt before renaming files (Force rename) ... +-g key --grep Only output info for this Exiv2 key (grep). +-h --help Display help and exit. +-i tgt --insert Insert target(s) for the 'insert' action. ... +-k --keep Preserve file timestamps when updating files (keep) +-K Key --key Report key. Similar to -g (grep) however key must match exactly. +-l dir --location Location (directory) for files to be inserted or extracted. +-m file --modify read commands from cmd-file +-M cmd --Modify Command line for the 'modify' action. ... +-n enc --encode Charset to decode Exif Unicode user comments. See: man 3 iconv_open +-O +-n --months Time adjustment by a positive or negative number of months, ... +-p mod --print Print report (common reports) +-P flg --Print Print report (fine grained control) +-q --quiet Silence warnings and error messages from the Exiv2 library ... +-Q lvl --log Set the log-level to 'd'(ebug), 'i'(nfo), 'w'(arning), 'e'(rror) +-r fmt --rename Filename format for the 'rename' action. ... +-S suf --suffix Use suffix .suf for source files for insert command. +-t --timestamp Set the file timestamp according to the Exif create timestamp ... +-T --Timestamp Only set the file timestamp according to Exif create timestamp ... +-u --unknown Show unknown tags ... +-v --verbose verbose +-V --version Show the program version and exit. +-Y +-n --years Time adjustment by a positive or negative number of years ... +.sp 1 +act pr | ex | in | rm | ad | mo | mv | fi | fc + print, extract, insert, delete, adjust, modify, rename, fixiso,fixcom + +cmd See "Commands" below. + +flg E | I | X | x | g | k | l | n | y | c | s | v | t | h + Exif , IPTC, XMP, num, grp, key, label, name , type, count, size, vanilla, translated, hex + +fmt Default format is %Y%m%d_%H%M%S. + +lvl d | i | i | w | e + debug, info, warning, error + +mod s | a | t | v | h | i | x | c | p | i | S | X : + summary, add, translated, vanilla, hex ... + iptc ,xmp, comment, preview, Structure,XMP raw + +tgt a | c | e | i | t | x + all, comment, exif, iptc, thumb, xmp + +.br +.fi +.ne 40 .SH OPTIONS .TP .B \-h @@ -152,7 +214,7 @@ Show large binary values (default is to suppress them). Show unknown tags (default is to suppress tags which don't have a name). .TP .B \-g \fIkey\fP -Only output info for this Exiv2 key (grep). +Only keys which match the given key (grep). .br Multiple \fB\-g\fP options can be used to grep info for several keys. This option uses the system @@ -167,6 +229,17 @@ Exif.Photo.DateTimeOriginal Ascii 20 2011:09:18 16:25:48 Exif.Photo.DateTimeDigitized Ascii 20 2011:09:18 16:25:48 .fi .TP +.B \-K \fIkey\fP +Only report data for given key. +.br +Multiple \fB\-K\fP options can be used to report more than a single key. + +.nf +exiv2 \-K Exif.Photo.DateTimeDigitized -K Exif.Photo.DateTimeOriginal \-pt R.jpg +Exif.Photo.DateTimeOriginal Ascii 20 2011:09:18 16:25:48 +Exif.Photo.DateTimeDigitized Ascii 20 2011:09:18 16:25:48 +.fi +.TP .B \-n \fIenc\fP Charset to use to decode Exif Unicode user comments. \fIenc\fP is a name understood by \fBiconv_open\fP(3), e.g., 'UTF-8'. @@ -286,14 +359,23 @@ c : JPEG comment .TP .B \-i \fItgt\fP Insert target(s) for the 'insert' action. Possible targets are the -same as those for the \fB\-d\fP option, plus a modifier: -.br +same as those for the \fB\-d\fP option, plus an optional modifier: +.sp 1 X : Insert metadata from an XMP sidecar file .xmp. The remaining insert targets determine what metadata to insert from the sidecar file. Possible are Exif, IPTC and XMP and the default is all of these. Note that the inserted XMP properties include those converted to Exif and IPTC. +.sp 1 +R : Round trip. Extract an XMP sidecar file to memory and read in back +into the file. +.sp 1 +- : Insert metadata from an XMP sidecar read from stdin .br +This is option is intended for "filter" operations on the XMP such as: +.br +$ exiv2 -e{tgt}- \fIfilename\fP | xmllint .... | exiv2 -i{tgt}- \fIfilename\fP +.sp 1 Only JPEG thumbnails can be inserted (not TIFF thumbnails), they need to be named \fIfile\fP\-thumb.jpg. .TP @@ -306,11 +388,13 @@ p[[, ...]] : Extract preview images. The optional comma separated list of preview image numbers is used to determine which preview images to extract. The available preview images and their numbers are displayed with the 'print' option \fB\-pp\fP. -.br +.sp 1 X : Extract metadata to an XMP sidecar file .xmp. The remaining extract targets determine what metadata to extract to the sidecar file. Possible are Exif, IPTC and XMP and the default is all of these. .sp 1 +- : Output sidecar file to stdout (see -i tgt for example) +.sp 1 .TP .B \-r \fIfmt\fP Filename format for the 'rename' action. The format string follows diff --git a/src/exiv2.cpp b/src/exiv2.cpp index 9fe5e183..bd37450a 100644 --- a/src/exiv2.cpp +++ b/src/exiv2.cpp @@ -215,7 +215,7 @@ void Params::version(bool verbose,std::ostream& os) const bool b64 = sizeof(void*)==8; const char* sBuild = b64 ? "(64 bit build)" : "(32 bit build)" ; os << EXV_PACKAGE_STRING << " " << Exiv2::versionNumberHexString() << " " << sBuild << "\n"; - if ( Params::instance().keys_.empty() ) { + if ( Params::instance().greps_.empty() ) { os << _("Copyright (C) 2004-2013 Andreas Huggel.\n") << "\n" << _("This program is free software; you can redistribute it and/or\n" @@ -234,7 +234,7 @@ void Params::version(bool verbose,std::ostream& os) const "Boston, MA 02110-1301 USA\n"); } - if ( verbose ) dumpLibraryInfo(os,Params::instance().keys_); + if ( verbose ) dumpLibraryInfo(os,Params::instance().greps_); } void Params::usage(std::ostream& os) const @@ -274,6 +274,7 @@ void Params::help(std::ostream& os) const << _(" -b Show large binary values.\n") << _(" -u Show unknown tags.\n") << _(" -g key Only output info for this key (grep).\n") + << _(" -K key Only output info for this key (exact match).\n") << _(" -n enc Charset to use to decode UNICODE Exif user comments.\n") << _(" -k Preserve file timestamps (keep).\n") << _(" -t Also set the file timestamp in 'rename' action (overrides -k).\n") @@ -360,7 +361,8 @@ int Params::option(int opt, const std::string& optarg, int optopt) case 'u': unknown_ = false; break; case 'f': force_ = true; fileExistsPolicy_ = overwritePolicy; break; case 'F': force_ = true; fileExistsPolicy_ = renamePolicy; break; - case 'g': rc = evalKey(optarg); printMode_ = pmList; break; + case 'g': rc = evalGrep(optarg); printMode_ = pmList; break; + case 'K': rc = evalKey(optarg); printMode_ = pmList; break; case 'n': charset_ = optarg; break; case 'r': rc = evalRename(opt, optarg); break; case 't': rc = evalRename(opt, optarg); break; @@ -418,14 +420,14 @@ int Params::setLogLevel(const std::string& optarg) return rc; } // Params::setLogLevel -int Params::evalKey( const std::string& optarg) +int Params::evalGrep( const std::string& optarg) { int result=0; #if EXV_HAVE_REGEX // try to compile a reg-exp from the input argument and store it in the vector - const size_t i = keys_.size(); - keys_.resize(i + 1); - regex_t *pRegex = &keys_[i]; + const size_t i = greps_.size(); + greps_.resize(i + 1); + regex_t *pRegex = &greps_[i]; int errcode = regcomp( pRegex, optarg.c_str(), REG_NOSUB); // there was an error compiling the regexp @@ -436,17 +438,24 @@ int Params::evalKey( const std::string& optarg) std::cerr << progname() << ": " << _("Option") << " -g: " << _("Invalid regexp") << " \"" << optarg << "\": " << buffer << "\n"; - + // free the memory and drop the regexp delete[] buffer; regfree( pRegex); - keys_.resize(i); + greps_.resize(i); result=1; } #else - keys_.push_back(optarg); + greps_.push_back(optarg); #endif return result; +} // Params::evalGrep + +int Params::evalKey( const std::string& optarg) +{ + int result=0; + keys_.push_back(optarg); + return result; } // Params::evalKey int Params::evalRename(int opt, const std::string& optarg) @@ -835,8 +844,55 @@ int Params::nonoption(const std::string& argv) return rc; } // Params::nonoption -int Params::getopt(int argc, char* const argv[]) +typedef std::map long_t; + +int Params::getopt(int argc, char* const Argv[]) { + char** argv = new char* [argc+1]; + argv[argc] = NULL; + long_t longs; + + longs["--adjust" ] = "-a"; + longs["--binary" ] = "-b"; + longs["--comment" ] = "-c"; + longs["--delete" ] = "-d"; + longs["--days" ] = "-D"; + longs["--force" ] = "-f"; + longs["--Force" ] = "-F"; + longs["--grep" ] = "-g"; + longs["--help" ] = "-h"; + longs["--insert" ] = "-i"; + longs["--keep" ] = "-k"; + longs["--key" ] = "-K"; + longs["--location" ] = "-l"; + longs["--modify" ] = "-m"; + longs["--Modify" ] = "-M"; + longs["--encode" ] = "-n"; + longs["--months" ] = "-O"; + longs["--print" ] = "-p"; + longs["--Print" ] = "-P"; + longs["--quiet" ] = "-q"; + longs["--log" ] = "-Q"; + longs["--rename" ] = "-r"; + longs["--suffix" ] = "-S"; + longs["--timestamp"] = "-t"; + longs["--Timestamp"] = "-T"; + longs["--unknown" ] = "-u"; + longs["--verbose" ] = "-v"; + longs["--Version" ] = "-V"; + longs["--version" ] = "-V"; + longs["--years" ] = "-Y"; + + for ( int i = 0 ; i < argc ; i++ ) { + std::string* arg = new std::string(Argv[i]); + if (longs.find(*arg) != longs.end() ) { + argv[i] = ::strdup(longs[*arg].c_str()); + } else { + argv[i] = ::strdup(Argv[i]); + } + delete arg; + } + int rc = Util::Getopt::getopt(argc, argv, optstring_); // Further consistency checks if (help_ || version_) return 0; @@ -903,6 +959,11 @@ int Params::getopt(int argc, char* const argv[]) << _("-T option can only be used with rename action\n"); rc = 1; } + + // cleanup the argument vector + for ( int i = 0 ; i < argc ; i++ ) ::free((void*)argv[i]); + delete [] argv; + return rc; } // Params::getopt @@ -1118,7 +1179,7 @@ namespace { || cmdEnd == std::string::npos || keyStart == std::string::npos) { std::string cmdLine ; -#if defined(_MSC_VER) || defined(__MINGW__) +#if defined(_MSC_VER) || defined(__MINGW__) for ( int i = 1 ; i < __argc ; i++ ) { cmdLine += std::string(" ") + formatArg(__argv[i]) ; } #endif throw Exiv2::Error(1, Exiv2::toString(num) diff --git a/src/exiv2app.hpp b/src/exiv2app.hpp index 6674624c..d9cb056a 100644 --- a/src/exiv2app.hpp +++ b/src/exiv2app.hpp @@ -128,8 +128,10 @@ public: typedef std::vector Files; //! Container for preview image numbers typedef std::set PreviewNumbers; + //! Container for greps + typedef exv_grep_keys_t Greps; //! Container for keys - typedef exv_grep_keys_t Keys; + typedef std::vector Keys; /*! @brief Controls all access to the global Params instance. @@ -218,7 +220,8 @@ public: std::string suffix_; //!< File extension of the file to insert Files files_; //!< List of non-option arguments. PreviewNumbers previewNumbers_; //!< List of preview numbers - Keys keys_; //!< List of keys to 'grep' from the metadata + Greps greps_; //!< List of keys to 'grep' from the metadata + Keys keys_; //!< List of keys to match from the metadata std::string charset_; //!< Charset to use for UNICODE Exif user comment private: @@ -234,7 +237,7 @@ private: @brief Default constructor. Note that optstring_ is initialized here. The c'tor is private to force instantiation through instance(). */ - Params() : optstring_(":hVvqfbuktTFa:Y:O:D:r:p:P:d:e:i:c:m:M:l:S:g:n:Q:"), + Params() : optstring_(":hVvqfbuktTFa:Y:O:D:r:p:P:d:e:i:c:m:M:l:S:g:K:n:Q:"), help_(false), version_(false), verbose_(false), @@ -267,6 +270,7 @@ private: //! @name Helpers //@{ int setLogLevel(const std::string& optarg); + int evalGrep( const std::string& optarg); int evalKey( const std::string& optarg); int evalRename(int opt, const std::string& optarg); int evalAdjust(const std::string& optarg); diff --git a/src/version.cpp b/src/version.cpp index 8bc00723..8ae7d856 100644 --- a/src/version.cpp +++ b/src/version.cpp @@ -144,28 +144,28 @@ typedef string_v::iterator string_i; #endif #endif -static void output(std::ostream& os,const exv_grep_keys_t& keys,const char* name,const std::string& value) +static void output(std::ostream& os,const exv_grep_keys_t& greps,const char* name,const std::string& value) { - bool bPrint = keys.empty(); - for( exv_grep_keys_t::const_iterator k = keys.begin(); - !bPrint && k != keys.end() ; ++k + bool bPrint = greps.empty(); + for( exv_grep_keys_t::const_iterator g = greps.begin(); + !bPrint && g != greps.end() ; ++g ) { #if EXV_HAVE_REGEX - bPrint = ( 0 == regexec( &(*k), name , 0, NULL, REG_NOTBOL | REG_NOTEOL) - || 0 == regexec( &(*k), value.c_str(), 0, NULL, REG_NOTBOL | REG_NOTEOL) + bPrint = ( 0 == regexec( &(*g), name , 0, NULL, REG_NOTBOL | REG_NOTEOL) + || 0 == regexec( &(*g), value.c_str(), 0, NULL, REG_NOTBOL | REG_NOTEOL) ); #else - bPrint = std::string(name).find(*k) != std::string::npos || value.find(*k) != std::string::npos; + bPrint = std::string(name).find(*g) != std::string::npos || value.find(*g) != std::string::npos; #endif } if ( bPrint ) os << name << "=" << value << endl; } -static void output(std::ostream& os,const exv_grep_keys_t& keys,const char* name,int value) +static void output(std::ostream& os,const exv_grep_keys_t& greps,const char* name,int value) { std::ostringstream stringStream; stringStream << value; - output(os,keys,name,stringStream.str()); + output(os,greps,name,stringStream.str()); } void dumpLibraryInfo(std::ostream& os,const exv_grep_keys_t& keys)