TuttleOFX  1
main.cpp
Go to the documentation of this file.
00001 #include <sam/common/utility.hpp>
00002 #include <sam/common/options.hpp>
00003 
00004 #include <boost/filesystem/operations.hpp>
00005 #include <boost/filesystem/exception.hpp>
00006 #include <boost/exception/diagnostic_information.hpp>
00007 #include <boost/foreach.hpp>
00008 #include <boost/algorithm/string.hpp>
00009 #include <boost/algorithm/string/split.hpp>
00010 #include <boost/program_options.hpp>
00011 #include <boost/shared_ptr.hpp>
00012 
00013 #include <detector.hpp>
00014 #include <Sequence.hpp>
00015 
00016 #include <algorithm>
00017 #include <iterator>
00018 
00019 namespace bpo = boost::program_options;
00020 namespace bfs = boost::filesystem;
00021 namespace bal = boost::algorithm;
00022 
00023 bool         selectRange    = false;
00024 std::ssize_t firstImage     = 0;
00025 std::ssize_t lastImage      = 0;
00026 
00027 // A helper function to simplify the main part.
00028 template<class T>
00029 std::ostream& operator<<(std::ostream& os, const std::vector<T>& v)
00030 {
00031         copy( v.begin(), v.end(), std::ostream_iterator<T>( std::cout, " " ) );
00032         return os;
00033 }
00034 
00035 void removeSequence( const sequenceParser::Sequence& s )
00036 {
00037         std::ssize_t first;
00038         std::ssize_t last;
00039         if( selectRange )
00040         {
00041                 first = std::max( s.getFirstTime(), firstImage );
00042                 last  = std::min( s.getLastTime(), lastImage );
00043         }
00044         else
00045         {
00046                 first = s.getFirstTime();
00047                 last  = s.getLastTime();
00048         }
00049 //      TUTTLE_TCOUT( "remove sequence." );
00050 //      TUTTLE_TCOUT("remove from " << first << " to " << last);
00051 
00052         for( sequenceParser::Time t = first; t <= last; t += s.getStep() )
00053         {
00054                 bfs::path sFile = s.getAbsoluteFilenameAt(t);
00055                 if( !bfs::exists( sFile ) )
00056                 {
00057                         TUTTLE_LOG_ERROR( "Could not remove (file not exist): " << sFile.string() );
00058                 }
00059                 else
00060                 {
00061                         TUTTLE_LOG_TRACE( "remove: " << tuttle::common::Color::get()->_folder << sFile.string() << tuttle::common::Color::get()->_std );
00062                         try
00063                         {
00064                                 bfs::remove( sFile );
00065                         }
00066                         catch( const boost::filesystem::filesystem_error& e )
00067                         {
00068 //                         if( e.code() == boost::system::errc::permission_denied )
00069 //                                 "permission denied"
00070                                 TUTTLE_LOG_ERROR( "sam-rm: Error:\t\n" << e.what() );
00071                                 /// @todo cin
00072 //                              TUTTLE_LOG_INFO( "sam-rm: Continue ? (Yes/No/Yes for All/No for All)" );
00073                         }
00074                 }
00075         }
00076 }
00077 
00078 void removeFileObject( boost::ptr_vector<sequenceParser::FileObject> &listing, std::vector<boost::filesystem::path> &notRemoved )
00079 {
00080 //      TUTTLE_TCOUT( "removeFileObject." );
00081         BOOST_FOREACH( const sequenceParser::FileObject& s, listing )
00082         {
00083                 if( !(s.getMaskType () == sequenceParser::eMaskTypeDirectory))
00084                 {
00085                         TUTTLE_LOG_TRACE( "remove: " << s );
00086                         if( s.getMaskType () == sequenceParser::eMaskTypeSequence )
00087                                 removeSequence( static_cast<const sequenceParser::Sequence&>( s ) );
00088                         else
00089                         {
00090                                 std::vector<bfs::path> paths = s.getFiles();
00091                                 for(unsigned int i=0; i<paths.size(); i++)
00092                                         bfs::remove( paths.at(i) );
00093                         }
00094                 }
00095                 else // is a directory
00096                 {
00097                         std::vector<boost::filesystem::path> paths = s.getFiles();
00098                         for(unsigned int i=0; i<paths.size(); i++)
00099                         {
00100                                 if( bfs::is_empty( paths.at(i) ) )
00101                                 {
00102                                         TUTTLE_LOG_TRACE( "remove: " << s );
00103                                         bfs::remove( paths.at(i) );
00104                                 }
00105                                 else
00106                                 {
00107                                         notRemoved.push_back( paths.at(i) );
00108                                 }
00109                         }
00110                 }
00111         }
00112 }
00113 
00114 void removeFiles( std::vector<boost::filesystem::path> &listing )
00115 {
00116         std::sort(listing.begin(), listing.end());
00117         std::reverse(listing.begin(), listing.end());
00118         BOOST_FOREACH( const boost::filesystem::path& paths, listing )
00119         {
00120                 if( bfs::is_empty( paths ) )
00121                 {
00122                         TUTTLE_LOG_TRACE( "remove: " << tuttle::common::Color::get()->_folder << paths << tuttle::common::Color::get()->_std );
00123                         bfs::remove( paths );
00124                 }
00125                 else
00126                 {
00127                         TUTTLE_LOG_ERROR( "could not remove " << paths );
00128                 }
00129         }
00130 }
00131 
00132 
00133 int main( int argc, char** argv )
00134 {
00135         signal(SIGINT, signal_callback_handler);
00136 
00137         using namespace tuttle::common;
00138         using namespace sam;
00139         
00140         boost::shared_ptr<formatters::Formatter> formatter( formatters::Formatter::get() );
00141         boost::shared_ptr<Color>                 color( Color::get() );
00142 
00143         sequenceParser::EMaskType                researchMask      = sequenceParser::eMaskTypeSequence; // by default show sequences
00144         sequenceParser::EMaskOptions             descriptionMask   = sequenceParser::eMaskOptionsColor; // by default show nothing
00145         bool                         recursiveListing  = false;
00146         std::vector<std::string>     paths;
00147         std::vector<std::string>     filters;
00148 
00149         formatter->init_logging();
00150         
00151         // Declare the supported options.
00152         bpo::options_description mainOptions;
00153         mainOptions.add_options()
00154                         ( kAllOptionString,         kAllOptionMessage )
00155                         ( kDirectoriesOptionString, kDirectoriesOptionMessage )
00156                         ( kExpressionOptionString,  bpo::value<std::string>(), kExpressionOptionMessage )
00157                         ( kFilesOptionString,       kFilesOptionMessage )
00158                         ( kHelpOptionString,        kHelpOptionMessage )
00159                         ( kIgnoreOptionString,      kIgnoreOptionMessage )
00160                         ( kPathOptionString,        kPathOptionMessage )
00161                         ( kRecursiveOptionString,   kRecursiveOptionMessage )
00162                         ( kVerboseOptionString,     bpo::value<int>()->default_value( 2 ), kVerboseOptionMessage )
00163                         ( kQuietOptionString,       kQuietOptionMessage )
00164                         ( kColorOptionString,       kColorOptionMessage )
00165                         ( kFirstImageOptionString,  bpo::value<std::ssize_t>(), kFirstImageOptionMessage )
00166                         ( kLastImageOptionString,   bpo::value<std::ssize_t>(), kLastImageOptionMessage )
00167                         ( kFullRMPathOptionString,  kFullRMPathOptionMessage )
00168                         ( kBriefOptionString,       kBriefOptionMessage );
00169         
00170         // describe hidden options
00171         bpo::options_description hidden;
00172         hidden.add_options()
00173                         ( kInputDirOptionString, bpo::value< std::vector<std::string> >(), kInputDirOptionMessage )
00174                         ( kEnableColorOptionString, bpo::value<std::string>(), kEnableColorOptionMessage );
00175         
00176         // define default options 
00177         bpo::positional_options_description pod;
00178         pod.add(kInputDirOptionString, -1);
00179         
00180         bpo::options_description cmdline_options;
00181         cmdline_options.add( mainOptions ).add( hidden );
00182 
00183         bpo::positional_options_description pd;
00184         pd.add( "", -1 );
00185         
00186         //parse the command line, and put the result in vm
00187         bpo::variables_map vm;
00188 
00189         bpo::notify( vm );
00190 
00191         try
00192         {
00193                 bpo::store( bpo::command_line_parser(argc, argv).options(cmdline_options).positional(pod).run(), vm );
00194 
00195                 // get environment options and parse them
00196                 if( const char* env_rm_options = std::getenv("SAM_RM_OPTIONS") )
00197                 {
00198                         const std::vector<std::string> vecOptions = bpo::split_unix( env_rm_options, " " );
00199                         bpo::store( bpo::command_line_parser(vecOptions).options(cmdline_options).positional(pod).run(), vm );
00200                 }
00201                 if( const char* env_rm_options = std::getenv("SAM_OPTIONS") )
00202                 {
00203                         const std::vector<std::string> vecOptions = bpo::split_unix( env_rm_options, " " );
00204                         bpo::store( bpo::command_line_parser(vecOptions).options(cmdline_options).positional(pod).run(), vm );
00205                 }
00206         }
00207         catch( const bpo::error& e)
00208         {
00209                 TUTTLE_LOG_ERROR( "sam-rm: command line error: " << e.what() );
00210                 exit( 254 );
00211         }
00212         catch(...)
00213         {
00214                 TUTTLE_LOG_ERROR( "sam-rm: unknown error in command line." );
00215                 exit( 254 );
00216         }
00217 
00218         if( vm.count( kColorOptionLongName ) )
00219         {
00220                 color->enable();
00221         }
00222         
00223         if( vm.count( kEnableColorOptionLongName ) )
00224         {
00225                 const std::string str = vm[kEnableColorOptionLongName].as<std::string>();
00226                 if( string_to_boolean(str) )
00227                 {
00228                         color->enable();
00229                 }
00230                 else
00231                 {
00232                         color->disable();
00233                 }
00234         }
00235 
00236         if( vm.count( kHelpOptionLongName ) )
00237         {
00238                 TUTTLE_LOG_INFO( color->_blue  << "TuttleOFX project [" << kUrlTuttleofxProject << "]" << color->_std );
00239                 TUTTLE_LOG_INFO( "" );
00240                 TUTTLE_LOG_INFO( color->_blue  << "NAME" << color->_std );
00241                 TUTTLE_LOG_INFO( color->_green << "\tsam-rm - remove file sequences" << color->_std );
00242                 TUTTLE_LOG_INFO( "" );
00243                 TUTTLE_LOG_INFO( color->_blue  << "SYNOPSIS" << color->_std );
00244                 TUTTLE_LOG_INFO( color->_green << "\tsam-rm [options] [sequence_pattern]" << color->_std );
00245                 TUTTLE_LOG_INFO( "" );
00246                 TUTTLE_LOG_INFO( color->_blue  << "DESCRIPTION" << color->_std << std::endl );
00247                 TUTTLE_LOG_INFO( "" );
00248                 TUTTLE_LOG_INFO( "Remove sequence of files, and could remove trees (folder, files and sequences)." );
00249                 TUTTLE_LOG_INFO( "" );
00250                 TUTTLE_LOG_INFO( color->_blue  << "OPTIONS" << color->_std );
00251                 TUTTLE_LOG_INFO( "" );
00252                 TUTTLE_LOG_INFO( mainOptions );
00253 
00254                 TUTTLE_LOG_INFO( color->_blue << "EXAMPLES" << color->_std );
00255                 SAM_EXAMPLE_TITLE_COUT( "Sequence possible definitions: " );
00256                 SAM_EXAMPLE_LINE_COUT ( "Auto-detect padding : ", "seq.@.jpg" );
00257                 SAM_EXAMPLE_LINE_COUT ( "Padding of 8 (usual style): ", "seq.########.jpg" );
00258                 SAM_EXAMPLE_LINE_COUT ( "Padding of 8 (printf style): ", "seq.%08d.jpg" );
00259                 SAM_EXAMPLE_TITLE_COUT( "Delete: " );
00260                 SAM_EXAMPLE_LINE_COUT ( "A sequence:", "sam-rm /path/to/sequence/seq.@.jpg" );
00261                 SAM_EXAMPLE_LINE_COUT ( "Sequences in a directory:", "sam-rm /path/to/sequence/" );
00262                 TUTTLE_LOG_INFO( "" );
00263 
00264                 return 0;
00265         }
00266 
00267         if( vm.count( kBriefOptionLongName) )
00268         {
00269                 TUTTLE_LOG_INFO( color->_green << "remove file sequences" << color->_std );
00270                 return 0;
00271         }
00272 
00273         if(vm.count( kExpressionOptionLongName))
00274         {
00275                 bal::split( filters, vm["expression"].as<std::string>(), bal::is_any_of(","));
00276         }
00277 
00278         if( vm.count( kDirectoriesOptionLongName ) )
00279         {
00280                 researchMask |= sequenceParser::eMaskTypeDirectory;
00281         }
00282         
00283         if( vm.count( kFilesOptionLongName ) )
00284         {
00285                 researchMask |= sequenceParser::eMaskTypeFile;
00286         }
00287         
00288         if( vm.count( kIgnoreOptionLongName ) )
00289         {
00290                 researchMask &= ~sequenceParser::eMaskTypeSequence;
00291         }
00292         
00293         switch( vm[ kVerboseOptionLongName ].as< int >() )
00294         {
00295                 case 0 :  formatter->setLogLevel( boost::log::trivial::trace   ); break;
00296                 case 1 :  formatter->setLogLevel( boost::log::trivial::debug   ); break;
00297                 case 2 :  formatter->setLogLevel( boost::log::trivial::info    ); break;
00298                 case 3 :  formatter->setLogLevel( boost::log::trivial::warning ); break;
00299                 case 4 :  formatter->setLogLevel( boost::log::trivial::error   ); break;
00300                 case 5 :  formatter->setLogLevel( boost::log::trivial::fatal   ); break;
00301                 default : formatter->setLogLevel( boost::log::trivial::warning ); break;
00302         }
00303         if( vm.count(kQuietOptionLongName) )
00304         {
00305                 formatter->setLogLevel( boost::log::trivial::fatal );
00306         }
00307 
00308         if( vm.count( kFirstImageOptionLongName ) )
00309         {
00310                 selectRange = true;
00311                 firstImage  = vm[kFirstImageOptionLongName].as< std::ssize_t >();
00312         }
00313 
00314         if( vm.count( kLastImageOptionLongName ) )
00315         {
00316                 selectRange = true;
00317                 lastImage  = vm[kLastImageOptionLongName].as< std::ssize_t >();
00318         }
00319 
00320         if( vm.count( kFullRMPathOptionLongName ) )
00321         {
00322                 researchMask |= sequenceParser::eMaskTypeDirectory;
00323                 researchMask |= sequenceParser::eMaskTypeFile;
00324                 researchMask |= sequenceParser::eMaskTypeSequence;
00325         }
00326         
00327         if( vm.count( kAllOptionLongName ) )
00328         {
00329                 // add .* files
00330                 descriptionMask |= sequenceParser::eMaskOptionsDotFile;
00331         }
00332         
00333         if( vm.count( kPathOptionLongName ) )
00334         {
00335                 descriptionMask |= sequenceParser::eMaskOptionsPath;
00336         }
00337         
00338         // defines paths, but if no directory specify in command line, we add the current path
00339         if( vm.count( kInputDirOptionLongName ) )
00340         {
00341                 paths = vm[kInputDirOptionLongName].as< std::vector<std::string> >();
00342         }
00343         else
00344         {
00345                 TUTTLE_LOG_ERROR( "No sequence and/or directory are specified." );
00346                 return 1;
00347         }
00348 
00349         if( vm.count( kRecursiveOptionLongName ) )
00350         {
00351                 recursiveListing = true;
00352         }
00353         //      for(unsigned int i=0; i<filters.size(); i++)
00354         //      TUTTLE_LOG_TRACE("filters = " << filters.at(i));
00355         //      TUTTLE_LOG_TRACE("research mask = " << researchMask);
00356         //      TUTTLE_LOG_TRACE("options  mask = " << descriptionMask);
00357 
00358         try
00359         {
00360                 std::vector<boost::filesystem::path> pathsNoRemoved;
00361                 
00362                 BOOST_FOREACH( bfs::path path, paths )
00363                 {
00364 //                      TUTTLE_TCOUT( "path: "<< path );
00365                         if( bfs::exists( path ) )
00366                         {
00367                                 if( bfs::is_directory( path ) )
00368                                 {
00369                                         //TUTTLE_TCOUT( "is a directory" );
00370                                         if(recursiveListing)
00371                                         {
00372                                                 for ( bfs::recursive_directory_iterator end, dir(path); dir != end; ++dir )
00373                                                 {
00374                                                         if( bfs::is_directory( *dir ) )
00375                                                         {
00376                                                                 //TUTTLE_TCOUT( *dir );
00377                                                                 boost::ptr_vector<sequenceParser::FileObject> listing = sequenceParser::fileObjectInDirectory( ( (bfs::path) *dir ).string(), filters, researchMask, descriptionMask );
00378                                                                 removeFileObject( listing, pathsNoRemoved );
00379                                                         }
00380                                                 }
00381                                         }
00382                                         boost::ptr_vector<sequenceParser::FileObject> listing = sequenceParser::fileObjectInDirectory( path.string(), filters, researchMask, descriptionMask );
00383                                         removeFileObject( listing, pathsNoRemoved );
00384                                 }
00385                                 else
00386                                 {
00387                                         //TUTTLE_TCOUT( "is NOT a directory "<< path.branch_path() << " | "<< path.leaf() );
00388                                         filters.push_back( path.leaf().string() );
00389                                         boost::ptr_vector<sequenceParser::FileObject> listing = sequenceParser::fileObjectInDirectory( path.branch_path().string(), filters, researchMask, descriptionMask );
00390                                         removeFileObject( listing, pathsNoRemoved );
00391                                 }
00392                         }
00393                         else
00394                         {
00395 //                              TUTTLE_TCOUT( "not exist ...." );
00396                                 try
00397                                 {
00398                                         sequenceParser::Sequence s(path.branch_path(), descriptionMask );
00399                                         s.initFromDetection( path.string(), sequenceParser::Sequence::ePatternDefault );
00400                                         if( s.getNbFiles() )
00401                                         {
00402 //                                              TUTTLE_TCOUT( s );
00403                                                 removeSequence( s );
00404                                         }
00405                                 }
00406                                 catch(... )
00407                                 {
00408                                         TUTTLE_LOG_ERROR( "Unrecognized pattern \"" << path << "\"" );
00409                                 }
00410                         }
00411                 }
00412                 // delete not empty folder the first time
00413                 removeFiles( pathsNoRemoved );
00414         }
00415         catch( bfs::filesystem_error &ex )
00416         {
00417                 TUTTLE_LOG_ERROR( ex.what() );
00418         }
00419         catch( ... )
00420         {
00421                 TUTTLE_LOG_ERROR( boost::current_exception_diagnostic_information() );
00422         }
00423         return 0;
00424 }
00425