TuttleOFX  1
mvcp.hpp
Go to the documentation of this file.
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