From ab9ee2c6dffe4c82e3df8490db1a44275377da76 Mon Sep 17 00:00:00 2001 From: Robin Mills Date: Tue, 8 Dec 2015 09:27:38 +0000 Subject: [PATCH] #1024. Support for C++11 #include . --grep keys may have an optional trailer /i to indicate to ignore case. --- include/exiv2/version.hpp | 22 +++++-- src/actions.cpp | 15 ++++- src/exiv2.1 | 11 ++-- src/exiv2.cpp | 112 ++++++++++++++++++++---------------- src/version.cpp | 22 ++++++- test/bugfixes-test.sh | 11 +++- test/data/bugfixes-test.out | Bin 1844064 -> 1844373 bytes test/data/exiv2-bug1024.exv | Bin 0 -> 16003 bytes 8 files changed, 131 insertions(+), 62 deletions(-) create mode 100644 test/data/exiv2-bug1024.exv diff --git a/include/exiv2/version.hpp b/include/exiv2/version.hpp index b9f88b63..ff93d7b0 100644 --- a/include/exiv2/version.hpp +++ b/include/exiv2/version.hpp @@ -37,11 +37,25 @@ // + standard includes #include #include -#if EXV_HAVE_REGEX -#include -typedef std::vector exv_grep_keys_t ; + +#define CPLUSPLUS11 201103L + +#if __cplusplus >= CPLUSPLUS11 +# include + typedef std::vector exv_grep_keys_t ; #else -typedef std::vector exv_grep_keys_t ; +# if EXV_HAVE_REGEX +# include + typedef std::vector exv_grep_keys_t ; +# else + struct Exiv2_grep_key_t { + Exiv2_grep_key_t(std::string pattern,bool bIgnoreCase) + :pattern_(pattern),bIgnoreCase_(bIgnoreCase) {} + std::string pattern_; + bool bIgnoreCase_; + }; + typedef std::vector exv_grep_keys_t ; +# endif #endif /*! diff --git a/src/actions.cpp b/src/actions.cpp index 20c0a5be..d6a29fa8 100644 --- a/src/actions.cpp +++ b/src/actions.cpp @@ -49,6 +49,7 @@ EXIV2_RCSID("@(#) $Id$") #include "preview.hpp" #include "futils.hpp" #include "i18n.h" // NLS support. +#include "version.hpp" // + standard includes #include @@ -581,10 +582,22 @@ namespace Action { for (Params::Greps::const_iterator g = Params::instance().greps_.begin(); !result && g != Params::instance().greps_.end(); ++g) { +#if __cplusplus >= CPLUSPLUS11 + std::smatch m; + result = std::regex_search(key,m, *g); +#else #if EXV_HAVE_REGEX result = regexec( &(*g), key.c_str(), 0, NULL, 0) == 0 ; #else - result = key.find(*g) != std::string::npos; + std::string Pattern(g->pattern_); + std::string Key(key); + if ( g->bIgnoreCase_ ) { + // https://notfaq.wordpress.com/2007/08/04/cc-convert-string-to-upperlower-case/ + std::transform(Pattern.begin(), Pattern.end(),Pattern.begin(), ::tolower); + std::transform(Key.begin() , Key.end() ,Key.begin() , ::tolower); + } + result = Key.find(Pattern) != std::string::npos; +#endif #endif } return result ; diff --git a/src/exiv2.1 b/src/exiv2.1 index 02d8c8ad..0714663d 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 "Nov 23, 2015" +.TH EXIV2 1 "Dec 8, 2015" .\" Please adjust this date whenever revising the manpage. .\" .\" Some roff macros, for reference: @@ -222,10 +222,11 @@ Show unknown tags (default is to suppress tags which don't have a name). 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 -regular expression engine: see man 3 regex. Platforms which do not support +can be used to grep info for several keys. When the library is build with C++11, +keys are matched using std::regex::extended. When build without C++11, keys are +processed with the system regular expression engine: see man 3 regex. Platforms which do not support regex use key for a substring match. You can determine the availability of regex -using the command: exiv2 -v -V -g have_regex +using the command: exiv2 -v -V -g have_regex -g cplusplus. .nf exiv2 \-g Date \-pt R.jpg @@ -235,6 +236,8 @@ Exif.Photo.DateTimeDigitized Ascii 20 2011:09:18 16:25:48 .fi -g (--grep) is only applied to keys. It is not generally applied to all output such as the default -ps report. + +The key may finish with the optional modifier /i to indicated case insensitive. .TP .B \-K \fIkey\fP Only report data for given key. diff --git a/src/exiv2.cpp b/src/exiv2.cpp index 9878f5c1..22f72dd1 100644 --- a/src/exiv2.cpp +++ b/src/exiv2.cpp @@ -415,15 +415,30 @@ int Params::setLogLevel(const std::string& optarg) return rc; } // Params::setLogLevel +// http://stackoverflow.com/questions/874134/find-if-string-ends-with-another-string-in-c +static inline bool ends_with(std::string const & value, std::string const & ending,std::string& stub) +{ + if (ending.size() > value.size()) return false; + bool bResult = std::equal(ending.rbegin(), ending.rend(), value.rbegin()); + stub = bResult ? value.substr(0,value.length() - ending.length()) : value; + return bResult ; +} + int Params::evalGrep( const std::string& optarg) { int result=0; + std::string pattern; + std::string ignoreCase("/i"); + bool bIgnoreCase = ends_with(optarg,ignoreCase,pattern); +#if __cplusplus >= CPLUSPLUS11 + greps_.push_back( std::regex(pattern, bIgnoreCase ? std::regex::icase|std::regex::extended : std::regex::extended) ); +#else #if EXV_HAVE_REGEX // try to compile a reg-exp from the input argument and store it in the vector const size_t i = greps_.size(); greps_.resize(i + 1); regex_t *pRegex = &greps_[i]; - int errcode = regcomp( pRegex, optarg.c_str(), REG_NOSUB); + int errcode = regcomp( pRegex, pattern.c_str(), bIgnoreCase ? REG_NOSUB|REG_ICASE : REG_NOSUB); // there was an error compiling the regexp if( errcode ) { @@ -441,7 +456,8 @@ int Params::evalGrep( const std::string& optarg) result=1; } #else - greps_.push_back(optarg); + greps_.push_back(Exiv2_grep_key_t(pattern,bIgnoreCase)); +#endif #endif return result; } // Params::evalGrep @@ -846,50 +862,50 @@ 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; - } + 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 @@ -958,8 +974,8 @@ int Params::getopt(int argc, char* const Argv[]) rc = 1; } - // cleanup the argument vector - for ( int i = 0 ; i < argc ; i++ ) ::free((void*)argv[i]); + // cleanup the argument vector + for ( int i = 0 ; i < argc ; i++ ) ::free((void*)argv[i]); delete [] argv; return rc; diff --git a/src/version.cpp b/src/version.cpp index 55fb2e6f..4d499a8c 100644 --- a/src/version.cpp +++ b/src/version.cpp @@ -172,18 +172,32 @@ namespace Exiv2 { }; #endif -static bool shouldOutput(const exv_grep_keys_t& greps,const char* name,const std::string& value) +static bool shouldOutput(const exv_grep_keys_t& greps,const char* key,const std::string& value) { bool bPrint = greps.empty(); for( exv_grep_keys_t::const_iterator g = greps.begin(); !bPrint && g != greps.end() ; ++g ) { +#if __cplusplus >= CPLUSPLUS11 + std::smatch m; + bPrint = std::regex_search(std::string(key),m,*g) || std::regex_search(value,m,*g); +#else #if EXV_HAVE_REGEX - bPrint = ( 0 == regexec( &(*g), name , 0, NULL, 0) + bPrint = ( 0 == regexec( &(*g), key , 0, NULL, 0) || 0 == regexec( &(*g), value.c_str(), 0, NULL, 0) ); #else - bPrint = std::string(name).find(*g) != std::string::npos || value.find(*g) != std::string::npos; + std::string Pattern(g->pattern_); + std::string Key(key); + std::string Value(value); + if ( g->bIgnoreCase_ ) { + // https://notfaq.wordpress.com/2007/08/04/cc-convert-string-to-upperlower-case/ + std::transform(Pattern.begin(), Pattern.end(),Pattern.begin(), ::tolower); + std::transform(Key.begin() , Key.end() ,Key.begin() , ::tolower); + std::transform(Value.begin() , Value.end() ,Value.begin() , ::tolower); + } + bPrint = Key.find(Pattern) != std::string::npos || Value.find(Pattern) != std::string::npos; +#endif #endif } return bPrint; @@ -504,6 +518,8 @@ void Exiv2::dumpLibraryInfo(std::ostream& os,const exv_grep_keys_t& keys) output(os,keys,"bits" , bits ); output(os,keys,"dll" , dll ); output(os,keys,"debug" , debug ); + output(os,keys,"cplusplus" , __cplusplus); + output(os,keys,"cplusplus11" , __cplusplus >= CPLUSPLUS11 ); output(os,keys,"version" , __VERSION__); output(os,keys,"date" , __DATE__ ); output(os,keys,"time" , __TIME__ ); diff --git a/test/bugfixes-test.sh b/test/bugfixes-test.sh index bd11b8b9..b799a305 100755 --- a/test/bugfixes-test.sh +++ b/test/bugfixes-test.sh @@ -332,6 +332,13 @@ source ./functions.source diff $diffargs $num-before.txt $num-after.txt > $num.txt diff $diffargs $num.txt $diffname + num=1024 + filename=exiv2-bug$num.exv + printf "$num " >&3 + echo '------>' Bug $num '<-------' >&2 + copyTestFile $filename + runTest exiv2 -pa --grep gpsl/i $filename + num=1026 filename=exiv2-bug$num.jpg printf "$num " >&3 @@ -430,7 +437,7 @@ source ./functions.source copyTestFile $filename runTest exiv2 -pv -g Lens $filename runTest exiv2 -pa -g Lens $filename - + num=1137 filename=exiv2-bug$num.jpg printf "$num " >&3 @@ -440,7 +447,7 @@ source ./functions.source runTest exiv2 -pa $filename runTest exiv2 -PkV --grep GPSL http://dev.exiv2.org/attachments/download/805/DSC_7154.jpg | runTest exiv2 -m- $filename runTest exiv2 -pa $filename - + ) 3>&1 > $results 2>&1 diff --git a/test/data/bugfixes-test.out b/test/data/bugfixes-test.out index 12149dc0ba198749c50702fccbd678e92709eeaa..e9ab4e85661ac32c649b6f97bff3f539b7478f31 100644 GIT binary patch delta 122 zcmaEGtYGTNf`%5x7N!>F7M2#)7Pc1l7LFFq7OocV7M>Q~7QPn#7J(MQ7NHj57LgXw z7O@ub7Ks+g7O5@L7w>VKDA?#i0N3R8$0R17zmvveYLt?iu3)TZVQ6kJ{k)Ql!{nB` VZd`^S0ZS7Db%pH*?@7;h0RY~OD4hTR delta 69 zcmbPwvf#n7f`%5x7N!>F7M2#)7Pc1l7LFFq7OocV7M>Q~7QPn#7J(MQ7NHj57LgXw Z7O@ub7Ks+g7O5@L7w>Indm!EK0svP07&QO@ diff --git a/test/data/exiv2-bug1024.exv b/test/data/exiv2-bug1024.exv new file mode 100644 index 0000000000000000000000000000000000000000..6d0edb24cde7357f19b9165aae08ecb57d85ce7c GIT binary patch literal 16003 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 literal 0 HcmV?d00001