#1053. Added new option -K key for exact Key match (not grep as -g).

Added long versions of all options (eg --key = -K)
Added Command Summary to exiv2.1
Work in progress: exiv2.1 documents features which are not yet implemented:
- modifier - (stdin/stdout) to insert/extract
- modifier R (roundtrip) to insert
v0.27.3
Robin Mills 10 years ago
parent e3b26b97ba
commit 3c42a1e47f

@ -172,6 +172,8 @@ namespace Action {
int printList(); int printList();
//! Return true if key should be printed, else false //! Return true if key should be printed, else false
bool grepTag(const std::string& key); 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 //! Print all metadata in a user defined format
int printMetadata(const Exiv2::Image* image); int printMetadata(const Exiv2::Image* image);
//! Print a metadatum in a user defined format //! Print a metadatum in a user defined format

@ -556,7 +556,7 @@ namespace Action {
if (xmpData.empty()) sMissing = "XMP" ; 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; int result = ( sMissing.empty() || bTagFilterGiven ) ? 0 : -3;
if ( result ) { if ( result ) {
std::cerr << path_ << ": " << "(No " << sMissing << " data found in the file)\n"; 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 Print::grepTag(const std::string& key)
{ {
bool result=Params::instance().keys_.empty(); bool result=Params::instance().greps_.empty();
for (Params::Keys::const_iterator k = Params::instance().keys_.begin(); for (Params::Greps::const_iterator g = Params::instance().greps_.begin();
!result && k != Params::instance().keys_.end(); ++k) !result && g != Params::instance().greps_.end(); ++g)
{ {
#if EXV_HAVE_REGEX #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 #else
result = key.find(*k) != std::string::npos; result = key.find(*g) != std::string::npos;
#endif #endif
} }
return result ; 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) void Print::printMetadatum(const Exiv2::Metadatum& md, const Exiv2::Image* pImage)
{ {
if (!grepTag(md.key())) return; if (!grepTag(md.key())) return;
if (!keyTag (md.key())) return;
if ( Params::instance().unknown_ if ( Params::instance().unknown_
&& md.tagName().substr(0, 2) == "0x") { && md.tagName().substr(0, 2) == "0x") {

@ -3,7 +3,7 @@
.\" First parameter, NAME, should be all caps .\" First parameter, NAME, should be all caps
.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection .\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
.\" other parameters are allowed: see man(7), man(1) .\" 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. .\" Please adjust this date whenever revising the manpage.
.\" .\"
.\" Some roff macros, for reference: .\" 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 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 writes it back in UCS-2. Use option \fB\-n\fP to specify the current
encoding of the comment if necessary. 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 .SH OPTIONS
.TP .TP
.B \-h .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). Show unknown tags (default is to suppress tags which don't have a name).
.TP .TP
.B \-g \fIkey\fP .B \-g \fIkey\fP
Only output info for this Exiv2 key (grep). Only keys which match the given key (grep).
.br .br
Multiple \fB\-g\fP options Multiple \fB\-g\fP options
can be used to grep info for several keys. This option uses the system 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 Exif.Photo.DateTimeDigitized Ascii 20 2011:09:18 16:25:48
.fi .fi
.TP .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 .B \-n \fIenc\fP
Charset to use to decode Exif Unicode user comments. \fIenc\fP is Charset to use to decode Exif Unicode user comments. \fIenc\fP is
a name understood by \fBiconv_open\fP(3), e.g., 'UTF-8'. a name understood by \fBiconv_open\fP(3), e.g., 'UTF-8'.
@ -286,14 +359,23 @@ c : JPEG comment
.TP .TP
.B \-i \fItgt\fP .B \-i \fItgt\fP
Insert target(s) for the 'insert' action. Possible targets are the Insert target(s) for the 'insert' action. Possible targets are the
same as those for the \fB\-d\fP option, plus a modifier: same as those for the \fB\-d\fP option, plus an optional modifier:
.br .sp 1
X : Insert metadata from an XMP sidecar file <file>.xmp. The remaining X : Insert metadata from an XMP sidecar file <file>.xmp. The remaining
insert targets determine what metadata to insert from the sidecar insert targets determine what metadata to insert from the sidecar
file. Possible are Exif, IPTC and XMP and the default is all of file. Possible are Exif, IPTC and XMP and the default is all of
these. Note that the inserted XMP properties include those converted these. Note that the inserted XMP properties include those converted
to Exif and IPTC. 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 .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 Only JPEG thumbnails can be inserted (not TIFF thumbnails), they need to
be named \fIfile\fP\-thumb.jpg. be named \fIfile\fP\-thumb.jpg.
.TP .TP
@ -306,11 +388,13 @@ p[<n>[,<m> ...]] : Extract preview images. The optional comma separated
list of preview image numbers is used to determine which preview images list of preview image numbers is used to determine which preview images
to extract. The available preview images and their numbers are displayed to extract. The available preview images and their numbers are displayed
with the 'print' option \fB\-pp\fP. with the 'print' option \fB\-pp\fP.
.br .sp 1
X : Extract metadata to an XMP sidecar file <file>.xmp. The remaining X : Extract metadata to an XMP sidecar file <file>.xmp. The remaining
extract targets determine what metadata to extract to the sidecar extract targets determine what metadata to extract to the sidecar
file. Possible are Exif, IPTC and XMP and the default is all of these. file. Possible are Exif, IPTC and XMP and the default is all of these.
.sp 1 .sp 1
- : Output sidecar file to stdout (see -i tgt for example)
.sp 1
.TP .TP
.B \-r \fIfmt\fP .B \-r \fIfmt\fP
Filename format for the 'rename' action. The format string follows Filename format for the 'rename' action. The format string follows

@ -215,7 +215,7 @@ void Params::version(bool verbose,std::ostream& os) const
bool b64 = sizeof(void*)==8; bool b64 = sizeof(void*)==8;
const char* sBuild = b64 ? "(64 bit build)" : "(32 bit build)" ; const char* sBuild = b64 ? "(64 bit build)" : "(32 bit build)" ;
os << EXV_PACKAGE_STRING << " " << Exiv2::versionNumberHexString() << " " << sBuild << "\n"; 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") os << _("Copyright (C) 2004-2013 Andreas Huggel.\n")
<< "\n" << "\n"
<< _("This program is free software; you can redistribute it and/or\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"); "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 void Params::usage(std::ostream& os) const
@ -274,6 +274,7 @@ void Params::help(std::ostream& os) const
<< _(" -b Show large binary values.\n") << _(" -b Show large binary values.\n")
<< _(" -u Show unknown tags.\n") << _(" -u Show unknown tags.\n")
<< _(" -g key Only output info for this key (grep).\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") << _(" -n enc Charset to use to decode UNICODE Exif user comments.\n")
<< _(" -k Preserve file timestamps (keep).\n") << _(" -k Preserve file timestamps (keep).\n")
<< _(" -t Also set the file timestamp in 'rename' action (overrides -k).\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 'u': unknown_ = false; break;
case 'f': force_ = true; fileExistsPolicy_ = overwritePolicy; break; case 'f': force_ = true; fileExistsPolicy_ = overwritePolicy; break;
case 'F': force_ = true; fileExistsPolicy_ = renamePolicy; 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 'n': charset_ = optarg; break;
case 'r': rc = evalRename(opt, optarg); break; case 'r': rc = evalRename(opt, optarg); break;
case 't': 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; return rc;
} // Params::setLogLevel } // Params::setLogLevel
int Params::evalKey( const std::string& optarg) int Params::evalGrep( const std::string& optarg)
{ {
int result=0; int result=0;
#if EXV_HAVE_REGEX #if EXV_HAVE_REGEX
// try to compile a reg-exp from the input argument and store it in the vector // try to compile a reg-exp from the input argument and store it in the vector
const size_t i = keys_.size(); const size_t i = greps_.size();
keys_.resize(i + 1); greps_.resize(i + 1);
regex_t *pRegex = &keys_[i]; regex_t *pRegex = &greps_[i];
int errcode = regcomp( pRegex, optarg.c_str(), REG_NOSUB); int errcode = regcomp( pRegex, optarg.c_str(), REG_NOSUB);
// there was an error compiling the regexp // there was an error compiling the regexp
@ -436,17 +438,24 @@ int Params::evalKey( const std::string& optarg)
std::cerr << progname() std::cerr << progname()
<< ": " << _("Option") << " -g: " << ": " << _("Option") << " -g: "
<< _("Invalid regexp") << " \"" << optarg << "\": " << buffer << "\n"; << _("Invalid regexp") << " \"" << optarg << "\": " << buffer << "\n";
// free the memory and drop the regexp // free the memory and drop the regexp
delete[] buffer; delete[] buffer;
regfree( pRegex); regfree( pRegex);
keys_.resize(i); greps_.resize(i);
result=1; result=1;
} }
#else #else
keys_.push_back(optarg); greps_.push_back(optarg);
#endif #endif
return result; return result;
} // Params::evalGrep
int Params::evalKey( const std::string& optarg)
{
int result=0;
keys_.push_back(optarg);
return result;
} // Params::evalKey } // Params::evalKey
int Params::evalRename(int opt, const std::string& optarg) int Params::evalRename(int opt, const std::string& optarg)
@ -835,8 +844,55 @@ int Params::nonoption(const std::string& argv)
return rc; return rc;
} // Params::nonoption } // Params::nonoption
int Params::getopt(int argc, char* const argv[]) typedef std::map<std::string,std::string> 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_); int rc = Util::Getopt::getopt(argc, argv, optstring_);
// Further consistency checks // Further consistency checks
if (help_ || version_) return 0; 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"); << _("-T option can only be used with rename action\n");
rc = 1; rc = 1;
} }
// cleanup the argument vector
for ( int i = 0 ; i < argc ; i++ ) ::free((void*)argv[i]);
delete [] argv;
return rc; return rc;
} // Params::getopt } // Params::getopt
@ -1118,7 +1179,7 @@ namespace {
|| cmdEnd == std::string::npos || cmdEnd == std::string::npos
|| keyStart == std::string::npos) { || keyStart == std::string::npos) {
std::string cmdLine ; 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]) ; } for ( int i = 1 ; i < __argc ; i++ ) { cmdLine += std::string(" ") + formatArg(__argv[i]) ; }
#endif #endif
throw Exiv2::Error(1, Exiv2::toString(num) throw Exiv2::Error(1, Exiv2::toString(num)

@ -128,8 +128,10 @@ public:
typedef std::vector<std::string> Files; typedef std::vector<std::string> Files;
//! Container for preview image numbers //! Container for preview image numbers
typedef std::set<int> PreviewNumbers; typedef std::set<int> PreviewNumbers;
//! Container for greps
typedef exv_grep_keys_t Greps;
//! Container for keys //! Container for keys
typedef exv_grep_keys_t Keys; typedef std::vector<std::string> Keys;
/*! /*!
@brief Controls all access to the global Params instance. @brief Controls all access to the global Params instance.
@ -218,7 +220,8 @@ public:
std::string suffix_; //!< File extension of the file to insert std::string suffix_; //!< File extension of the file to insert
Files files_; //!< List of non-option arguments. Files files_; //!< List of non-option arguments.
PreviewNumbers previewNumbers_; //!< List of preview numbers 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 std::string charset_; //!< Charset to use for UNICODE Exif user comment
private: private:
@ -234,7 +237,7 @@ private:
@brief Default constructor. Note that optstring_ is initialized here. @brief Default constructor. Note that optstring_ is initialized here.
The c'tor is private to force instantiation through instance(). 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), help_(false),
version_(false), version_(false),
verbose_(false), verbose_(false),
@ -267,6 +270,7 @@ private:
//! @name Helpers //! @name Helpers
//@{ //@{
int setLogLevel(const std::string& optarg); int setLogLevel(const std::string& optarg);
int evalGrep( const std::string& optarg);
int evalKey( const std::string& optarg); int evalKey( const std::string& optarg);
int evalRename(int opt, const std::string& optarg); int evalRename(int opt, const std::string& optarg);
int evalAdjust(const std::string& optarg); int evalAdjust(const std::string& optarg);

@ -144,28 +144,28 @@ typedef string_v::iterator string_i;
#endif #endif
#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(); bool bPrint = greps.empty();
for( exv_grep_keys_t::const_iterator k = keys.begin(); for( exv_grep_keys_t::const_iterator g = greps.begin();
!bPrint && k != keys.end() ; ++k !bPrint && g != greps.end() ; ++g
) { ) {
#if EXV_HAVE_REGEX #if EXV_HAVE_REGEX
bPrint = ( 0 == regexec( &(*k), name , 0, NULL, REG_NOTBOL | REG_NOTEOL) bPrint = ( 0 == regexec( &(*g), name , 0, NULL, REG_NOTBOL | REG_NOTEOL)
|| 0 == regexec( &(*k), value.c_str(), 0, NULL, REG_NOTBOL | REG_NOTEOL) || 0 == regexec( &(*g), value.c_str(), 0, NULL, REG_NOTBOL | REG_NOTEOL)
); );
#else #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 #endif
} }
if ( bPrint ) os << name << "=" << value << endl; 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; std::ostringstream stringStream;
stringStream << value; 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) void dumpLibraryInfo(std::ostream& os,const exv_grep_keys_t& keys)

Loading…
Cancel
Save