TuttleOFX
1
|
00001 #include <sam/common/utility.hpp> 00002 #include <sam/common/options.hpp> 00003 00004 #include <tuttle/common/exceptions.hpp> 00005 00006 #include <boost/filesystem/operations.hpp> 00007 #include <boost/filesystem/exception.hpp> 00008 #include <boost/exception/diagnostic_information.hpp> 00009 #include <boost/algorithm/string.hpp> 00010 #include <boost/algorithm/string/split.hpp> 00011 #include <boost/foreach.hpp> 00012 #include <boost/program_options.hpp> 00013 00014 #include <Sequence.hpp> 00015 00016 #include <algorithm> 00017 00018 #ifndef SAM_MOVEFILES 00019 #define SAM_MV_OR_CP_OPTIONS "SAM_CP_OPTIONS" 00020 #define SAM_TOOL "sam-cp" 00021 #else 00022 #define SAM_MV_OR_CP_OPTIONS "SAM_MV_OPTIONS" 00023 #define SAM_TOOL "sam-mv" 00024 #endif 00025 00026 namespace bfs = boost::filesystem; 00027 namespace bpo = boost::program_options; 00028 namespace bal = boost::algorithm; 00029 00030 00031 void copy_sequence( const sequenceParser::Sequence& s, const sequenceParser::Time firstImage, const sequenceParser::Time lastImage, const sequenceParser::Sequence& d, int offset = 0 ) 00032 { 00033 sequenceParser::Time begin; 00034 sequenceParser::Time end; 00035 sequenceParser::Time step; 00036 if (offset > 0) { 00037 begin = lastImage; 00038 end = firstImage; 00039 step = -s.getStep(); 00040 } else { 00041 begin = firstImage; 00042 end = lastImage; 00043 step = s.getStep(); 00044 } 00045 00046 for( sequenceParser::Time t = begin; 00047 (offset > 0) ? (t >= end) : (t <= end); 00048 t += step ) 00049 { 00050 bfs::path sFile = s.getAbsoluteFilenameAt(t); 00051 //TUTTLE_TLOG_VAR( TUTTLE_TRACE, sFile ); 00052 if( bfs::exists( sFile) ) 00053 { 00054 bfs::path dFile = d.getAbsoluteFilenameAt(t + offset); 00055 //TUTTLE_TLOG( TUTTLE_TRACE, "do " << sFile << " -> " << dFile ); 00056 #ifndef SAM_MOVEFILES // copy file(s) 00057 if( bfs::exists(dFile) ) 00058 { 00059 TUTTLE_LOG_ERROR( "Could not copy: " << dFile.string( ) ); 00060 } 00061 else 00062 { 00063 try 00064 { 00065 //TUTTLE_LOG_TRACE( "copy " << sFile << " -> " << dFile ); 00066 bfs::copy_file(sFile, dFile); 00067 } 00068 catch (const bpo::error& e) 00069 { 00070 TUTTLE_LOG_ERROR( "error : " << e.what() ); 00071 } 00072 catch (...) 00073 { 00074 TUTTLE_LOG_ERROR( boost::current_exception_diagnostic_information( ) ); 00075 } 00076 } 00077 #else // move file(s) 00078 if( bfs::exists( dFile ) ) 00079 { 00080 TUTTLE_LOG_ERROR( "Could not move: " << dFile.string( ) ); 00081 } 00082 else 00083 { 00084 try 00085 { 00086 //TUTTLE_LOG_TRACE( "move " << sFile << " -> " << dFile ); 00087 bfs::rename( sFile, dFile ); 00088 } 00089 catch( const bpo::error& e ) 00090 { 00091 TUTTLE_LOG_ERROR( "error : " << e.what() ); 00092 } 00093 catch( ... ) 00094 { 00095 TUTTLE_LOG_ERROR( boost::current_exception_diagnostic_information( ) ); 00096 } 00097 } 00098 #endif 00099 } 00100 } 00101 } 00102 00103 void copy_sequence( const sequenceParser::Sequence& s, const sequenceParser::Sequence& d, const sequenceParser::Time offset = 0 ) 00104 { 00105 copy_sequence(s, s.getFirstTime(), s.getLastTime(), d, offset); 00106 } 00107 00108 void copy_sequence( const sequenceParser::Sequence& s, const sequenceParser::Time firstImage, const sequenceParser::Time lastImage, const bfs::path& d, const sequenceParser::Time offset = 0 ) 00109 { 00110 sequenceParser::Sequence dSeq( s ); // create dst from src 00111 dSeq.setDirectory( d ); // modify path 00112 copy_sequence( s, firstImage, lastImage, dSeq, offset ); 00113 } 00114 00115 void copy_sequence( const sequenceParser::Sequence& s, const bfs::path& d, const sequenceParser::Time offset = 0 ) 00116 { 00117 copy_sequence( s, s.getFirstTime(), s.getLastTime(), d, offset ); 00118 } 00119 00120 int sammvcp(int argc, char** argv) 00121 { 00122 signal(SIGINT, signal_callback_handler); 00123 00124 using namespace tuttle::common; 00125 using namespace sam; 00126 00127 boost::shared_ptr<formatters::Formatter> formatter( formatters::Formatter::get() ); 00128 boost::shared_ptr<Color> color( Color::get() ); 00129 00130 sequenceParser::EMaskOptions descriptionMask = sequenceParser::eMaskOptionsNone; // by default show nothing 00131 std::vector<std::string> paths; 00132 std::vector<std::string> filters; 00133 00134 std::ssize_t offset = 0; 00135 std::ssize_t inputFirst = 0; 00136 std::ssize_t inputLast = 0; 00137 std::ssize_t outputFirst = 0; 00138 std::ssize_t outputLast = 0; 00139 00140 bool dstIsSeq = false; 00141 bool hasInputFirst = false; 00142 bool hasInputLast = false; 00143 00144 00145 typedef enum { 00146 eOffsetModeNotSet, 00147 eOffsetModeValue, 00148 eOffsetModeFirstTime, 00149 eOffsetModeLastTime, 00150 } EOffsetMode; 00151 EOffsetMode offsetMode = eOffsetModeNotSet; 00152 00153 formatter->init_logging(); 00154 00155 // Declare the supported options. 00156 bpo::options_description mainOptions; 00157 mainOptions.add_options() 00158 ( kHelpOptionString, kHelpOptionMessage ) 00159 ( kOffsetOptionString, bpo::value<std::ssize_t>(), kOffsetOptionMessage ) 00160 // ( "force,f" , bpo::value<bool>( ) , "if a destination file exists, replace it" ) 00161 ( kVerboseOptionString, bpo::value<int>()->default_value( 2 ), kVerboseOptionMessage ) 00162 ( kQuietOptionString, kQuietOptionMessage ) 00163 ( kInputFirstOptionString, bpo::value<std::ssize_t>(), kInputFirstOptionMessage ) 00164 ( kInputLastOptionString, bpo::value<std::ssize_t>(), kInputLastOptionMessage ) 00165 ( kOutputFirstOptionString, bpo::value<std::ssize_t>(), kOutputFirstOptionMessage ) 00166 ( kOutputLastOptionString, bpo::value<std::ssize_t>(), kOutputLastOptionMessage ) 00167 ( kColorOptionString, kColorOptionMessage ) 00168 ( kBriefOptionString, kBriefOptionMessage ); 00169 00170 // describe hidden options 00171 bpo::options_description hidden; 00172 hidden.add_options() 00173 ( kInputOptionString, bpo::value<std::vector<std::string> >(), kInputOptionMessage ) 00174 ( kEnableColorOptionString, bpo::value<std::string>(), kEnableColorOptionMessage ); 00175 00176 // define default options 00177 bpo::positional_options_description pod; 00178 pod.add( kInputOptionLongName, -1 ); 00179 00180 bpo::options_description cmdline_options; 00181 cmdline_options.add( mainOptions ).add( hidden ); 00182 00183 //parse the command line, and put the result in vm 00184 bpo::variables_map vm; 00185 try 00186 { 00187 bpo::store( bpo::command_line_parser( argc, argv ).options( cmdline_options ).positional( pod ).run(), vm ); 00188 00189 // get environnement options and parse them 00190 if( const char* env_options = std::getenv( SAM_MV_OR_CP_OPTIONS ) ) 00191 { 00192 const std::vector<std::string> vecOptions = bpo::split_unix( env_options, " " ); 00193 bpo::store(bpo::command_line_parser( vecOptions ).options( cmdline_options ).positional( pod ).run(), vm ); 00194 } 00195 if( const char* env_options = std::getenv( "SAM_OPTIONS" ) ) 00196 { 00197 const std::vector<std::string> vecOptions = bpo::split_unix( env_options, " " ); 00198 bpo::store(bpo::command_line_parser( vecOptions ).options(cmdline_options ).positional( pod ).run(), vm ); 00199 } 00200 bpo::notify(vm); 00201 } 00202 catch( const bpo::error& e ) 00203 { 00204 TUTTLE_LOG_ERROR( SAM_TOOL ": command line error: " << e.what() ); 00205 exit(254); 00206 } 00207 catch( ... ) 00208 { 00209 TUTTLE_LOG_ERROR( SAM_TOOL ": unknown error in command line." ); 00210 exit(254); 00211 } 00212 00213 if( vm.count( kColorOptionLongName ) ) 00214 { 00215 color->enable(); 00216 } 00217 00218 if( vm.count( kEnableColorOptionLongName ) ) 00219 { 00220 const std::string str = vm[kEnableColorOptionLongName].as<std::string>(); 00221 if( string_to_boolean(str) ) 00222 { 00223 color->enable(); 00224 } 00225 else 00226 { 00227 color->disable(); 00228 } 00229 } 00230 00231 // defines paths 00232 if( vm.count( kInputOptionLongName ) ) 00233 { 00234 paths = vm[ kInputOptionLongName ].as<std::vector<std::string> >(); 00235 } 00236 00237 00238 if( vm.count( kBriefOptionLongName ) ) 00239 { 00240 #ifndef SAM_MOVEFILES 00241 TUTTLE_LOG_INFO( color->_green << "copy sequence(s) in a directory" << color->_std); 00242 #else 00243 TUTTLE_LOG_INFO( color->_green << "move sequence(s) in a directory" << color->_std ); 00244 #endif 00245 return 0; 00246 } 00247 00248 bool isPathSizeTooSmall = ( paths.size() < 2 ); 00249 if( vm.count( kHelpOptionLongName ) || isPathSizeTooSmall ) 00250 { 00251 if( isPathSizeTooSmall && !vm.count( kHelpOptionLongName ) ) 00252 TUTTLE_LOG_ERROR( "Two sequences and/or directories must be specified." ); 00253 00254 TUTTLE_LOG_INFO( color->_blue << "TuttleOFX project [" << kUrlTuttleofxProject << "]" << color->_std ); 00255 TUTTLE_LOG_INFO( "" ); 00256 #ifndef SAM_MOVEFILES 00257 TUTTLE_LOG_INFO( color->_blue <<"NAME" << color->_std ); 00258 TUTTLE_LOG_INFO( color->_green << "\tsam-cp - copy sequence(s) in a directory" << color->_std ); 00259 TUTTLE_LOG_INFO( "" ); 00260 TUTTLE_LOG_INFO( color->_blue << "SYNOPSIS" << color->_std ); 00261 TUTTLE_LOG_INFO( color->_green << "\tsam-cp [options] sequence[s] [outputDirectory][outputSequence]" << color->_std ); 00262 #else 00263 TUTTLE_LOG_INFO( color->_blue << "NAME" << color->_std ); 00264 TUTTLE_LOG_INFO( color->_green << "\tsam-mv - move sequence(s) in a directory" << color->_std); 00265 TUTTLE_LOG_INFO( "" ); 00266 TUTTLE_LOG_INFO( color->_blue << "SYNOPSIS" << color->_std ); 00267 TUTTLE_LOG_INFO( color->_green << "\tsam-mv [options] sequence[s] [outputDirectory][outputSequence]" << color->_std ); 00268 #endif 00269 TUTTLE_LOG_INFO( "" ); 00270 TUTTLE_LOG_INFO( color->_blue << "DESCRIPTION" << color->_std ); 00271 TUTTLE_LOG_INFO( "" ); 00272 #ifndef SAM_MOVEFILES 00273 TUTTLE_LOG_INFO( "Copy sequence of image files, and could remove trees (folder, files and sequences)." ); 00274 #else 00275 TUTTLE_LOG_INFO( "Move sequence of image files, and could remove trees (folder, files and sequences)." ); 00276 #endif 00277 TUTTLE_LOG_INFO( "" ); 00278 TUTTLE_LOG_INFO( color->_blue << "OPTIONS" << color->_std ); 00279 TUTTLE_LOG_INFO( mainOptions ); 00280 /////Examples 00281 00282 TUTTLE_LOG_INFO( color->_blue << "EXAMPLES" << color->_std << std::left ); 00283 SAM_EXAMPLE_TITLE_COUT( "Sequence possible definitions: " ); 00284 SAM_EXAMPLE_LINE_COUT ( "Auto-detect padding : ", "seq.@.jpg" ); 00285 SAM_EXAMPLE_LINE_COUT ( "Padding of 8 (usual style): ", "seq.########.jpg" ); 00286 SAM_EXAMPLE_LINE_COUT ( "Padding of 8 (printf style): ", "seq.%08d.jpg" ); 00287 #ifndef SAM_MOVEFILES 00288 SAM_EXAMPLE_TITLE_COUT( "Copy a sequence: " ); 00289 SAM_EXAMPLE_LINE_COUT ( "", "sam-cp /path/to/sequence/seq.@.jpg /path/to/sequence_copy/" ); 00290 SAM_EXAMPLE_LINE_COUT ( "", "sam-cp /path/to/sequence/seq.@.jpg /path/to/sequences_copy/seq.@.jpg" ); 00291 SAM_EXAMPLE_TITLE_COUT( "Copy and rename a sequence: " ); 00292 SAM_EXAMPLE_LINE_COUT ( "", "sam-cp /path/to/sequence/seq.@.jpg /path/to/sequence_copy/seq_copy.@.jpg " ); 00293 SAM_EXAMPLE_TITLE_COUT( "Copy a part of sequence: " ); 00294 SAM_EXAMPLE_LINE_COUT ( "", "sam-cp /path/to/sequence/seq.@.jpg /path/to/sequence_copy/ --input-first 677837 --input-last 677838" ); 00295 SAM_EXAMPLE_TITLE_COUT( "Renumber a sequence: " ); 00296 SAM_EXAMPLE_LINE_COUT ( "", "sam-cp /path/to/sequence/seq.@.jpg /path/to/sequence_copy/ --output-first 0" ); 00297 #else 00298 SAM_EXAMPLE_TITLE_COUT( "Move a sequence: "); 00299 SAM_EXAMPLE_LINE_COUT ( "", "sam-mv /path/to/sequence/seq.@.jpg /path/to/sequence_move/" ); 00300 SAM_EXAMPLE_LINE_COUT ( "", "sam-mv /path/to/sequence/seq.@.jpg /path/to/sequences_move/seq.@.jpg" ); 00301 SAM_EXAMPLE_TITLE_COUT( "Move and rename a sequence:" ); 00302 SAM_EXAMPLE_LINE_COUT ( "", "sam-mv /path/to/sequence/seq.@.jpg /path/to/sequence_move/seq_move.@.jpg" ); 00303 SAM_EXAMPLE_TITLE_COUT( "Move a part of sequence:" ); 00304 SAM_EXAMPLE_LINE_COUT ( "", "sam-mv /path/to/sequence/seq.@.jpg /path/to/sequence_move/ --input-first 677837 --input-last 677838" ); 00305 SAM_EXAMPLE_TITLE_COUT( "Renumber a sequence:" ); 00306 SAM_EXAMPLE_LINE_COUT ( "", "sam-mv /path/to/sequence/seq.@.jpg /path/to/sequence_move/ --output-first 0" ); 00307 00308 #endif 00309 return 1; 00310 } 00311 00312 if( vm.count(kExpressionOptionLongName) ) 00313 { 00314 bal::split(filters, vm["expression"].as<std::string>(), bal::is_any_of(",")); 00315 } 00316 00317 if( vm.count(kAllOptionLongName) ) 00318 { 00319 // add .* files 00320 descriptionMask |= sequenceParser::eMaskOptionsDotFile; 00321 } 00322 00323 if( vm.count(kOffsetOptionLongName) ) 00324 { 00325 offset = vm[kOffsetOptionLongName].as<std::ssize_t>(); 00326 offsetMode = eOffsetModeValue; 00327 } 00328 00329 if( vm.count(kInputFirstOptionLongName) ) 00330 { 00331 hasInputFirst = true; 00332 inputFirst = vm[kInputFirstOptionLongName].as<std::ssize_t>(); 00333 } 00334 00335 if( vm.count(kInputLastOptionLongName) ) 00336 { 00337 hasInputLast = true; 00338 inputLast = vm[kInputLastOptionLongName].as<std::ssize_t>(); 00339 } 00340 00341 if( vm.count(kOutputFirstOptionLongName) ) 00342 { 00343 outputFirst = vm[kOutputFirstOptionLongName].as<std::ssize_t>(); 00344 if( offsetMode != eOffsetModeNotSet ) 00345 { 00346 TUTTLE_LOG_ERROR( "You can't cumulate multiple options to modify the time." ); 00347 return 255; 00348 } 00349 offsetMode = eOffsetModeFirstTime; 00350 } 00351 00352 if( vm.count(kOutputLastOptionLongName) ) 00353 { 00354 outputLast = vm[kOutputLastOptionLongName].as<std::ssize_t>(); 00355 if (offsetMode != eOffsetModeNotSet) 00356 { 00357 TUTTLE_LOG_ERROR( "You can't cumulate multiple options to modify the time." ); 00358 return 255; 00359 } 00360 offsetMode = eOffsetModeLastTime; 00361 } 00362 00363 switch( vm[ kVerboseOptionLongName ].as< int >() ) 00364 { 00365 case 0 : formatter->setLogLevel( boost::log::trivial::trace ); break; 00366 case 1 : formatter->setLogLevel( boost::log::trivial::debug ); break; 00367 case 2 : formatter->setLogLevel( boost::log::trivial::info ); break; 00368 case 3 : formatter->setLogLevel( boost::log::trivial::warning ); break; 00369 case 4 : formatter->setLogLevel( boost::log::trivial::error ); break; 00370 case 5 : formatter->setLogLevel( boost::log::trivial::fatal ); break; 00371 default : formatter->setLogLevel( boost::log::trivial::warning ); break; 00372 } 00373 if( vm.count(kQuietOptionLongName) ) 00374 { 00375 formatter->setLogLevel( boost::log::trivial::fatal ); 00376 } 00377 00378 bfs::path dstPath = paths.back(); 00379 paths.pop_back(); 00380 std::string sequencePattern; 00381 00382 if( !bfs::is_directory( dstPath ) ) 00383 { 00384 sequencePattern = dstPath.filename().string(); 00385 dstPath = dstPath.parent_path(); 00386 00387 if( ! dstPath.empty() && !bfs::is_directory( dstPath ) ) 00388 { 00389 TUTTLE_LOG_ERROR( "Your destination is not in a valid directory: " << tuttle::quotes( dstPath.string() ) << "." ); 00390 return 255; 00391 } 00392 } 00393 else 00394 { 00395 if( paths.size() > 1 ) 00396 { 00397 TUTTLE_LOG_ERROR( "To copy multiple sequences, your destination must be a directory: " << tuttle::quotes( dstPath.string() ) << "." ); 00398 return 255; 00399 } 00400 sequencePattern = ""; 00401 } 00402 00403 sequenceParser::Sequence dstSeq( dstPath, descriptionMask ); 00404 00405 if( sequencePattern.size() > 0 ) 00406 { 00407 dstIsSeq = dstSeq.initFromPattern( dstPath, sequencePattern, 0, 0, 1, descriptionMask, sequenceParser::Sequence::ePatternAll ); 00408 if( !dstIsSeq ) // there is a pattern, but it's not valid. 00409 { 00410 TUTTLE_LOG_ERROR("Your destination " << tuttle::quotes(sequencePattern) << " is not a valid pattern. Your destination can be a directory or a pattern." ); 00411 return 255; 00412 } 00413 } 00414 00415 try 00416 { 00417 BOOST_FOREACH( const bfs::path& srcPath, paths ) 00418 { 00419 sequenceParser::Sequence srcSeq( srcPath.branch_path(), descriptionMask ); 00420 const bool srcIsSeq = srcSeq.initFromDetection( srcPath.string(), sequenceParser::Sequence::ePatternDefault ); 00421 if( ! srcIsSeq ) 00422 { 00423 TUTTLE_LOG_ERROR( color->_error << "Input is not a sequence: " << tuttle::quotes( srcPath.string() ) << "." << color->_std ); 00424 return 255; 00425 } 00426 if( srcSeq.getNbFiles() == 0 ) 00427 { 00428 TUTTLE_LOG_ERROR( color->_error << "No existing file for the input sequence: " << tuttle::quotes( srcPath.string() ) << "." << color->_std ); 00429 return 255; 00430 } 00431 00432 sequenceParser::Time first = hasInputFirst ? inputFirst : srcSeq.getFirstTime(); 00433 sequenceParser::Time last = hasInputLast ? inputLast : srcSeq.getLastTime(); 00434 switch( offsetMode ) 00435 { 00436 case eOffsetModeNotSet: 00437 { 00438 offset = 0; 00439 break; 00440 } 00441 case eOffsetModeValue: 00442 { 00443 // set by "offset" command line option 00444 break; 00445 } 00446 case eOffsetModeFirstTime: 00447 { 00448 // set by "input-first" command line option 00449 offset = outputFirst - first; 00450 break; 00451 } 00452 case eOffsetModeLastTime: 00453 { 00454 // set by "input-last" command line option 00455 offset = outputLast - last; 00456 break; 00457 } 00458 } 00459 if( dstIsSeq ) 00460 { 00461 TUTTLE_LOG_TRACE( srcSeq.getAbsoluteStandardPattern( ) << " -> " << dstSeq.getAbsoluteStandardPattern( ) << " (" << srcSeq.getNbFiles( ) << ") " ); 00462 copy_sequence( srcSeq, first, last, dstSeq, offset ); 00463 } 00464 else 00465 { 00466 TUTTLE_LOG_TRACE( srcSeq.getAbsoluteStandardPattern( ) << " -> " << dstPath / srcSeq.getStandardPattern( ) << " (" << srcSeq.getNbFiles( ) << ")" ); 00467 copy_sequence( srcSeq, first, last, dstPath, offset ); 00468 } 00469 } 00470 } 00471 catch( bfs::filesystem_error &ex ) 00472 { 00473 TUTTLE_LOG_ERROR( ex.what( ) ); 00474 return 254; 00475 } 00476 catch(...) 00477 { 00478 TUTTLE_LOG_ERROR( boost::current_exception_diagnostic_information( ) ); 00479 return 253; 00480 } 00481 00482 return 0; 00483 } 00484