|
Packit |
01d647 |
// ***************************************************************** -*- C++ -*-
|
|
Packit |
01d647 |
/*
|
|
Packit |
01d647 |
* Copyright (C) 2009 Brad Schick <schickb@gmail.com>
|
|
Packit |
01d647 |
*
|
|
Packit |
01d647 |
* This file is part of the organize tool.
|
|
Packit |
01d647 |
*
|
|
Packit |
01d647 |
* This program is free software; you can redistribute it and/or
|
|
Packit |
01d647 |
* modify it under the terms of the GNU General Public License
|
|
Packit |
01d647 |
* as published by the Free Software Foundation; either version 2
|
|
Packit |
01d647 |
* of the License, or (at your option) any later version.
|
|
Packit |
01d647 |
*
|
|
Packit |
01d647 |
* This program is distributed in the hope that it will be useful,
|
|
Packit |
01d647 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
01d647 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
Packit |
01d647 |
* GNU General Public License for more details.
|
|
Packit |
01d647 |
*
|
|
Packit |
01d647 |
* You should have received a copy of the GNU General Public License
|
|
Packit |
01d647 |
* along with this program; if not, write to the Free Software
|
|
Packit |
01d647 |
* Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA.
|
|
Packit |
01d647 |
*/
|
|
Packit |
01d647 |
/*
|
|
Packit |
01d647 |
File: organize.cpp
|
|
Packit |
01d647 |
Author(s): Brad Schick (brad) <schickb@gmail.com>
|
|
Packit |
01d647 |
History: 19-Jan-09, brad: created
|
|
Packit |
01d647 |
*/
|
|
Packit |
01d647 |
// *****************************************************************************
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
#include <boost/program_options.hpp>
|
|
Packit |
01d647 |
#include <boost/regex.hpp>
|
|
Packit |
01d647 |
#include <boost/array.hpp>
|
|
Packit |
01d647 |
#include <boost/algorithm/string.hpp>
|
|
Packit |
01d647 |
#include <boost/lexical_cast.hpp>
|
|
Packit |
01d647 |
#include <exiv2/image.hpp>
|
|
Packit |
01d647 |
#include <exiv2/error.hpp>
|
|
Packit |
01d647 |
#include <exiv2/basicio.hpp>
|
|
Packit |
01d647 |
#include <iostream>
|
|
Packit |
01d647 |
#include <iomanip>
|
|
Packit |
01d647 |
#include <cassert>
|
|
Packit |
01d647 |
#include <limits>
|
|
Packit |
01d647 |
#include "MD5.h"
|
|
Packit |
01d647 |
#include "helpers.hpp"
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
typedef Exiv2::byte md5digest[16];
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
namespace po = boost::program_options;
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
bool g_verbose = false;
|
|
Packit |
01d647 |
bool g_neednewline = false;
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// Array size should match number of SLOTs
|
|
Packit |
01d647 |
boost::array<int,4> g_run_order = {{-1, -1, -1, -1}};
|
|
Packit |
01d647 |
const int EXIF_SLOT = 0;
|
|
Packit |
01d647 |
const int IPTC_SLOT = 1;
|
|
Packit |
01d647 |
const int XMP_SLOT = 2;
|
|
Packit |
01d647 |
const int FILE_SLOT = 3;
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
const unsigned DOT_EVERY = 55;
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
struct Pattern {
|
|
Packit |
01d647 |
std::string pat;
|
|
Packit |
01d647 |
std::string desc;
|
|
Packit |
01d647 |
pfunc funcs[4]; // order should always be exif, iptc, xmp, file
|
|
Packit |
01d647 |
};
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
struct PathPart {
|
|
Packit |
01d647 |
std::string pre;
|
|
Packit |
01d647 |
const Pattern *pat;
|
|
Packit |
01d647 |
std::string post;
|
|
Packit |
01d647 |
PathPart(std::string pre_, const Pattern *pat_, std::string post_)
|
|
Packit |
01d647 |
: pre(pre_), pat(pat_), post(post_) {}
|
|
Packit |
01d647 |
};
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
std::vector<PathPart> g_path_parts;
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// Instead of making these all global
|
|
Packit |
01d647 |
struct ProcessParams {
|
|
Packit |
01d647 |
const fs::path &dest_dir;
|
|
Packit |
01d647 |
const bool dry_run;
|
|
Packit |
01d647 |
const bool ignore_dups;
|
|
Packit |
01d647 |
const bool ignore_unsorted;
|
|
Packit |
01d647 |
const bool force;
|
|
Packit |
01d647 |
const bool rename;
|
|
Packit |
01d647 |
const bool symlink;
|
|
Packit |
01d647 |
const bool verify;
|
|
Packit |
01d647 |
const bool move;
|
|
Packit |
01d647 |
const long limit_depth;
|
|
Packit |
01d647 |
const fs::path &dups_dir;
|
|
Packit |
01d647 |
const fs::path &unsorted_dir;
|
|
Packit |
01d647 |
const std::vector<std::string> &excludes;
|
|
Packit |
01d647 |
unsigned dups_count;
|
|
Packit |
01d647 |
unsigned unsorted_count;
|
|
Packit |
01d647 |
unsigned dir_err_count;
|
|
Packit |
01d647 |
unsigned file_err_count;
|
|
Packit |
01d647 |
unsigned ok_count;
|
|
Packit |
01d647 |
unsigned dups_ignored_count;
|
|
Packit |
01d647 |
unsigned unsorted_ignored_count;
|
|
Packit |
01d647 |
unsigned dir_ex_count;
|
|
Packit |
01d647 |
unsigned file_ex_count;
|
|
Packit |
01d647 |
};
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
void process_directory(const fs::path &directory, const long depth,
|
|
Packit |
01d647 |
ProcessParams ¶ms);
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
const Pattern g_patterns[] = {
|
|
Packit |
01d647 |
{"@date", "date captured (2009-01-19)",
|
|
Packit |
01d647 |
{exif_date, iptc_date, NULL, file_date} },
|
|
Packit |
01d647 |
{"@year", "year captured (2009)",
|
|
Packit |
01d647 |
{exif_year, iptc_year, NULL, file_year} },
|
|
Packit |
01d647 |
{"@month", "month captured (01)",
|
|
Packit |
01d647 |
{exif_month, iptc_month, NULL, file_month} },
|
|
Packit |
01d647 |
{"@day", "day captured (19)",
|
|
Packit |
01d647 |
{exif_day, iptc_day, NULL, file_day} },
|
|
Packit |
01d647 |
{"@time", "time captured (14-35-27)",
|
|
Packit |
01d647 |
{exif_time, iptc_time, NULL, file_time} },
|
|
Packit |
01d647 |
{"@hour", "hour captured (14)",
|
|
Packit |
01d647 |
{exif_hour, iptc_hour, NULL, file_hour} },
|
|
Packit |
01d647 |
{"@min", "minute captured (35)",
|
|
Packit |
01d647 |
{exif_minute, iptc_minute, NULL, file_minute} },
|
|
Packit |
01d647 |
{"@sec", "second captured (27)",
|
|
Packit |
01d647 |
{exif_second, iptc_second, NULL, file_second} },
|
|
Packit |
01d647 |
{"@dim", "pixel dimension (2272-1704)",
|
|
Packit |
01d647 |
{exif_dimension, NULL, NULL, file_dimension} },
|
|
Packit |
01d647 |
{"@x", "pixel width (2272)",
|
|
Packit |
01d647 |
{exif_width, NULL, NULL, file_width} },
|
|
Packit |
01d647 |
{"@y", "pixel height (1704)",
|
|
Packit |
01d647 |
{exif_height, NULL, NULL, file_height} },
|
|
Packit |
01d647 |
{"@make", "device make (Canon)",
|
|
Packit |
01d647 |
{exif_make, NULL, NULL, NULL} },
|
|
Packit |
01d647 |
{"@model", "device model (Canon PowerShot S40)",
|
|
Packit |
01d647 |
{exif_model, NULL, NULL, NULL} },
|
|
Packit |
01d647 |
{"@speed", "shutter speed (1-60)",
|
|
Packit |
01d647 |
{exif_speed, NULL, NULL, NULL} },
|
|
Packit |
01d647 |
{"@aper", "aperture (F3.2)",
|
|
Packit |
01d647 |
{exif_aperture, NULL, NULL, NULL} },
|
|
Packit |
01d647 |
{"@iso", "iso speed (400)",
|
|
Packit |
01d647 |
{exif_iso, NULL, NULL, NULL} },
|
|
Packit |
01d647 |
{"@focal", "focal length (8.6 mm)",
|
|
Packit |
01d647 |
{exif_focal, NULL, NULL, NULL} },
|
|
Packit |
01d647 |
{"@dist", "subject distance (1.03 m)",
|
|
Packit |
01d647 |
{exif_distance, NULL, NULL, NULL} },
|
|
Packit |
01d647 |
{"@meter", "meter mode (multi-segment)",
|
|
Packit |
01d647 |
{exif_meter, NULL, NULL, NULL} },
|
|
Packit |
01d647 |
{"@macro", "macro mode (Off)",
|
|
Packit |
01d647 |
{exif_macro, NULL, NULL, NULL} },
|
|
Packit |
01d647 |
{"@orient", "orientation (top_left)",
|
|
Packit |
01d647 |
{exif_orientation, NULL, NULL, NULL} },
|
|
Packit |
01d647 |
{"@lens", "lens name (Tamron 90mm f-2.8)",
|
|
Packit |
01d647 |
{exif_lens, NULL, NULL, NULL} },
|
|
Packit |
01d647 |
{"@key", "first keyword (Family)",
|
|
Packit |
01d647 |
{exif_keyword, iptc_keyword, NULL, NULL} },
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
{"", "", {NULL, NULL, NULL, NULL} }
|
|
Packit |
01d647 |
};
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// Check that 'opt1' and 'opt2' are not specified at the same time.
|
|
Packit |
01d647 |
void conflicting(const po::variables_map& vm,
|
|
Packit |
01d647 |
const char* opt1, const char* opt2)
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
if (vm.count(opt1) && !vm[opt1].defaulted()
|
|
Packit |
01d647 |
&& vm.count(opt2) && !vm[opt2].defaulted()) {
|
|
Packit |
01d647 |
throw std::logic_error(std::string("conflicting options '")
|
|
Packit |
01d647 |
+ opt1 + "' and '" + opt2 + "'");
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// Check that 'required' is present
|
|
Packit |
01d647 |
void required(const po::variables_map& vm, const char* required)
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
if (!vm.count(required) || vm[required].defaulted()) {
|
|
Packit |
01d647 |
throw std::logic_error(std::string("required parameter '") + required
|
|
Packit |
01d647 |
+ "' is missing");
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
void info(const std::string &msg)
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
if(g_verbose) {
|
|
Packit |
01d647 |
std::cout << msg << "\n";
|
|
Packit |
01d647 |
g_neednewline = false;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
void error(const std::exception &e, const std::string &msg)
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
if(g_neednewline) {
|
|
Packit |
01d647 |
std::cout << "\n";
|
|
Packit |
01d647 |
g_neednewline = false;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
std::cerr << e.what() << "\n";
|
|
Packit |
01d647 |
std::cerr << msg << std::endl;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
void usage_header(const char* exname)
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
std::cout << "Usage: " << exname << " [options] source-dir dest-dir pattern\n";
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
void usage_full(const po::options_description &options, const char* exname)
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
usage_header(exname);
|
|
Packit |
01d647 |
std::cout << "\n Creates groups of files in new directories defined by a metadata 'pattern'.\n" <<
|
|
Packit |
01d647 |
" Files are copied, moved, or linked from 'source-dir' to 'dest-dir'.\n" <<
|
|
Packit |
01d647 |
" The destination directory should not be within the source directory.\n\n";
|
|
Packit |
01d647 |
std::cout << options;
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
std::cout << "\nPattern values:\n";
|
|
Packit |
01d647 |
for( const Pattern *pattern = g_patterns; pattern->pat.length(); ++pattern) {
|
|
Packit |
01d647 |
std::cout << " " << std::setw(8) << std::left << pattern->pat;
|
|
Packit |
01d647 |
std::cout << pattern->desc << "\n";
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
std::cout << "\nExamples:\n";
|
|
Packit |
01d647 |
std::cout << " `" << exname << " -m mess clean @year-@month'\n";
|
|
Packit |
01d647 |
std::cout << " Moves files from 'mess' into directories of 'clean' according to\n" <<
|
|
Packit |
01d647 |
" year-month the file was captured (clean/2006-11/...)\n\n";
|
|
Packit |
01d647 |
std::cout << " `" << exname << " -o ie source find width-@x/height-@y'\n";
|
|
Packit |
01d647 |
std::cout << " Copies files into directories according first to pixel width then pixel\n" <<
|
|
Packit |
01d647 |
" height. Check iptc then exif metadata (find/width-2272/height-1704/...)\n\n";
|
|
Packit |
01d647 |
std::cout << " `" << exname << " -lf source find @aper/@hour'\n";
|
|
Packit |
01d647 |
std::cout << " Force create symlinks in directories according first to aperture then\n" <<
|
|
Packit |
01d647 |
" hour captured (find/F3.2/15/...)\n";
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
std::cout << std::endl;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
void version()
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
std::cout << "organized 0.1\n" <<
|
|
Packit |
01d647 |
"Copyright (C) 2009 Brad Schick. <schickb@gmail.com>\n\n" <<
|
|
Packit |
01d647 |
"This program is free software; you can redistribute it and/or\n"
|
|
Packit |
01d647 |
"modify it under the terms of the GNU General Public License\n"
|
|
Packit |
01d647 |
"as published by the Free Software Foundation; either version 2\n"
|
|
Packit |
01d647 |
"of the License, or (at your option) any later version.\n"
|
|
Packit |
01d647 |
"\n"
|
|
Packit |
01d647 |
"This program is distributed in the hope that it will be useful,\n"
|
|
Packit |
01d647 |
"but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
|
|
Packit |
01d647 |
"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
|
|
Packit |
01d647 |
"GNU General Public License for more details.\n"
|
|
Packit |
01d647 |
"\n"
|
|
Packit |
01d647 |
"You should have received a copy of the GNU General Public\n"
|
|
Packit |
01d647 |
"License along with this program; if not, write to the Free\n"
|
|
Packit |
01d647 |
"Software Foundation, Inc., 51 Franklin Street, Fifth Floor,\n"
|
|
Packit |
01d647 |
"Boston, MA 02110-1301 USA" << std::endl;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// Returns empty string if the destination subdirectory could not be determined
|
|
Packit |
01d647 |
// for the supplied source file.
|
|
Packit |
01d647 |
std::string build_dest(const fs::path &source_file)
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
std::string dest;
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
Exiv2::Image::AutoPtr image;
|
|
Packit |
01d647 |
try {
|
|
Packit |
01d647 |
image = Exiv2::ImageFactory::open(source_file.string());
|
|
Packit |
01d647 |
image->readMetadata();
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
catch(const Exiv2::AnyError&) {
|
|
Packit |
01d647 |
// No metadata, let things continue to try file info
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
std::vector<PathPart>::iterator iter = g_path_parts.begin();
|
|
Packit |
01d647 |
std::vector<PathPart>::iterator end = g_path_parts.end();
|
|
Packit |
01d647 |
for( ; iter != end; ++iter) {
|
|
Packit |
01d647 |
dest += iter->pre;
|
|
Packit |
01d647 |
std::string result;
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
const Pattern *pat = iter->pat;
|
|
Packit |
01d647 |
for(unsigned fx = 0; fx < g_run_order.size(); ++fx) {
|
|
Packit |
01d647 |
if(g_run_order[fx] != -1 && pat->funcs[g_run_order[fx]]) {
|
|
Packit |
01d647 |
if(g_run_order[fx] == FILE_SLOT) {
|
|
Packit |
01d647 |
// Always run file operations
|
|
Packit |
01d647 |
result = pat->funcs[g_run_order[fx]](image.get(), source_file);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
else if(image.get()) {
|
|
Packit |
01d647 |
// No point in running metadata operations without an image
|
|
Packit |
01d647 |
result = pat->funcs[g_run_order[fx]](image.get(), source_file);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
if(result.length())
|
|
Packit |
01d647 |
break;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
// If we found no data, even for part of pattern, give up and
|
|
Packit |
01d647 |
// return no destination
|
|
Packit |
01d647 |
if(!result.length())
|
|
Packit |
01d647 |
return result;
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
dest += (result + iter->post);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
return dest;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
bool md5sum(const fs::path &path, md5digest &digest)
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
try {
|
|
Packit |
01d647 |
Exiv2::FileIo io(path.string());
|
|
Packit |
01d647 |
if (io.open() != 0)
|
|
Packit |
01d647 |
return false;
|
|
Packit |
01d647 |
Exiv2::IoCloser closer(io);
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
Exiv2::byte buff[4096];
|
|
Packit |
01d647 |
MD5_CTX context;
|
|
Packit |
01d647 |
MD5Init(&context);
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
long read_count = io.read(buff, 4096);
|
|
Packit |
01d647 |
while(read_count) {
|
|
Packit |
01d647 |
MD5Update(&context, buff, read_count);
|
|
Packit |
01d647 |
read_count = io.read(buff, 4096);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
MD5Final(digest, &context);
|
|
Packit |
01d647 |
return true;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
catch (std::exception& ) {
|
|
Packit |
01d647 |
return false;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
int main(int argc, char* argv[])
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
po::options_description options("Options");
|
|
Packit |
01d647 |
// Don't use default values because the help print it ugly and too wide
|
|
Packit |
01d647 |
options.add_options()
|
|
Packit |
01d647 |
("move,m", "move files rather than copy")
|
|
Packit |
01d647 |
("symlink,s", "symlink files rather than copy (posix only)")
|
|
Packit |
01d647 |
("order,o", po::value<std::string>(),
|
|
Packit |
01d647 |
"order and types of metadata to read\ne=exif, i=iptc, f=file (default: eif)")
|
|
Packit |
01d647 |
("unsorted,u", po::value<std::string>(),
|
|
Packit |
01d647 |
"special directory to store unsorted files (default: unsorted)")
|
|
Packit |
01d647 |
("dups,d", po::value<std::string>(),
|
|
Packit |
01d647 |
"special directory to store files with duplicate names (default: duplicates)")
|
|
Packit |
01d647 |
("force,f", "overwrite duplicate files instead of using special directory")
|
|
Packit |
01d647 |
("rename,r", "rename duplicate files instead of using special directory")
|
|
Packit |
01d647 |
("ignore,i", "ignore both unsorted and duplicate files instead of using special directories")
|
|
Packit |
01d647 |
("ignore-unsorted", "ignore unsorted files instead of using special directory")
|
|
Packit |
01d647 |
("ignore-dups", "ignore duplicate files instead of using special directory")
|
|
Packit |
01d647 |
("verify", "verify copied or moved files and exit if incorrect")
|
|
Packit |
01d647 |
("exclude,x", po::value< std::vector<std::string> >(),
|
|
Packit |
01d647 |
"exclude directories and files that contain arg (case sensitive on all platforms)")
|
|
Packit |
01d647 |
("limit-depth,l", po::value<long>(),
|
|
Packit |
01d647 |
"limit recursion to specified depth (0 disables recursion)")
|
|
Packit |
01d647 |
("verbose,v", "prints operations as they happen")
|
|
Packit |
01d647 |
("dry-run,n", "do not make actual changes (implies verbose)")
|
|
Packit |
01d647 |
("help,h", "show this help message then exit")
|
|
Packit |
01d647 |
("version,V", "show program version then exit")
|
|
Packit |
01d647 |
;
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
po::options_description hidden("Hidden Options");
|
|
Packit |
01d647 |
hidden.add_options()
|
|
Packit |
01d647 |
("source-dir", po::value< std::string >(), "directory of files to organize, may end in file wildcard")
|
|
Packit |
01d647 |
("dest-dir", po::value< std::string >(), "designation directory for files, may not be within source-dir")
|
|
Packit |
01d647 |
("pattern", po::value< std::string >(), "subdirectory pattern for grouping files within dest-dir")
|
|
Packit |
01d647 |
;
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
po::options_description cmdline;
|
|
Packit |
01d647 |
cmdline.add(options).add(hidden);
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
po::positional_options_description positional;
|
|
Packit |
01d647 |
positional.add("source-dir", 1);
|
|
Packit |
01d647 |
positional.add("dest-dir", 1);
|
|
Packit |
01d647 |
positional.add("pattern", 1);
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
try {
|
|
Packit |
01d647 |
po::variables_map vm;
|
|
Packit |
01d647 |
po::store(po::command_line_parser(argc, argv).
|
|
Packit |
01d647 |
options(cmdline).positional(positional).run(), vm);
|
|
Packit |
01d647 |
po::notify(vm);
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
if (vm.count("help")) {
|
|
Packit |
01d647 |
usage_full(options, argv[0]);
|
|
Packit |
01d647 |
return 0;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
if (vm.count("version")) {
|
|
Packit |
01d647 |
version();
|
|
Packit |
01d647 |
return 0;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
conflicting(vm, "verify", "symlink");
|
|
Packit |
01d647 |
conflicting(vm, "move", "symlink");
|
|
Packit |
01d647 |
conflicting(vm, "unsorted", "ignore");
|
|
Packit |
01d647 |
conflicting(vm, "unsorted", "ignore-unsorted");
|
|
Packit |
01d647 |
conflicting(vm, "dups", "ignore");
|
|
Packit |
01d647 |
conflicting(vm, "dups", "ignore-dups");
|
|
Packit |
01d647 |
conflicting(vm, "force", "ignore");
|
|
Packit |
01d647 |
conflicting(vm, "force", "ignore-dups");
|
|
Packit |
01d647 |
conflicting(vm, "force", "rename");
|
|
Packit |
01d647 |
conflicting(vm, "rename", "ignore");
|
|
Packit |
01d647 |
conflicting(vm, "rename", "ignore-dups");
|
|
Packit |
01d647 |
required(vm, "source-dir");
|
|
Packit |
01d647 |
required(vm, "dest-dir");
|
|
Packit |
01d647 |
required(vm, "pattern");
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
const bool dry_run = vm.count("dry-run") != 0;
|
|
Packit |
01d647 |
g_verbose = (vm.count("verbose") != 0 || dry_run);
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
std::string order = "eif";
|
|
Packit |
01d647 |
if(vm.count("order")) {
|
|
Packit |
01d647 |
order = vm["order"].as<std::string>();
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
boost::to_lower(order);
|
|
Packit |
01d647 |
if(order.length() > 3) {
|
|
Packit |
01d647 |
throw std::logic_error(std::string("order is longer than 4 characters"));
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
unsigned i = 0;
|
|
Packit |
01d647 |
std::string::iterator end = order.end();
|
|
Packit |
01d647 |
for(std::string::iterator iter = order.begin(); iter != end && i < 4; ++iter, ++i) {
|
|
Packit |
01d647 |
switch(*iter) {
|
|
Packit |
01d647 |
case 'e':
|
|
Packit |
01d647 |
g_run_order[i] = EXIF_SLOT;
|
|
Packit |
01d647 |
break;
|
|
Packit |
01d647 |
case 'i':
|
|
Packit |
01d647 |
g_run_order[i] = IPTC_SLOT;
|
|
Packit |
01d647 |
break;
|
|
Packit |
01d647 |
case 'x':
|
|
Packit |
01d647 |
throw std::logic_error(std::string("xmp not implemented yet '") +
|
|
Packit |
01d647 |
*iter + "'");
|
|
Packit |
01d647 |
break;
|
|
Packit |
01d647 |
case 'f':
|
|
Packit |
01d647 |
g_run_order[i] = FILE_SLOT;
|
|
Packit |
01d647 |
break;
|
|
Packit |
01d647 |
default:
|
|
Packit |
01d647 |
throw std::logic_error(std::string("unknown order character '") +
|
|
Packit |
01d647 |
*iter + "'");
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
const fs::path source_dir( vm["source-dir"].as<std::string>() );
|
|
Packit |
01d647 |
if( !exists(source_dir) || !is_directory(source_dir) ) {
|
|
Packit |
01d647 |
throw std::logic_error(std::string("source '") +
|
|
Packit |
01d647 |
source_dir.string() + "' must exist and be a directory");
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
const fs::path dest_dir( vm["dest-dir"].as<std::string>() );
|
|
Packit |
01d647 |
if( exists(dest_dir) && !is_directory(dest_dir) ) {
|
|
Packit |
01d647 |
throw std::logic_error(std::string("destination '") +
|
|
Packit |
01d647 |
dest_dir.string() + "' must be a directory");
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// Boost doesn't seem to have a way to get a canonical path, so this
|
|
Packit |
01d647 |
// simple test is easy to confuse with some ../../'s in the paths. Oh
|
|
Packit |
01d647 |
// well, this is good enough for now.
|
|
Packit |
01d647 |
fs::path test_dest(dest_dir);
|
|
Packit |
01d647 |
for(; !test_dest.empty(); test_dest = test_dest.parent_path()) {
|
|
Packit |
01d647 |
if(fs::equivalent(source_dir, test_dest)) {
|
|
Packit |
01d647 |
throw std::logic_error(std::string("dest-dir must not be within source-dir"));
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// Disect the pattern
|
|
Packit |
01d647 |
std::string pattern = vm["pattern"].as<std::string>();
|
|
Packit |
01d647 |
boost::regex regex( "([^@]*)(@[[:alpha:]]+)([^@]*)");
|
|
Packit |
01d647 |
boost::sregex_iterator m_iter = make_regex_iterator(pattern, regex);
|
|
Packit |
01d647 |
boost::sregex_iterator m_end;
|
|
Packit |
01d647 |
for( ; m_iter != m_end; ++m_iter) {
|
|
Packit |
01d647 |
const boost::smatch &match = *m_iter;
|
|
Packit |
01d647 |
const std::string &pre = match[1];
|
|
Packit |
01d647 |
const std::string &pat = match[2];
|
|
Packit |
01d647 |
const std::string &post = match[3];
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// Should put this in a map, but there aren't that many options now
|
|
Packit |
01d647 |
bool found = false;
|
|
Packit |
01d647 |
for( const Pattern *pattern = g_patterns; pattern->pat.length(); ++pattern) {
|
|
Packit |
01d647 |
if(pattern->pat == pat) {
|
|
Packit |
01d647 |
PathPart part(pre, pattern, post);
|
|
Packit |
01d647 |
g_path_parts.push_back(part);
|
|
Packit |
01d647 |
found = true;
|
|
Packit |
01d647 |
break;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
if(!found) {
|
|
Packit |
01d647 |
throw std::logic_error(std::string("unknown pattern '") + pat + "'");
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// Assign defaults to params that need them
|
|
Packit |
01d647 |
const bool ignore = vm.count("ignore") != 0;
|
|
Packit |
01d647 |
std::vector<std::string> excludes;
|
|
Packit |
01d647 |
if(vm.count("exclude"))
|
|
Packit |
01d647 |
excludes = vm["exclude"].as< std::vector<std::string> >();
|
|
Packit |
01d647 |
long limit_depth = LONG_MAX;
|
|
Packit |
01d647 |
if(vm.count("limit-depth")) {
|
|
Packit |
01d647 |
limit_depth = vm["limit-depth"].as<long>();
|
|
Packit |
01d647 |
// Boost program_options doesn't work with unsigned, so do it manually
|
|
Packit |
01d647 |
if( limit_depth < 0 )
|
|
Packit |
01d647 |
throw std::logic_error(std::string("recursion depth limit must be positive"));
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
std::string dups = "duplicates";
|
|
Packit |
01d647 |
if(vm.count("dups"))
|
|
Packit |
01d647 |
dups = vm["dups"].as<std::string>();
|
|
Packit |
01d647 |
const fs::path dups_dir = dest_dir / dups;
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
std::string unsorted = "unsorted";
|
|
Packit |
01d647 |
if(vm.count("unsorted"))
|
|
Packit |
01d647 |
unsorted = vm["unsorted"].as<std::string>();
|
|
Packit |
01d647 |
const fs::path unsorted_dir = dest_dir / unsorted;
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
ProcessParams params = {
|
|
Packit |
01d647 |
dest_dir,
|
|
Packit |
01d647 |
dry_run,
|
|
Packit |
01d647 |
(vm.count("ignore-dups") != 0 || ignore),
|
|
Packit |
01d647 |
(vm.count("ignore-unsorted") != 0 || ignore),
|
|
Packit |
01d647 |
vm.count("force") != 0,
|
|
Packit |
01d647 |
vm.count("rename") != 0,
|
|
Packit |
01d647 |
vm.count("symlink") != 0,
|
|
Packit |
01d647 |
vm.count("verify") != 0,
|
|
Packit |
01d647 |
vm.count("move") != 0,
|
|
Packit |
01d647 |
limit_depth,
|
|
Packit |
01d647 |
dups_dir,
|
|
Packit |
01d647 |
unsorted_dir,
|
|
Packit |
01d647 |
excludes,
|
|
Packit |
01d647 |
0, 0, 0, 0, 0, 0, 0, 0, 0
|
|
Packit |
01d647 |
};
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
process_directory(source_dir, 0, params);
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
std::string op = "copied";
|
|
Packit |
01d647 |
if(params.symlink)
|
|
Packit |
01d647 |
op = "linked";
|
|
Packit |
01d647 |
else if(params.move)
|
|
Packit |
01d647 |
op = "moved";
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
if(dry_run)
|
|
Packit |
01d647 |
op = std::string("would be ") + op;
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
if(g_neednewline)
|
|
Packit |
01d647 |
std::cout << "\n";
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
std::cout << "\n" << params.ok_count << " files " << op << "\n";
|
|
Packit |
01d647 |
std::cout << " " << params.dups_count << " duplicates\n";
|
|
Packit |
01d647 |
std::cout << " " << params.unsorted_count << " unsorted\n";
|
|
Packit |
01d647 |
if(params.dups_ignored_count)
|
|
Packit |
01d647 |
std::cout << params.dups_ignored_count << " duplicates ignored\n";
|
|
Packit |
01d647 |
if(params.unsorted_ignored_count)
|
|
Packit |
01d647 |
std::cout << params.unsorted_ignored_count << " unsorted ignored\n";
|
|
Packit |
01d647 |
if(params.dir_ex_count)
|
|
Packit |
01d647 |
std::cout << params.dir_ex_count << " directories excluded\n";
|
|
Packit |
01d647 |
if(params.file_ex_count)
|
|
Packit |
01d647 |
std::cout << params.file_ex_count << " files excluded\n";
|
|
Packit |
01d647 |
if(params.dir_err_count)
|
|
Packit |
01d647 |
std::cout << params.dir_err_count << " directory errors\n";
|
|
Packit |
01d647 |
if(params.file_err_count)
|
|
Packit |
01d647 |
std::cout << params.file_err_count << " file errors\n";
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
return 0;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
catch (Exiv2::AnyError& e) {
|
|
Packit |
01d647 |
error(e, std::string("Aborting"));
|
|
Packit |
01d647 |
return -1;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
catch(std::logic_error& e) {
|
|
Packit |
01d647 |
error(e, "");
|
|
Packit |
01d647 |
usage_header(argv[0]);
|
|
Packit |
01d647 |
std::cout << argv[0] << " -h for more help" << std::endl;
|
|
Packit |
01d647 |
return -2;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
catch(std::exception& e) {
|
|
Packit |
01d647 |
error(e, "Aborting");
|
|
Packit |
01d647 |
return -3;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
boost::regex uregex("(.*?)\\(([[:digit:]]{1,2})\\)$");
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
fs::path uniquify(const fs::path &dest)
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
std::string ext = dest.extension().string();
|
|
Packit |
01d647 |
std::string fname = dest.stem().string();
|
|
Packit |
01d647 |
fs::path parent = dest.parent_path();
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
unsigned number = 1;
|
|
Packit |
01d647 |
std::string newfname;
|
|
Packit |
01d647 |
fs::path newdest;
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
boost::smatch match;
|
|
Packit |
01d647 |
if(boost::regex_search(fname, match, uregex)) {
|
|
Packit |
01d647 |
// Matches are indexes into fname, so don't change it while reading values
|
|
Packit |
01d647 |
newfname = match[1];
|
|
Packit |
01d647 |
number = boost::lexical_cast<short>(match[2]);
|
|
Packit |
01d647 |
fname = newfname;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
do {
|
|
Packit |
01d647 |
newfname = fname + "(" + boost::lexical_cast<std::string>(++number) + ")" + ext;
|
|
Packit |
01d647 |
newdest = parent / newfname;
|
|
Packit |
01d647 |
} while(fs::exists(newdest));
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
return newdest;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
void process_directory(const fs::path &directory, const long depth,
|
|
Packit |
01d647 |
ProcessParams ¶ms)
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
// Exclude entire directories
|
|
Packit |
01d647 |
bool exclude = false;
|
|
Packit |
01d647 |
std::vector<std::string>::const_iterator x_iter = params.excludes.begin();
|
|
Packit |
01d647 |
std::vector<std::string>::const_iterator x_end = params.excludes.end();
|
|
Packit |
01d647 |
for( ; x_iter != x_end; ++x_iter ) {
|
|
Packit |
01d647 |
if(boost::contains(directory.string(), *x_iter)) {
|
|
Packit |
01d647 |
exclude = true;
|
|
Packit |
01d647 |
break;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
if(exclude) {
|
|
Packit |
01d647 |
info(std::string("excluding directory: ") + directory.string() +
|
|
Packit |
01d647 |
" matched: " + *x_iter);
|
|
Packit |
01d647 |
++params.dir_ex_count;
|
|
Packit |
01d647 |
return;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
try {
|
|
Packit |
01d647 |
fs::directory_iterator p_iter(directory), p_end;
|
|
Packit |
01d647 |
for( ; p_iter != p_end; ++p_iter) {
|
|
Packit |
01d647 |
if( is_directory(*p_iter) ) {
|
|
Packit |
01d647 |
// recurse if we haven't hit the limit
|
|
Packit |
01d647 |
if(depth < params.limit_depth)
|
|
Packit |
01d647 |
process_directory(p_iter->path(), depth + 1, params);
|
|
Packit |
01d647 |
else {
|
|
Packit |
01d647 |
info(std::string("depth reached, skipping: ") +
|
|
Packit |
01d647 |
p_iter->path().string());
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
else if( is_regular_file(*p_iter) ) {
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// Check again for excluding file names
|
|
Packit |
01d647 |
exclude = false;
|
|
Packit |
01d647 |
x_iter = params.excludes.begin();
|
|
Packit |
01d647 |
for( ; x_iter != x_end; ++x_iter ) {
|
|
Packit |
01d647 |
if(boost::contains(p_iter->path().string(), *x_iter)) {
|
|
Packit |
01d647 |
exclude = true;
|
|
Packit |
01d647 |
break;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
if(exclude) {
|
|
Packit |
01d647 |
info(std::string("excluding file: ") + p_iter->path().string() +
|
|
Packit |
01d647 |
" matched: " + *x_iter);
|
|
Packit |
01d647 |
++params.file_ex_count;
|
|
Packit |
01d647 |
continue;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
try {
|
|
Packit |
01d647 |
const fs::path dest_subdir = build_dest(*p_iter);
|
|
Packit |
01d647 |
fs::path dest_file;
|
|
Packit |
01d647 |
if(!dest_subdir.empty())
|
|
Packit |
01d647 |
dest_file = params.dest_dir / dest_subdir;
|
|
Packit |
01d647 |
else if(params.ignore_unsorted) {
|
|
Packit |
01d647 |
info(std::string("ignoring unsorted: ") + p_iter->path().string());
|
|
Packit |
01d647 |
++params.unsorted_ignored_count;
|
|
Packit |
01d647 |
continue;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
else {
|
|
Packit |
01d647 |
info(std::string("unsorted file (missing metadata): ") + p_iter->path().string());
|
|
Packit |
01d647 |
dest_file = params.unsorted_dir;
|
|
Packit |
01d647 |
++params.unsorted_count;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
dest_file /= p_iter->path().filename();
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
if(fs::exists(dest_file)) {
|
|
Packit |
01d647 |
if(params.ignore_dups) {
|
|
Packit |
01d647 |
info(std::string("ignoring: ") + p_iter->path().string() +
|
|
Packit |
01d647 |
" duplicates: " + dest_file.string());
|
|
Packit |
01d647 |
++params.dups_ignored_count;
|
|
Packit |
01d647 |
continue;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
else {
|
|
Packit |
01d647 |
if(params.force) {
|
|
Packit |
01d647 |
info(std::string("force removing: ") + dest_file.string() + " for: "
|
|
Packit |
01d647 |
+ p_iter->path().string());
|
|
Packit |
01d647 |
if(!params.dry_run)
|
|
Packit |
01d647 |
fs::remove(dest_file);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
else if(params.rename) {
|
|
Packit |
01d647 |
info(std::string("renaming: ") + p_iter->path().string() +
|
|
Packit |
01d647 |
" duplicates: " + dest_file.string());
|
|
Packit |
01d647 |
dest_file = uniquify(dest_file);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
else {
|
|
Packit |
01d647 |
info(std::string("duplicate file: ") + p_iter->path().string() +
|
|
Packit |
01d647 |
" of: " + dest_file.string());
|
|
Packit |
01d647 |
dest_file = params.dups_dir / dest_subdir / p_iter->path().filename();
|
|
Packit |
01d647 |
// Ugh, more dup possibilities
|
|
Packit |
01d647 |
if(fs::exists(dest_file)) {
|
|
Packit |
01d647 |
info(std::string("renaming: ") + p_iter->path().string() +
|
|
Packit |
01d647 |
" duplicates: " + dest_file.string());
|
|
Packit |
01d647 |
dest_file = uniquify(dest_file);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
++params.dups_count;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
if(!params.dry_run)
|
|
Packit |
01d647 |
fs::create_directories(dest_file.parent_path());
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
if(params.symlink) {
|
|
Packit |
01d647 |
info(std::string("linking from: ") + p_iter->path().string() +
|
|
Packit |
01d647 |
" to: " + dest_file.string());
|
|
Packit |
01d647 |
if(!params.dry_run) {
|
|
Packit |
01d647 |
// The target of a symlink must be either absolute (aka complete) or
|
|
Packit |
01d647 |
// relative to the location of the link. Easiest solution is to make
|
|
Packit |
01d647 |
// a complete path.
|
|
Packit |
01d647 |
fs::path target;
|
|
Packit |
01d647 |
if(p_iter->path().is_complete())
|
|
Packit |
01d647 |
target = p_iter->path();
|
|
Packit |
01d647 |
else
|
|
Packit |
01d647 |
target = fs::initial_path() / p_iter->path();
|
|
Packit |
01d647 |
fs::create_symlink(target, dest_file);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
else {
|
|
Packit |
01d647 |
info(std::string("copying from: ") + p_iter->path().string() +
|
|
Packit |
01d647 |
" to: " + dest_file.string());
|
|
Packit |
01d647 |
if(!params.dry_run) {
|
|
Packit |
01d647 |
// Copy the file and restore its write time (needed for posix)
|
|
Packit |
01d647 |
std::time_t time = fs::last_write_time(*p_iter);
|
|
Packit |
01d647 |
fs::copy_file(*p_iter, dest_file);
|
|
Packit |
01d647 |
fs::last_write_time(dest_file, time);
|
|
Packit |
01d647 |
if(params.verify) {
|
|
Packit |
01d647 |
md5digest src_digest, dst_digest;
|
|
Packit |
01d647 |
bool ok = md5sum(p_iter->path(), src_digest);
|
|
Packit |
01d647 |
if(ok)
|
|
Packit |
01d647 |
ok = md5sum(dest_file, dst_digest);
|
|
Packit |
01d647 |
if(ok)
|
|
Packit |
01d647 |
ok = (memcmp(src_digest,dst_digest, sizeof(md5digest))==0);
|
|
Packit |
01d647 |
if(!ok) {
|
|
Packit |
01d647 |
// Should probably find a more appropriate exception for this
|
|
Packit |
01d647 |
throw std::runtime_error(std::string("File verification failed: '")
|
|
Packit |
01d647 |
+ p_iter->path().string() + "' differs from '" +
|
|
Packit |
01d647 |
dest_file.string() + "'");
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
else {
|
|
Packit |
01d647 |
info(std::string("verification passed"));
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
if(params.move) {
|
|
Packit |
01d647 |
info(std::string("removing: ") + p_iter->path().string());
|
|
Packit |
01d647 |
if(!params.dry_run)
|
|
Packit |
01d647 |
fs::remove(*p_iter);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
if(!g_verbose && (params.ok_count % DOT_EVERY)==0) {
|
|
Packit |
01d647 |
std::cout << "." << std::flush;
|
|
Packit |
01d647 |
g_neednewline = true;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
++params.ok_count;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
catch(fs::filesystem_error& e) {
|
|
Packit |
01d647 |
error(e, std::string("skipping file: " + p_iter->path().string()));
|
|
Packit |
01d647 |
++params.file_err_count;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
catch(fs::filesystem_error& e) {
|
|
Packit |
01d647 |
error(e, std::string("skipping directory: " + directory.string()));
|
|
Packit |
01d647 |
++params.dir_err_count;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|