diff --git a/contrib/organize/MD5.cpp b/contrib/organize/MD5.cpp deleted file mode 100644 index a6e2e822..00000000 --- a/contrib/organize/MD5.cpp +++ /dev/null @@ -1,235 +0,0 @@ -/* - * This code implements the MD5 message-digest algorithm. - * The algorithm is due to Ron Rivest. This code was - * written by Colin Plumb in 1993, no copyright is claimed. - * This code is in the public domain; do with it what you wish. - * - * Equivalent code is available from RSA Data Security, Inc. - * This code has been tested against that, and is equivalent, - * except that you don't need to include two pages of legalese - * with every copy. - * - * To compute the message digest of a chunk of bytes, declare an - * MD5_CTX structure, pass it to MD5Init, call MD5Update as - * needed on buffers full of bytes, and then call MD5Final, which - * will fill a supplied 16-byte array with the digest. - * - * Changed so as no longer to depend on Colin Plumb's `usual.h' header - * definitions; now uses stuff from dpkg's config.h. - * - Ian Jackson . - * Still in the public domain. - */ - -#include - -#include "MD5.h" - -using namespace std; - -static void -byteSwap(UWORD32 *buf, unsigned words) -{ - const uint32_t byteOrderTest = 0x1; - if (((char *)&byteOrderTest)[0] == 0) { - md5byte *p = (md5byte *)buf; - - do { - *buf++ = (UWORD32)((unsigned)p[3] << 8 | p[2]) << 16 | - ((unsigned)p[1] << 8 | p[0]); - p += 4; - } while (--words); - } -} - -/* - * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious - * initialization constants. - */ -void -MD5Init(struct MD5_CTX *ctx) -{ - ctx->buf[0] = 0x67452301; - ctx->buf[1] = 0xefcdab89; - ctx->buf[2] = 0x98badcfe; - ctx->buf[3] = 0x10325476; - - ctx->bytes[0] = 0; - ctx->bytes[1] = 0; -} - -/* - * Update context to reflect the concatenation of another buffer full - * of bytes. - */ -void -MD5Update(struct MD5_CTX *ctx, md5byte const *buf, unsigned len) -{ - UWORD32 t; - - /* Update byte count */ - - t = ctx->bytes[0]; - if ((ctx->bytes[0] = t + len) < t) - ctx->bytes[1]++; /* Carry from low to high */ - - t = 64 - (t & 0x3f); /* Space available in ctx->in (at least 1) */ - if (t > len) { - memcpy((md5byte *)ctx->in + 64 - t, buf, len); - return; - } - /* First chunk is an odd size */ - memcpy((md5byte *)ctx->in + 64 - t, buf, t); - byteSwap(ctx->in, 16); - MD5Transform(ctx->buf, ctx->in); - buf += t; - len -= t; - - /* Process data in 64-byte chunks */ - while (len >= 64) { - memcpy(ctx->in, buf, 64); - byteSwap(ctx->in, 16); - MD5Transform(ctx->buf, ctx->in); - buf += 64; - len -= 64; - } - - /* Handle any remaining bytes of data. */ - memcpy(ctx->in, buf, len); -} - -/* - * Final wrapup - pad to 64-byte boundary with the bit pattern - * 1 0* (64-bit count of bits processed, MSB-first) - */ -void -MD5Final(md5byte digest[16], struct MD5_CTX *ctx) -{ - int count = ctx->bytes[0] & 0x3f; /* Number of bytes in ctx->in */ - md5byte *p = (md5byte *)ctx->in + count; - - /* Set the first char of padding to 0x80. There is always room. */ - *p++ = 0x80; - - /* Bytes of padding needed to make 56 bytes (-8..55) */ - count = 56 - 1 - count; - - if (count < 0) { /* Padding forces an extra block */ - memset(p, 0, count + 8); - byteSwap(ctx->in, 16); - MD5Transform(ctx->buf, ctx->in); - p = (md5byte *)ctx->in; - count = 56; - } - memset(p, 0, count); - byteSwap(ctx->in, 14); - - /* Append length in bits and transform */ - ctx->in[14] = ctx->bytes[0] << 3; - ctx->in[15] = ctx->bytes[1] << 3 | ctx->bytes[0] >> 29; - MD5Transform(ctx->buf, ctx->in); - - byteSwap(ctx->buf, 4); - memcpy(digest, ctx->buf, 16); - memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */ -} - -/* The four core functions - F1 is optimized somewhat */ - -/* #define F1(x, y, z) (x & y | ~x & z) */ -#define F1(x, y, z) (z ^ (x & (y ^ z))) -#define F2(x, y, z) F1(z, x, y) -#define F3(x, y, z) (x ^ y ^ z) -#define F4(x, y, z) (y ^ (x | ~z)) - -/* This is the central step in the MD5 algorithm. */ -#define MD5STEP(f,w,x,y,z,in,s) \ - (w += f(x,y,z) + in, w = (w<>(32-s)) + x) - -/* - * The core of the MD5 algorithm, this alters an existing MD5 hash to - * reflect the addition of 16 longwords of new data. MD5Update blocks - * the data and converts bytes into longwords for this routine. - */ -void -MD5Transform(UWORD32 buf[4], UWORD32 const in[16]) -{ - register UWORD32 a, b, c, d; - - a = buf[0]; - b = buf[1]; - c = buf[2]; - d = buf[3]; - - MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); - MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); - MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); - MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); - MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); - MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); - MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); - MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); - MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); - MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); - MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); - MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); - MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); - MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); - MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); - MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); - - MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); - MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); - MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); - MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); - MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); - MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); - MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); - MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); - MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); - MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); - MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); - MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); - MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); - MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); - MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); - MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); - - MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); - MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); - MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); - MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); - MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); - MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); - MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); - MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); - MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); - MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); - MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); - MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); - MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); - MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); - MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); - MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); - - MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); - MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); - MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); - MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); - MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); - MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); - MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); - MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); - MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); - MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); - MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); - MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); - MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); - MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); - MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); - MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); - - buf[0] += a; - buf[1] += b; - buf[2] += c; - buf[3] += d; -} diff --git a/contrib/organize/MD5.h b/contrib/organize/MD5.h deleted file mode 100644 index 00a5e94a..00000000 --- a/contrib/organize/MD5.h +++ /dev/null @@ -1,50 +0,0 @@ -#ifndef __MD5_h__ -#define __MD5_h__ - -/* - * This is the header file for the MD5 message-digest algorithm. - * The algorithm is due to Ron Rivest. This code was - * written by Colin Plumb in 1993, no copyright is claimed. - * This code is in the public domain; do with it what you wish. - * - * Equivalent code is available from RSA Data Security, Inc. - * This code has been tested against that, and is equivalent, - * except that you don't need to include two pages of legalese - * with every copy. - * - * To compute the message digest of a chunk of bytes, declare an - * MD5_CTX structure, pass it to MD5Init, call MD5Update as - * needed on buffers full of bytes, and then call MD5Final, which - * will fill a supplied 16-byte array with the digest. - * - * Changed so as no longer to depend on Colin Plumb's `usual.h' - * header definitions; now uses stuff from dpkg's config.h - * - Ian Jackson . - * Still in the public domain. - */ - -#include -#ifdef EXV_HAVE_STDINT_H -# include -#endif - -/* MSVC doesn't provide C99 types, but it has MS specific variants */ -#ifdef _MSC_VER -typedef unsigned __int32 uint32_t; -#endif - -typedef unsigned char md5byte; -typedef uint32_t UWORD32; - -struct MD5_CTX { - UWORD32 buf[4]; - UWORD32 bytes[2]; - UWORD32 in[16]; -}; - -extern void MD5Init(struct MD5_CTX *context); -extern void MD5Update(struct MD5_CTX *context, md5byte const *buf, unsigned len); -extern void MD5Final(unsigned char digest[16], struct MD5_CTX *context); -extern void MD5Transform(UWORD32 buf[4], UWORD32 const in[16]); - -#endif diff --git a/contrib/organize/Makefile b/contrib/organize/Makefile deleted file mode 100644 index cf459806..00000000 --- a/contrib/organize/Makefile +++ /dev/null @@ -1,144 +0,0 @@ -# ************************************************************* -*- Makefile -*- -# -# Copyright (C) 2004-2015 Andreas Huggel -# -# This Makefile is part of the Exiv2 distribution. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following -# disclaimer in the documentation and/or other materials provided -# with the distribution. -# 3. The name of the author may not be used to endorse or promote -# products derived from this software without specific prior -# written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR -# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE -# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER -# IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR -# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN -# IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# -# File: Makefile -# Author(s): Andreas Huggel (ahu) -# History: 31-Jan-09, ahu: created -# -# Description: -# Simple Makefile to build the organize application. Requires installed -# exiv2 library and headers. Adapted from samples/Makefile. -# -# Restrictions: -# Requires GNU make. -# - -# ****************************************************************************** -# Default make target -all: ozbin - -# Include system configuration -top_srcdir = ../.. -include $(top_srcdir)/config/config.mk -include boost.mk - -# ****************************************************************************** -# Source files - -# Source files for the organize application -OZMAIN = organize.cpp -OZSRC = helpers.cpp MD5.cpp - -# ****************************************************************************** -# Initialisations -SHELL = /bin/sh - -.SUFFIXES: -.SUFFIXES: .c .cpp .o .so - -.PRECIOUS: %.cpp - -CPPFLAGS := -I$(BOOST_INC_DIR) `pkg-config exiv2 --cflags` -ifdef HAVE_STDINT - CPPFLAGS += -DEXV_HAVE_STDINT_H=1 -endif - -LDFLAGS := $(BOOST_LIBS) `pkg-config exiv2 --libs` - -OZOBJ = $(OZSRC:.cpp=.o) $(OZMAIN:.cpp=.o) -OZBIN = $(OZMAIN:.cpp=) -OZEXE = $(OZMAIN:.cpp=$(EXEEXT)) - -ifdef DEP_TRACKING -DEP = $(OZMAIN:%.cpp=$(DEPDIR)/%.d) $(OZSRC:%.cpp=$(DEPDIR)/%.d) -endif - -# ****************************************************************************** -# Rules -ozbin: $(OZBIN) - -$(OZOBJ): %.o: %.cpp - $(COMPILE.cc) -o $@ $< - @$(MAKEDEPEND) - @$(POSTDEPEND) - -%.ii: %.cpp - set -e; \ - $(CXXCPP) -E $(CPPFLAGS) $< | sed '/^[ ]*$$/d' > $@ - -# ****************************************************************************** -# Targets -.PHONY: all ozbin relink binclean install uninstall mostlyclean clean distclean maintainer-clean - -ifdef DEP_TRACKING -# Include targets from dependency files --include $(DEP) -endif - -$(OZBIN): $(OZOBJ) - $(LIBTOOL) --mode=link $(LINK.cc) -o $@ $(OZOBJ) - -relink: binclean organize - -install: - $(INSTALL_DIRS) $(DESTDIR)$(bindir) - @$(LIBTOOL) --mode=install $(INSTALL_PROGRAM) $(OZEXE) $(DESTDIR)$(bindir)/$(OZEXE) - -uninstall: - @$(LIBTOOL) --mode=uninstall $(RM) $(DESTDIR)$(bindir)/$(OZEXE) - -rmdir $(DESTDIR)$(bindir) - -# Remove binaries, e.g., to relink them -binclean: - $(RM) $(OZEXE) - -mostlyclean: - $(RM) core - $(RM) $(OZMAIN:.cpp=.ii) $(OZSRC:.cpp=.ii) - $(RM) $(OZMAIN:%.cpp=.libs/%.d) $(OZSRC:%.cpp=.libs/%.d) - -rmdir .libs - $(RM) $(OZOBJ) - -clean: binclean mostlyclean - -# Run `make distclean' from the top source directory to also remove -# files created by configuring the program. -distclean: clean -ifdef DEP_TRACKING - $(RM) $(DEP) - -rmdir $(DEPDIR) -endif - $(RM) *~ *.bak *# - -# This command is intended for maintainers to use; it deletes files -# that may need special tools to rebuild. -maintainer-clean: uninstall distclean diff --git a/contrib/organize/README b/contrib/organize/README deleted file mode 100644 index 26285f72..00000000 --- a/contrib/organize/README +++ /dev/null @@ -1,3 +0,0 @@ -organize uses the Boost library (http://www.boost.org). -Configuration settings for Boost are in the file boost.mk -in this directory and should be changed as required. diff --git a/contrib/organize/boost.mk b/contrib/organize/boost.mk deleted file mode 100644 index b7cd50cb..00000000 --- a/contrib/organize/boost.mk +++ /dev/null @@ -1,3 +0,0 @@ -# Boost configuration for organize - change paths and library names as needed -BOOST_INC_DIR = /usr/local/include/boost-1_37 -BOOST_LIBS = /usr/local/lib/libboost_system-gcc43-mt-1_37.a /usr/local/lib/libboost_filesystem-gcc43-mt-1_37.a /usr/local/lib/libboost_regex-gcc43-mt-1_37.a /usr/local/lib/libboost_program_options-gcc43-mt-1_37.a diff --git a/contrib/organize/helpers.cpp b/contrib/organize/helpers.cpp deleted file mode 100644 index 18a5605f..00000000 --- a/contrib/organize/helpers.cpp +++ /dev/null @@ -1,635 +0,0 @@ -// ***************************************************************** -*- C++ -*- -/* - * Copyright (C) 2009 Brad Schick - * - * This file is part of the organize tool. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA. - */ -// ***************************************************************************** - -#include -#include -#include -#include -#include -#include -#include -#include -#include -//#include -#include -#include -#include -#include "helpers.hpp" - -#define BOOST_FILESYSTEM_NO_DEPRECATED - -namespace fs = boost::filesystem; -typedef Exiv2::ExifData::const_iterator (*EasyAccessFct)(const Exiv2::ExifData& ed); - - -std::string scrub(const std::string &dirty, bool strip_space = false) -{ - std::string scrub = boost::trim_copy(dirty); - if(strip_space) { - boost::regex space("\\s"); - scrub = boost::regex_replace(scrub, space, ""); - } - boost::regex dash("[:/\\\\|<>]"); - boost::regex under("[\"'\\[\\]\\{\\}#=%\\$\\?,\\+\\*]"); - scrub = boost::regex_replace(scrub, dash, "-"); - - return boost::regex_replace(scrub, under, "_"); -} - -bool exif_data(const Exiv2::Image *image, const char *key, Exiv2::ExifData::const_iterator &md) -{ - assert(image && key); - bool ok = false; - try { - const Exiv2::ExifData &exifData = image->exifData(); - Exiv2::ExifKey exifKey(key); - md = exifData.findKey(exifKey); - if(md != exifData.end() && md->typeId() != Exiv2::undefined) - ok = true; - } - catch(const Exiv2::AnyError&) { - } - return ok; -} - -bool exif_data_easy(const Exiv2::Image *image, EasyAccessFct easy, Exiv2::ExifData::const_iterator &md) -{ - assert(image && easy); - bool ok = false; - try { - const Exiv2::ExifData &exifData = image->exifData(); - md = easy(exifData); - if(md != exifData.end() && md->typeId() != Exiv2::undefined) - ok = true; - } - catch(const Exiv2::AnyError&) { - } - return ok; -} - - -bool iptc_data(const Exiv2::Image *image, const char *key, Exiv2::IptcData::const_iterator &md) -{ - bool ok = false; - assert(image && key); - try { - const Exiv2::IptcData &iptcData = image->iptcData(); - Exiv2::IptcKey iptcKey(key); - md = iptcData.findKey(iptcKey); - if(md != iptcData.end() && md->typeId() != Exiv2::undefined) - ok = true; - } - catch(const Exiv2::AnyError&) { - } - return ok; -} - -std::string exif_date(const Exiv2::Image *image, const fs::path &) -{ - Exiv2::ExifData::const_iterator md; - bool done = exif_data(image, "Exif.Photo.DateTimeDigitized", md); - if(!done) - done = exif_data(image, "Exif.Photo.DateTimeOriginal", md); - if(!done) - return ""; - - std::string date = scrub(md->print().substr(0,10)); - // Some files have zeros for dates, just fail in that case - if(boost::lexical_cast(date.substr(0,4))==0) - return ""; - - return date; -} - -std::string exif_year(const Exiv2::Image *image, const fs::path &path) -{ - std::string date = exif_date(image, path); - if(date.length()) - return date.substr(0,4); - else - return date; -} - -std::string exif_month(const Exiv2::Image *image, const fs::path &path) -{ - std::string date = exif_date(image, path); - if(date.length()) - return date.substr(5,2); - else - return date; -} - -std::string exif_day(const Exiv2::Image *image, const fs::path &path) -{ - std::string date = exif_date(image, path); - if(date.length()) - return date.substr(8,2); - else - return date; -} - -bool iptc_get_date(const Exiv2::Image *image, Exiv2::DateValue::Date &date) -{ - Exiv2::IptcData::const_iterator md; - bool done = iptc_data(image, "Iptc.Application2.DigitizationDate", md); - if(!done) - done = iptc_data(image, "Iptc.Application2.DateCreated", md); - if(!done) - return false; - date = ((Exiv2::DateValue*)md->getValue().get())->getDate(); - return date.year > 0; -} - -std::string iptc_date(const Exiv2::Image *image, const fs::path &) -{ - Exiv2::DateValue::Date date; - if(iptc_get_date(image, date)) - return str(boost::format("%4d-%02d-%02d") % date.year % date.month % date.day); - else - return ""; -} - -std::string iptc_year(const Exiv2::Image *image, const fs::path &) -{ - Exiv2::DateValue::Date date; - if(iptc_get_date(image, date)) - return str(boost::format("%4d") % date.year); - else - return ""; -} - -std::string iptc_month(const Exiv2::Image *image, const fs::path &) -{ - Exiv2::DateValue::Date date; - if(iptc_get_date(image, date)) - return str(boost::format("%02d") % date.month); - else - return ""; -} - -std::string iptc_day(const Exiv2::Image *image, const fs::path &) -{ - Exiv2::DateValue::Date date; - if(iptc_get_date(image, date)) - return str(boost::format("%02d") % date.day); - else - return ""; -} - -bool file_get_tm(const fs::path &path, std::tm &tm) -{ - std::time_t timer = fs::last_write_time(path); - if(time > 0) { - tm = *localtime(&timer); - return true; - } - else { - return false; - } -} - -std::string file_date(const Exiv2::Image *, const fs::path &path) -{ - std::tm tm; - if(file_get_tm(path, tm)) - return str(boost::format("%4d-%02d-%02d") % (tm.tm_year + 1900) % (tm.tm_mon + 1) % tm.tm_mday); - else - return ""; -} - -std::string file_year(const Exiv2::Image *, const fs::path &path) -{ - std::tm tm; - if(file_get_tm(path, tm)) - return str(boost::format("%4d") % (tm.tm_year + 1900)); - else - return ""; -} - -std::string file_month(const Exiv2::Image *, const fs::path &path) -{ - std::tm tm; - if(file_get_tm(path, tm)) - return str(boost::format("%02d") % (tm.tm_mon + 1)); - else - return ""; -} - -std::string file_day(const Exiv2::Image *, const fs::path &path) -{ - std::tm tm; - if(file_get_tm(path, tm)) - return str(boost::format("%02d") % tm.tm_mday); - else - return ""; -} - -/* -std::string xmp_date(const Exiv2::Image *image, const fs::path &) -{ - return ""; -} - -std::string xmp_year(const Exiv2::Image *image, const fs::path &) -{ - return ""; -} - -std::string xmp_month(const Exiv2::Image *image, const fs::path &) -{ - return ""; -} - -std::string xmp_day(const Exiv2::Image *image, const fs::path &) -{ - return ""; -}*/ - -std::string exif_time(const Exiv2::Image *image, const fs::path &) -{ - Exiv2::ExifData::const_iterator md; - bool done = exif_data(image, "Exif.Photo.DateTimeDigitized", md); - if(!done) - done = exif_data(image, "Exif.Photo.DateTimeOriginal", md); - if(!done) - return ""; - - std::string datetime = md->print(); - // Some files have zeros for dates, just fail in that case - if(boost::lexical_cast(datetime.substr(0,4)) == 0) - return ""; - - return scrub(datetime.substr(11)); -} - -std::string exif_hour(const Exiv2::Image *image, const fs::path &path) -{ - std::string time = exif_time(image, path); - if(time.length()) - return time.substr(0,2); - else - return time; -} - -std::string exif_minute(const Exiv2::Image *image, const fs::path &path) -{ - std::string time = exif_time(image, path); - if(time.length()) - return time.substr(3,2); - else - return time; -} - -std::string exif_second(const Exiv2::Image *image, const fs::path &path) -{ - std::string time = exif_time(image, path); - if(time.length()) - return time.substr(6,2); - else - return time; -} - -bool iptc_get_time(const Exiv2::Image *image, Exiv2::TimeValue::Time &time) -{ - Exiv2::IptcData::const_iterator md; - bool done = iptc_data(image, "Iptc.Application2.DigitizationTime", md); - if(!done) - done = iptc_data(image, "Iptc.Application2.TimeCreated", md); - if(!done) - return false; - time = ((Exiv2::TimeValue*)md->getValue().get())->getTime(); - // Zero is a valid time, so this one is hard to check. - return true; -} - -std::string iptc_time(const Exiv2::Image *image, const fs::path &) -{ - Exiv2::TimeValue::Time time; - if(iptc_get_time(image, time)) - return str(boost::format("%02d-%02d-%02d") % time.hour % time.minute % time.second); - else - return ""; -} - -std::string iptc_hour(const Exiv2::Image *image, const fs::path &) -{ - Exiv2::TimeValue::Time time; - if(iptc_get_time(image, time)) - return str(boost::format("%02d") % time.hour); - else - return ""; -} - -std::string iptc_minute(const Exiv2::Image *image, const fs::path &) -{ - Exiv2::TimeValue::Time time; - if(iptc_get_time(image, time)) - return str(boost::format("%02d") % time.minute); - else - return ""; -} - -std::string iptc_second(const Exiv2::Image *image, const fs::path &) -{ - Exiv2::TimeValue::Time time; - if(iptc_get_time(image, time)) - return str(boost::format("%02d") % time.second); - else - return ""; -} - -std::string file_time(const Exiv2::Image *, const fs::path &path) -{ - std::tm tm; - if(file_get_tm(path, tm)) - return str(boost::format("%02d-%02d-%02d") % tm.tm_hour % tm.tm_min % tm.tm_sec); - else - return ""; -} - -std::string file_hour(const Exiv2::Image *, const fs::path &path) -{ - std::tm tm; - if(file_get_tm(path, tm)) - return str(boost::format("%02d") % tm.tm_hour); - else - return ""; -} - -std::string file_minute(const Exiv2::Image *, const fs::path &path) -{ - std::tm tm; - if(file_get_tm(path, tm)) - return str(boost::format("%02d") % tm.tm_min); - else - return ""; -} - -std::string file_second(const Exiv2::Image *, const fs::path &path) -{ - std::tm tm; - if(file_get_tm(path, tm)) - return str(boost::format("%02d") % tm.tm_sec); - else - return ""; -} - -/*std::string xmp_time(const Exiv2::Image *image, const fs::path &) -{ - return ""; -} - -std::string xmp_hour(const Exiv2::Image *image, const fs::path &) -{ - return ""; -} - -std::string xmp_minute(const Exiv2::Image *image, const fs::path &) -{ - return ""; -} - -std::string xmp_second(const Exiv2::Image *image, const fs::path &) -{ - return ""; -}*/ - -std::string exif_dimension(const Exiv2::Image *image, const fs::path &path) -{ - return exif_width(image, path) + "-" + exif_height(image, path); -} - -std::string exif_width(const Exiv2::Image *image, const fs::path &) -{ - Exiv2::ExifData::const_iterator md; - bool done = exif_data(image, "Exif.Photo.PixelXDimension", md); - if(!done) - return ""; - return scrub(md->print()); -} - -std::string exif_height(const Exiv2::Image *image, const fs::path &) -{ - Exiv2::ExifData::const_iterator md; - bool done = exif_data(image, "Exif.Photo.PixelYDimension", md); - if(!done) - return ""; - return scrub(md->print()); -} - -std::string file_dimension(const Exiv2::Image *image, const fs::path &path) -{ - if(image) - return file_width(image, path) + "-" + file_height(image, path); - else - return ""; -} - -std::string file_width(const Exiv2::Image *image, const fs::path &) -{ - if(image) - return str(boost::format("%02d") % image->pixelWidth()); - else - return ""; -} - -std::string file_height(const Exiv2::Image *image, const fs::path &) -{ - if(image) - return str(boost::format("%02d") % image->pixelHeight()); - else - return ""; -} - -/* -std::string xmp_dimension(const Exiv2::Image *image, const fs::path &) -{ - return "" -} - -std::string xmp_width(const Exiv2::Image *image, const fs::path &) -{ - return ""; -} - -std::string xmp_height(const Exiv2::Image *image, const fs::path &) -{ - return ""; -}*/ - -std::string exif_model(const Exiv2::Image *image, const fs::path &) -{ - Exiv2::ExifData::const_iterator md; - bool done = exif_data(image, "Exif.Image.Model", md); - if(!done) - return ""; - return scrub(md->print()); -} - -std::string exif_make(const Exiv2::Image *image, const fs::path &) -{ - Exiv2::ExifData::const_iterator md; - bool done = exif_data(image, "Exif.Image.Make", md); - if(!done) - return ""; - return scrub(md->print()); -} - -/*std::string xmp_model(const Exiv2::Image *image, const fs::path &) -{ - return ""; -}*/ - -std::string exif_speed(const Exiv2::Image *image, const fs::path &) -{ - Exiv2::ExifData::const_iterator md; - bool done = exif_data(image, "Exif.Photo.ShutterSpeedValue", md); - if(!done) - done = exif_data(image, "Exif.Photo.ExposureTime", md); - if(!done) - return ""; - return scrub(md->print()); -} - -/*std::string xmp_speed(const Exiv2::Image *image, const fs::path &) -{ - return ""; -}*/ - -std::string exif_aperture(const Exiv2::Image *image, const fs::path &) -{ - Exiv2::ExifData::const_iterator md; - bool done = exif_data(image, "Exif.Photo.ApertureValue", md); - if(!done) - done = exif_data(image, "Exif.Photo.FNumber", md); - if(!done) - return ""; - return scrub(md->print()); -} - -/*std::string xmp_aperture(const Exiv2::Image *image, const fs::path &) -{ - return ""; -}*/ - -std::string exif_focal(const Exiv2::Image *image, const fs::path &) -{ - Exiv2::ExifData::const_iterator md; - bool done = exif_data(image, "Exif.Photo.FocalLength", md); - if(!done) - return ""; - return scrub(md->print()); -} - -/*std::string xmp_focal(const Exiv2::Image *image, const fs::path &) -{ - return ""; -}*/ - -std::string exif_distance(const Exiv2::Image *image, const fs::path &) -{ - Exiv2::ExifData::const_iterator md; - bool done = exif_data(image, "Exif.Photo.SubjectDistance", md); - if(!done) - return ""; - return scrub(md->print()); -} - -/*std::string xmp_distance(const Exiv2::Image *image, const fs::path &) -{ - return ""; -}*/ - -std::string exif_meter(const Exiv2::Image *image, const fs::path &) -{ - Exiv2::ExifData::const_iterator md; - bool done = exif_data(image, "Exif.Photo.MeteringMode", md); - if(!done) - return ""; - return scrub(md->print()); -} - -std::string exif_macro(const Exiv2::Image *image, const fs::path &) -{ - Exiv2::ExifData::const_iterator md; - bool done = exif_data_easy(image, Exiv2::macroMode, md); - if(!done) - return ""; - return scrub(md->print()); -} - -std::string exif_orientation(const Exiv2::Image *image, const fs::path &) -{ - Exiv2::ExifData::const_iterator md; - bool done = exif_data_easy(image, Exiv2::orientation, md); - if(!done) - return ""; - return scrub(md->print(), true); -} - -std::string exif_lens(const Exiv2::Image *image, const fs::path &) -{ - Exiv2::ExifData::const_iterator md; - bool done = exif_data_easy(image, Exiv2::lensName, md); - if(!done) - return ""; - return scrub(md->print()); -} - - -std::string exif_iso(const Exiv2::Image *image, const fs::path &) -{ - Exiv2::ExifData::const_iterator md; - bool done = exif_data_easy(image, Exiv2::isoSpeed, md); - if(!done) - return ""; - return scrub(md->print()); -} - -/*std::string xmp_meter(const Exiv2::Image *image, const fs::path &) -{ - return ""; -}*/ - -std::string exif_keyword(const Exiv2::Image *image, const fs::path &) -{ - Exiv2::ExifData::const_iterator md; - bool done = exif_data(image, "Exif.Photo.UserComment", md); - if(!done) - return ""; - return scrub(md->print()); -} - -std::string iptc_keyword(const Exiv2::Image *image, const fs::path &) -{ - Exiv2::IptcData::const_iterator md; - bool done = iptc_data(image, "Iptc.Application2.Keywords", md); - if(!done) - return ""; - return scrub(md->print()); -} - -/*std::string xmp_keyword(const Exiv2::Image *image, const fs::path &) -{ - return ""; -}*/ - diff --git a/contrib/organize/helpers.hpp b/contrib/organize/helpers.hpp deleted file mode 100644 index 5a85e28b..00000000 --- a/contrib/organize/helpers.hpp +++ /dev/null @@ -1,101 +0,0 @@ -// ***************************************************************** -*- C++ -*- -/* - * Copyright (C) 2004-2021 Exiv2 authors - * This program is part of the Exiv2 distribution. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA. - */ -// ***************************************************************************** - - -#ifndef HELPERS_HPP_ -#define HELPERS_HPP_ - -#include - -#define BOOST_FILESYSTEM_NO_DEPRECATED -namespace fs = boost::filesystem; - - -typedef std::string (*pfunc)(const Exiv2::Image *image, const fs::path &path); - -// This would be a lot smaller if Exiv2 had support -// for unified metadata - -std::string exif_date(const Exiv2::Image *image, const fs::path &path); -std::string exif_year(const Exiv2::Image *image, const fs::path &path); -std::string exif_month(const Exiv2::Image *image, const fs::path &path); -std::string exif_day(const Exiv2::Image *image, const fs::path &path); -std::string iptc_date(const Exiv2::Image *image, const fs::path &path); -std::string iptc_year(const Exiv2::Image *image, const fs::path &path); -std::string iptc_month(const Exiv2::Image *image, const fs::path &path); -std::string iptc_day(const Exiv2::Image *image, const fs::path &path); -std::string file_date(const Exiv2::Image *image, const fs::path &path); -std::string file_year(const Exiv2::Image *image, const fs::path &path); -std::string file_month(const Exiv2::Image *image, const fs::path &path); -std::string file_day(const Exiv2::Image *image, const fs::path &path); -/*std::string xmp_date(const Exiv2::Image *image, const fs::path &path); -std::string xmp_year(const Exiv2::Image *image, const fs::path &path); -std::string xmp_month(const Exiv2::Image *image, const fs::path &path); -std::string xmp_day(const Exiv2::Image *image, const fs::path &path);*/ -std::string exif_time(const Exiv2::Image *image, const fs::path &path); -std::string exif_hour(const Exiv2::Image *image, const fs::path &path); -std::string exif_minute(const Exiv2::Image *image, const fs::path &path); -std::string exif_second(const Exiv2::Image *image, const fs::path &path); -std::string iptc_time(const Exiv2::Image *image, const fs::path &path); -std::string iptc_hour(const Exiv2::Image *image, const fs::path &path); -std::string iptc_minute(const Exiv2::Image *image, const fs::path &path); -std::string iptc_second(const Exiv2::Image *image, const fs::path &path); -std::string file_time(const Exiv2::Image *image, const fs::path &path); -std::string file_hour(const Exiv2::Image *image, const fs::path &path); -std::string file_minute(const Exiv2::Image *image, const fs::path &path); -std::string file_second(const Exiv2::Image *image, const fs::path &path); -/*std::string xmp_time(const Exiv2::Image *image, const fs::path &path); -std::string xmp_hour(const Exiv2::Image *image, const fs::path &path); -std::string xmp_minute(const Exiv2::Image *image, const fs::path &path); -std::string xmp_second(const Exiv2::Image *image, const fs::path &path);*/ -std::string exif_dimension(const Exiv2::Image *image, const fs::path &path); -std::string exif_width(const Exiv2::Image *image, const fs::path &path); -std::string exif_height(const Exiv2::Image *image, const fs::path &path); -std::string file_dimension(const Exiv2::Image *image, const fs::path &path); -std::string file_width(const Exiv2::Image *image, const fs::path &path); -std::string file_height(const Exiv2::Image *image, const fs::path &path); -/*std::string xmp_dimension(const Exiv2::Image *image, const fs::path &path); -std::string xmp_width(const Exiv2::Image *image, const fs::path &path); -std::string xmp_height(const Exiv2::Image *image, const fs::path &path);*/ -std::string exif_model(const Exiv2::Image *image, const fs::path &path); -std::string exif_make(const Exiv2::Image *image, const fs::path &path); -/*std::string xmp_model(const Exiv2::Image *image, const fs::path &path); -std::string xmp_make(const Exiv2::Image *image, const fs::path &path);*/ -std::string exif_speed(const Exiv2::Image *image, const fs::path &path); -//std::string xmp_speed(const Exiv2::Image *image, const fs::path &path); -std::string exif_aperture(const Exiv2::Image *image, const fs::path &path); -//std::string xmp_aperture(const Exiv2::Image *image, const fs::path &path); -std::string exif_focal(const Exiv2::Image *image, const fs::path &path); -//std::string xmp_focal(const Exiv2::Image *image, const fs::path &path); -std::string exif_distance(const Exiv2::Image *image, const fs::path &path); -//std::string xmp_distance(const Exiv2::Image *image, const fs::path &path); -std::string exif_meter(const Exiv2::Image *image, const fs::path &path); -//std::string xmp_meter(const Exiv2::Image *image, const fs::path &path); -std::string exif_macro(const Exiv2::Image *image, const fs::path &path); -std::string exif_orientation(const Exiv2::Image *image, const fs::path &path); -std::string exif_lens(const Exiv2::Image *image, const fs::path &path); -std::string exif_keyword(const Exiv2::Image *image, const fs::path &path); -std::string iptc_keyword(const Exiv2::Image *image, const fs::path &path); -//std::string xmp_keyword(const Exiv2::Image *image, const fs::path &path); -std::string exif_iso(const Exiv2::Image *image, const fs::path &path); - -#endif //HELPERS_HPP_ - diff --git a/contrib/organize/organize.cpp b/contrib/organize/organize.cpp deleted file mode 100644 index a176a6bc..00000000 --- a/contrib/organize/organize.cpp +++ /dev/null @@ -1,759 +0,0 @@ -// ***************************************************************** -*- C++ -*- -/* - * Copyright (C) 2004-2021 Exiv2 authors - * This program is part of the Exiv2 distribution. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA. - */ -// ***************************************************************************** - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "MD5.h" -#include "helpers.hpp" - -typedef Exiv2::byte md5digest[16]; - -namespace po = boost::program_options; - -bool g_verbose = false; -bool g_neednewline = false; - -// Array size should match number of SLOTs -boost::array g_run_order = {{-1, -1, -1, -1}}; -const int EXIF_SLOT = 0; -const int IPTC_SLOT = 1; -const int XMP_SLOT = 2; -const int FILE_SLOT = 3; - -const unsigned DOT_EVERY = 55; - -struct Pattern { - std::string pat; - std::string desc; - pfunc funcs[4]; // order should always be exif, iptc, xmp, file -}; - -struct PathPart { - std::string pre; - const Pattern *pat; - std::string post; - PathPart(std::string pre_, const Pattern *pat_, std::string post_) - : pre(pre_), pat(pat_), post(post_) {} -}; - -std::vector g_path_parts; - -// Instead of making these all global -struct ProcessParams { - const fs::path &dest_dir; - const bool dry_run; - const bool ignore_dups; - const bool ignore_unsorted; - const bool force; - const bool rename; - const bool symlink; - const bool verify; - const bool move; - const long limit_depth; - const fs::path &dups_dir; - const fs::path &unsorted_dir; - const std::vector &excludes; - unsigned dups_count; - unsigned unsorted_count; - unsigned dir_err_count; - unsigned file_err_count; - unsigned ok_count; - unsigned dups_ignored_count; - unsigned unsorted_ignored_count; - unsigned dir_ex_count; - unsigned file_ex_count; -}; - -void process_directory(const fs::path &directory, const long depth, - ProcessParams ¶ms); - -const Pattern g_patterns[] = { - {"@date", "date captured (2009-01-19)", - {exif_date, iptc_date, NULL, file_date} }, - {"@year", "year captured (2009)", - {exif_year, iptc_year, NULL, file_year} }, - {"@month", "month captured (01)", - {exif_month, iptc_month, NULL, file_month} }, - {"@day", "day captured (19)", - {exif_day, iptc_day, NULL, file_day} }, - {"@time", "time captured (14-35-27)", - {exif_time, iptc_time, NULL, file_time} }, - {"@hour", "hour captured (14)", - {exif_hour, iptc_hour, NULL, file_hour} }, - {"@min", "minute captured (35)", - {exif_minute, iptc_minute, NULL, file_minute} }, - {"@sec", "second captured (27)", - {exif_second, iptc_second, NULL, file_second} }, - {"@dim", "pixel dimension (2272-1704)", - {exif_dimension, NULL, NULL, file_dimension} }, - {"@x", "pixel width (2272)", - {exif_width, NULL, NULL, file_width} }, - {"@y", "pixel height (1704)", - {exif_height, NULL, NULL, file_height} }, - {"@make", "device make (Canon)", - {exif_make, NULL, NULL, NULL} }, - {"@model", "device model (Canon PowerShot S40)", - {exif_model, NULL, NULL, NULL} }, - {"@speed", "shutter speed (1-60)", - {exif_speed, NULL, NULL, NULL} }, - {"@aper", "aperture (F3.2)", - {exif_aperture, NULL, NULL, NULL} }, - {"@iso", "iso speed (400)", - {exif_iso, NULL, NULL, NULL} }, - {"@focal", "focal length (8.6 mm)", - {exif_focal, NULL, NULL, NULL} }, - {"@dist", "subject distance (1.03 m)", - {exif_distance, NULL, NULL, NULL} }, - {"@meter", "meter mode (multi-segment)", - {exif_meter, NULL, NULL, NULL} }, - {"@macro", "macro mode (Off)", - {exif_macro, NULL, NULL, NULL} }, - {"@orient", "orientation (top_left)", - {exif_orientation, NULL, NULL, NULL} }, - {"@lens", "lens name (Tamron 90mm f-2.8)", - {exif_lens, NULL, NULL, NULL} }, - {"@key", "first keyword (Family)", - {exif_keyword, iptc_keyword, NULL, NULL} }, - - {"", "", {NULL, NULL, NULL, NULL} } -}; - - -// Check that 'opt1' and 'opt2' are not specified at the same time. -void conflicting(const po::variables_map& vm, - const char* opt1, const char* opt2) -{ - if (vm.count(opt1) && !vm[opt1].defaulted() - && vm.count(opt2) && !vm[opt2].defaulted()) { - throw std::logic_error(std::string("conflicting options '") - + opt1 + "' and '" + opt2 + "'"); - } -} - -// Check that 'required' is present -void required(const po::variables_map& vm, const char* required) -{ - if (!vm.count(required) || vm[required].defaulted()) { - throw std::logic_error(std::string("required parameter '") + required - + "' is missing"); - } -} - -void info(const std::string &msg) -{ - if(g_verbose) { - std::cout << msg << "\n"; - g_neednewline = false; - } -} - -void error(const std::exception &e, const std::string &msg) -{ - if(g_neednewline) { - std::cout << "\n"; - g_neednewline = false; - } - std::cerr << e.what() << "\n"; - std::cerr << msg << std::endl; -} - -void usage_header(const char* exname) -{ - std::cout << "Usage: " << exname << " [options] source-dir dest-dir pattern\n"; -} - -void usage_full(const po::options_description &options, const char* exname) -{ - usage_header(exname); - std::cout << "\n Creates groups of files in new directories defined by a metadata 'pattern'.\n" << - " Files are copied, moved, or linked from 'source-dir' to 'dest-dir'.\n" << - " The destination directory should not be within the source directory.\n\n"; - std::cout << options; - - std::cout << "\nPattern values:\n"; - for( const Pattern *pattern = g_patterns; pattern->pat.length(); ++pattern) { - std::cout << " " << std::setw(8) << std::left << pattern->pat; - std::cout << pattern->desc << "\n"; - } - - std::cout << "\nExamples:\n"; - std::cout << " `" << exname << " -m mess clean @year-@month'\n"; - std::cout << " Moves files from 'mess' into directories of 'clean' according to\n" << - " year-month the file was captured (clean/2006-11/...)\n\n"; - std::cout << " `" << exname << " -o ie source find width-@x/height-@y'\n"; - std::cout << " Copies files into directories according first to pixel width then pixel\n" << - " height. Check iptc then exif metadata (find/width-2272/height-1704/...)\n\n"; - std::cout << " `" << exname << " -lf source find @aper/@hour'\n"; - std::cout << " Force create symlinks in directories according first to aperture then\n" << - " hour captured (find/F3.2/15/...)\n"; - - std::cout << std::endl; -} - -void version() -{ - std::cout << "organized 0.1\n" << - "Copyright (C) 2009 Brad Schick. \n\n" << - "This program is free software; you can redistribute it and/or\n" - "modify it under the terms of the GNU General Public License\n" - "as published by the Free Software Foundation; either version 2\n" - "of the License, or (at your option) any later version.\n" - "\n" - "This program is distributed in the hope that it will be useful,\n" - "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" - "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" - "GNU General Public License for more details.\n" - "\n" - "You should have received a copy of the GNU General Public\n" - "License along with this program; if not, write to the Free\n" - "Software Foundation, Inc., 51 Franklin Street, Fifth Floor,\n" - "Boston, MA 02110-1301 USA" << std::endl; -} - -// Returns empty string if the destination subdirectory could not be determined -// for the supplied source file. -std::string build_dest(const fs::path &source_file) -{ - std::string dest; - - Exiv2::Image::AutoPtr image; - try { - image = Exiv2::ImageFactory::open(source_file.string()); - image->readMetadata(); - } - catch(const Exiv2::AnyError&) { - // No metadata, let things continue to try file info - } - - std::vector::iterator iter = g_path_parts.begin(); - std::vector::iterator end = g_path_parts.end(); - for( ; iter != end; ++iter) { - dest += iter->pre; - std::string result; - - const Pattern *pat = iter->pat; - for(unsigned fx = 0; fx < g_run_order.size(); ++fx) { - if(g_run_order[fx] != -1 && pat->funcs[g_run_order[fx]]) { - if(g_run_order[fx] == FILE_SLOT) { - // Always run file operations - result = pat->funcs[g_run_order[fx]](image.get(), source_file); - } - else if(image.get()) { - // No point in running metadata operations without an image - result = pat->funcs[g_run_order[fx]](image.get(), source_file); - } - if(result.length()) - break; - } - } - // If we found no data, even for part of pattern, give up and - // return no destination - if(!result.length()) - return result; - - dest += (result + iter->post); - } - return dest; -} - -bool md5sum(const fs::path &path, md5digest &digest) -{ - try { - Exiv2::FileIo io(path.string()); - if (io.open() != 0) - return false; - Exiv2::IoCloser closer(io); - - Exiv2::byte buff[4096]; - MD5_CTX context; - MD5Init(&context); - - long read_count = io.read(buff, 4096); - while(read_count) { - MD5Update(&context, buff, read_count); - read_count = io.read(buff, 4096); - } - MD5Final(digest, &context); - return true; - } - catch (std::exception& ) { - return false; - } -} - - -int main(int argc, char* argv[]) -{ - po::options_description options("Options"); - // Don't use default values because the help print it ugly and too wide - options.add_options() - ("move,m", "move files rather than copy") - ("symlink,s", "symlink files rather than copy (posix only)") - ("order,o", po::value(), - "order and types of metadata to read\ne=exif, i=iptc, f=file (default: eif)") - ("unsorted,u", po::value(), - "special directory to store unsorted files (default: unsorted)") - ("dups,d", po::value(), - "special directory to store files with duplicate names (default: duplicates)") - ("force,f", "overwrite duplicate files instead of using special directory") - ("rename,r", "rename duplicate files instead of using special directory") - ("ignore,i", "ignore both unsorted and duplicate files instead of using special directories") - ("ignore-unsorted", "ignore unsorted files instead of using special directory") - ("ignore-dups", "ignore duplicate files instead of using special directory") - ("verify", "verify copied or moved files and exit if incorrect") - ("exclude,x", po::value< std::vector >(), - "exclude directories and files that contain arg (case sensitive on all platforms)") - ("limit-depth,l", po::value(), - "limit recursion to specified depth (0 disables recursion)") - ("verbose,v", "prints operations as they happen") - ("dry-run,n", "do not make actual changes (implies verbose)") - ("help,h", "show this help message then exit") - ("version,V", "show program version then exit") - ; - - po::options_description hidden("Hidden Options"); - hidden.add_options() - ("source-dir", po::value< std::string >(), "directory of files to organize, may end in file wildcard") - ("dest-dir", po::value< std::string >(), "designation directory for files, may not be within source-dir") - ("pattern", po::value< std::string >(), "subdirectory pattern for grouping files within dest-dir") - ; - - po::options_description cmdline; - cmdline.add(options).add(hidden); - - po::positional_options_description positional; - positional.add("source-dir", 1); - positional.add("dest-dir", 1); - positional.add("pattern", 1); - - try { - po::variables_map vm; - po::store(po::command_line_parser(argc, argv). - options(cmdline).positional(positional).run(), vm); - po::notify(vm); - - if (vm.count("help")) { - usage_full(options, argv[0]); - return 0; - } - - if (vm.count("version")) { - version(); - return 0; - } - - conflicting(vm, "verify", "symlink"); - conflicting(vm, "move", "symlink"); - conflicting(vm, "unsorted", "ignore"); - conflicting(vm, "unsorted", "ignore-unsorted"); - conflicting(vm, "dups", "ignore"); - conflicting(vm, "dups", "ignore-dups"); - conflicting(vm, "force", "ignore"); - conflicting(vm, "force", "ignore-dups"); - conflicting(vm, "force", "rename"); - conflicting(vm, "rename", "ignore"); - conflicting(vm, "rename", "ignore-dups"); - required(vm, "source-dir"); - required(vm, "dest-dir"); - required(vm, "pattern"); - - const bool dry_run = vm.count("dry-run") != 0; - g_verbose = (vm.count("verbose") != 0 || dry_run); - - std::string order = "eif"; - if(vm.count("order")) { - order = vm["order"].as(); - - boost::to_lower(order); - if(order.length() > 3) { - throw std::logic_error(std::string("order is longer than 4 characters")); - } - } - - unsigned i = 0; - std::string::iterator end = order.end(); - for(std::string::iterator iter = order.begin(); iter != end && i < 4; ++iter, ++i) { - switch(*iter) { - case 'e': - g_run_order[i] = EXIF_SLOT; - break; - case 'i': - g_run_order[i] = IPTC_SLOT; - break; - case 'x': - throw std::logic_error(std::string("xmp not implemented yet '") + - *iter + "'"); - break; - case 'f': - g_run_order[i] = FILE_SLOT; - break; - default: - throw std::logic_error(std::string("unknown order character '") + - *iter + "'"); - } - } - - const fs::path source_dir( vm["source-dir"].as() ); - if( !exists(source_dir) || !is_directory(source_dir) ) { - throw std::logic_error(std::string("source '") + - source_dir.string() + "' must exist and be a directory"); - } - - const fs::path dest_dir( vm["dest-dir"].as() ); - if( exists(dest_dir) && !is_directory(dest_dir) ) { - throw std::logic_error(std::string("destination '") + - dest_dir.string() + "' must be a directory"); - } - - // Boost doesn't seem to have a way to get a canonical path, so this - // simple test is easy to confuse with some ../../'s in the paths. Oh - // well, this is good enough for now. - fs::path test_dest(dest_dir); - for(; !test_dest.empty(); test_dest = test_dest.parent_path()) { - if(fs::equivalent(source_dir, test_dest)) { - throw std::logic_error(std::string("dest-dir must not be within source-dir")); - } - } - - // Disect the pattern - std::string pattern = vm["pattern"].as(); - boost::regex regex( "([^@]*)(@[[:alpha:]]+)([^@]*)"); - boost::sregex_iterator m_iter = make_regex_iterator(pattern, regex); - boost::sregex_iterator m_end; - for( ; m_iter != m_end; ++m_iter) { - const boost::smatch &match = *m_iter; - const std::string &pre = match[1]; - const std::string &pat = match[2]; - const std::string &post = match[3]; - - // Should put this in a map, but there aren't that many options now - bool found = false; - for( const Pattern *pattern = g_patterns; pattern->pat.length(); ++pattern) { - if(pattern->pat == pat) { - PathPart part(pre, pattern, post); - g_path_parts.push_back(part); - found = true; - break; - } - } - - if(!found) { - throw std::logic_error(std::string("unknown pattern '") + pat + "'"); - } - } - - // Assign defaults to params that need them - const bool ignore = vm.count("ignore") != 0; - std::vector excludes; - if(vm.count("exclude")) - excludes = vm["exclude"].as< std::vector >(); - long limit_depth = LONG_MAX; - if(vm.count("limit-depth")) { - limit_depth = vm["limit-depth"].as(); - // Boost program_options doesn't work with unsigned, so do it manually - if( limit_depth < 0 ) - throw std::logic_error(std::string("recursion depth limit must be positive")); - } - std::string dups = "duplicates"; - if(vm.count("dups")) - dups = vm["dups"].as(); - const fs::path dups_dir = dest_dir / dups; - - std::string unsorted = "unsorted"; - if(vm.count("unsorted")) - unsorted = vm["unsorted"].as(); - const fs::path unsorted_dir = dest_dir / unsorted; - - ProcessParams params = { - dest_dir, - dry_run, - (vm.count("ignore-dups") != 0 || ignore), - (vm.count("ignore-unsorted") != 0 || ignore), - vm.count("force") != 0, - vm.count("rename") != 0, - vm.count("symlink") != 0, - vm.count("verify") != 0, - vm.count("move") != 0, - limit_depth, - dups_dir, - unsorted_dir, - excludes, - 0, 0, 0, 0, 0, 0, 0, 0, 0 - }; - - process_directory(source_dir, 0, params); - - std::string op = "copied"; - if(params.symlink) - op = "linked"; - else if(params.move) - op = "moved"; - - if(dry_run) - op = std::string("would be ") + op; - - if(g_neednewline) - std::cout << "\n"; - - std::cout << "\n" << params.ok_count << " files " << op << "\n"; - std::cout << " " << params.dups_count << " duplicates\n"; - std::cout << " " << params.unsorted_count << " unsorted\n"; - if(params.dups_ignored_count) - std::cout << params.dups_ignored_count << " duplicates ignored\n"; - if(params.unsorted_ignored_count) - std::cout << params.unsorted_ignored_count << " unsorted ignored\n"; - if(params.dir_ex_count) - std::cout << params.dir_ex_count << " directories excluded\n"; - if(params.file_ex_count) - std::cout << params.file_ex_count << " files excluded\n"; - if(params.dir_err_count) - std::cout << params.dir_err_count << " directory errors\n"; - if(params.file_err_count) - std::cout << params.file_err_count << " file errors\n"; - - return 0; - } - catch (Exiv2::AnyError& e) { - error(e, std::string("Aborting")); - return -1; - } - catch(std::logic_error& e) { - error(e, ""); - usage_header(argv[0]); - std::cout << argv[0] << " -h for more help" << std::endl; - return -2; - } - catch(std::exception& e) { - error(e, "Aborting"); - return -3; - } -} - -boost::regex uregex("(.*?)\\(([[:digit:]]{1,2})\\)$"); - -fs::path uniquify(const fs::path &dest) -{ - std::string ext = dest.extension().string(); - std::string fname = dest.stem().string(); - fs::path parent = dest.parent_path(); - - unsigned number = 1; - std::string newfname; - fs::path newdest; - - boost::smatch match; - if(boost::regex_search(fname, match, uregex)) { - // Matches are indexes into fname, so don't change it while reading values - newfname = match[1]; - number = boost::lexical_cast(match[2]); - fname = newfname; - } - - do { - newfname = fname + "(" + boost::lexical_cast(++number) + ")" + ext; - newdest = parent / newfname; - } while(fs::exists(newdest)); - - return newdest; -} - -void process_directory(const fs::path &directory, const long depth, - ProcessParams ¶ms) -{ - // Exclude entire directories - bool exclude = false; - std::vector::const_iterator x_iter = params.excludes.begin(); - std::vector::const_iterator x_end = params.excludes.end(); - for( ; x_iter != x_end; ++x_iter ) { - if(boost::contains(directory.string(), *x_iter)) { - exclude = true; - break; - } - } - if(exclude) { - info(std::string("excluding directory: ") + directory.string() + - " matched: " + *x_iter); - ++params.dir_ex_count; - return; - } - - try { - fs::directory_iterator p_iter(directory), p_end; - for( ; p_iter != p_end; ++p_iter) { - if( is_directory(*p_iter) ) { - // recurse if we haven't hit the limit - if(depth < params.limit_depth) - process_directory(p_iter->path(), depth + 1, params); - else { - info(std::string("depth reached, skipping: ") + - p_iter->path().string()); - } - } - else if( is_regular_file(*p_iter) ) { - - // Check again for excluding file names - exclude = false; - x_iter = params.excludes.begin(); - for( ; x_iter != x_end; ++x_iter ) { - if(boost::contains(p_iter->path().string(), *x_iter)) { - exclude = true; - break; - } - } - if(exclude) { - info(std::string("excluding file: ") + p_iter->path().string() + - " matched: " + *x_iter); - ++params.file_ex_count; - continue; - } - - try { - const fs::path dest_subdir = build_dest(*p_iter); - fs::path dest_file; - if(!dest_subdir.empty()) - dest_file = params.dest_dir / dest_subdir; - else if(params.ignore_unsorted) { - info(std::string("ignoring unsorted: ") + p_iter->path().string()); - ++params.unsorted_ignored_count; - continue; - } - else { - info(std::string("unsorted file (missing metadata): ") + p_iter->path().string()); - dest_file = params.unsorted_dir; - ++params.unsorted_count; - } - - dest_file /= p_iter->path().filename(); - - if(fs::exists(dest_file)) { - if(params.ignore_dups) { - info(std::string("ignoring: ") + p_iter->path().string() + - " duplicates: " + dest_file.string()); - ++params.dups_ignored_count; - continue; - } - else { - if(params.force) { - info(std::string("force removing: ") + dest_file.string() + " for: " - + p_iter->path().string()); - if(!params.dry_run) - fs::remove(dest_file); - } - else if(params.rename) { - info(std::string("renaming: ") + p_iter->path().string() + - " duplicates: " + dest_file.string()); - dest_file = uniquify(dest_file); - } - else { - info(std::string("duplicate file: ") + p_iter->path().string() + - " of: " + dest_file.string()); - dest_file = params.dups_dir / dest_subdir / p_iter->path().filename(); - // Ugh, more dup possibilities - if(fs::exists(dest_file)) { - info(std::string("renaming: ") + p_iter->path().string() + - " duplicates: " + dest_file.string()); - dest_file = uniquify(dest_file); - } - } - ++params.dups_count; - } - } - - if(!params.dry_run) - fs::create_directories(dest_file.parent_path()); - - if(params.symlink) { - info(std::string("linking from: ") + p_iter->path().string() + - " to: " + dest_file.string()); - if(!params.dry_run) { - // The target of a symlink must be either absolute (aka complete) or - // relative to the location of the link. Easiest solution is to make - // a complete path. - fs::path target; - if(p_iter->path().is_complete()) - target = p_iter->path(); - else - target = fs::initial_path() / p_iter->path(); - fs::create_symlink(target, dest_file); - } - } - else { - info(std::string("copying from: ") + p_iter->path().string() + - " to: " + dest_file.string()); - if(!params.dry_run) { - // Copy the file and restore its write time (needed for posix) - std::time_t time = fs::last_write_time(*p_iter); - fs::copy_file(*p_iter, dest_file); - fs::last_write_time(dest_file, time); - if(params.verify) { - md5digest src_digest, dst_digest; - bool ok = md5sum(p_iter->path(), src_digest); - if(ok) - ok = md5sum(dest_file, dst_digest); - if(ok) - ok = (memcmp(src_digest,dst_digest, sizeof(md5digest))==0); - if(!ok) { - // Should probably find a more appropriate exception for this - throw std::runtime_error(std::string("File verification failed: '") - + p_iter->path().string() + "' differs from '" + - dest_file.string() + "'"); - } - else { - info(std::string("verification passed")); - } - } - } - } - if(params.move) { - info(std::string("removing: ") + p_iter->path().string()); - if(!params.dry_run) - fs::remove(*p_iter); - } - - if(!g_verbose && (params.ok_count % DOT_EVERY)==0) { - std::cout << "." << std::flush; - g_neednewline = true; - } - ++params.ok_count; - } - catch(fs::filesystem_error& e) { - error(e, std::string("skipping file: " + p_iter->path().string())); - ++params.file_err_count; - } - } - } - } - catch(fs::filesystem_error& e) { - error(e, std::string("skipping directory: " + directory.string())); - ++params.dir_err_count; - } -} -