Beispiel #1
0
static void usage_conflicts(void)
{
    usage_header();
    puts("Usage: bee dep conflicts\n\n"

         "Print conflicting packages.\n");
}
Beispiel #2
0
static void usage_update(void)
{
    usage_header();
    puts("Usage: bee dep update <pkgname>\n\n"

         "Update the information about a package.\n");
}
Beispiel #3
0
static void usage_rebuild(void)
{
    usage_header();
    puts("Usage: bee dep rebuild\n\n"

         "Rebuild the cache.\n");
}
Beispiel #4
0
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;
}
Beispiel #5
0
static void usage_remove(void)
{
    usage_header();
    puts("Usage: bee dep remove [options] <pkgname>\n\n"

         "Remove a package from the cache.\n\n"

         "Options:\n"
         "    --print    print which files can be deleted from the hard drive\n");
}
Beispiel #6
0
static void usage(void)
{
    usage_header();
    puts("Usage: bee dep <command> [<args>]\n\n"

         "Commands:\n"
         "    rebuild      rebuild the cache\n"
         "    update       update the cache for a specific package\n"
         "    remove       remove a package from the cache\n"
         "    list         list information\n"
         "    conflicts    show conflicting packages\n\n"

         "See 'bee dep <command> --help' for more information on a specific command.");
}
Beispiel #7
0
static void usage_list(void)
{
    usage_header();
    puts("Usage: bee dep list [options]\n\n"

         "Get information from the cache.\n\n"

         "Options:\n"
         "    --packages                   list all packages\n"
         "    --files        <pkg>         list all files of a package\n"
         "    --depending-on <pkg|file>    list packages depending on a pkg or file\n"
         "    --required-by  <pkg|file>    list packages required by a pkg or file\n"
         "    --removable    <pkg>         show all removable files of a package\n"
         "    --provider-of  <file>        show the providers of a file\n"
         "    --not-cached   <file>        print files which are not in the cache;\n"
         "                                 check those files which are listed in <file>\n"
         "    --broken                     list all broken dependencies\n"
         "    --count                      do not print results; just count\n");
}
Beispiel #8
0
int short_usage(const char *message, ...)
{
	va_list args;

	if(message) {
		va_start(args, message);

		(void) vfprintf(stderr, message, args);

		va_end(args);

	}
	usage_header(stderr);
	flac_fprintf(stderr, "\n");
	flac_fprintf(stderr, "This is the short help; for full help use 'metaflac --help'\n");
	flac_fprintf(stderr, "\n");
	usage_summary(stderr);

	return message? 1 : 0;
}
Beispiel #9
0
int long_usage(const char *message, ...)
{
	FILE *out = (message? stderr : stdout);
	va_list args;

	if(message) {
		va_start(args, message);

		(void) vfprintf(stderr, message, args);

		va_end(args);

	}
	usage_header(out);
	fprintf(out, "\n");
	usage_summary(out);
	fprintf(out, "\n");
	fprintf(out, "Shorthand operations:\n");
	fprintf(out, "--show-md5sum         Show the MD5 signature from the STREAMINFO block.\n");
	fprintf(out, "--show-min-blocksize  Show the minimum block size from the STREAMINFO block.\n");
	fprintf(out, "--show-max-blocksize  Show the maximum block size from the STREAMINFO block.\n");
	fprintf(out, "--show-min-framesize  Show the minimum frame size from the STREAMINFO block.\n");
	fprintf(out, "--show-max-framesize  Show the maximum frame size from the STREAMINFO block.\n");
	fprintf(out, "--show-sample-rate    Show the sample rate from the STREAMINFO block.\n");
	fprintf(out, "--show-channels       Show the number of channels from the STREAMINFO block.\n");
	fprintf(out, "--show-bps            Show the # of bits per sample from the STREAMINFO block.\n");
	fprintf(out, "--show-total-samples  Show the total # of samples from the STREAMINFO block.\n");
	fprintf(out, "\n");
	fprintf(out, "--show-vendor-tag     Show the vendor string from the VORBIS_COMMENT block.\n");
	fprintf(out, "--show-tag=NAME       Show all tags where the field name matches 'NAME'.\n");
	fprintf(out, "--remove-tag=NAME     Remove all tags whose field name is 'NAME'.\n");
	fprintf(out, "--remove-first-tag=NAME  Remove first tag whose field name is 'NAME'.\n");
	fprintf(out, "--remove-all-tags     Remove all tags, leaving only the vendor string.\n");
	fprintf(out, "--set-tag=FIELD       Add a tag.  The FIELD must comply with the Vorbis comment\n");
	fprintf(out, "                      spec, of the form \"NAME=VALUE\".  If there is currently\n");
	fprintf(out, "                      no tag block, one will be created.\n");
	fprintf(out, "--set-tag-from-file=FIELD   Like --set-tag, except the VALUE is a filename\n");
	fprintf(out, "                      whose contents will be read verbatim to set the tag value.\n");
	fprintf(out, "                      Unless --no-utf8-convert is specified, the contents will\n");
	fprintf(out, "                      be converted to UTF-8 from the local charset.  This can\n");
	fprintf(out, "                      be used to store a cuesheet in a tag (e.g.\n");
	fprintf(out, "                      --set-tag-from-file=\"CUESHEET=image.cue\").  Do not try\n");
	fprintf(out, "                      to store binary data in tag fields!  Use APPLICATION\n");
	fprintf(out, "                      blocks for that.\n");
	fprintf(out, "--import-tags-from=FILE Import tags from a file.  Use '-' for stdin.  Each line\n");
	fprintf(out, "                      should be of the form NAME=VALUE.  Multi-line comments\n");
	fprintf(out, "                      are currently not supported.  Specify --remove-all-tags\n");
	fprintf(out, "                      and/or --no-utf8-convert before --import-tags-from if\n");
	fprintf(out, "                      necessary.  If FILE is '-' (stdin), only one FLAC file\n");
	fprintf(out, "                      may be specified.\n");
	fprintf(out, "--export-tags-to=FILE Export tags to a file.  Use '-' for stdout.  Each line\n");
	fprintf(out, "                      will be of the form NAME=VALUE.  Specify\n");
	fprintf(out, "                      --no-utf8-convert if necessary.\n");
	fprintf(out, "--import-cuesheet-from=FILE  Import a cuesheet from a file.  Use '-' for stdin.\n");
	fprintf(out, "                      Only one FLAC file may be specified.  A seekpoint will be\n");
	fprintf(out, "                      added for each index point in the cuesheet to the\n");
	fprintf(out, "                      SEEKTABLE unless --no-cued-seekpoints is specified.\n");
	fprintf(out, "--export-cuesheet-to=FILE  Export CUESHEET block to a cuesheet file, suitable\n");
	fprintf(out, "                      for use by CD authoring software.  Use '-' for stdout.\n");
	fprintf(out, "                      Only one FLAC file may be specified on the command line.\n");
	fprintf(out, "--import-picture-from=FILENAME|SPECIFICATION  Import a picture and store it in a\n");
	fprintf(out, "                      PICTURE block.  Either a filename for the picture file or\n");
	fprintf(out, "                      a more complete specification form can be used.  The\n");
	fprintf(out, "                      SPECIFICATION is a string whose parts are separated by |\n");
	fprintf(out, "                      characters.  Some parts may be left empty to invoke\n");
	fprintf(out, "                      default values.  FILENAME is just shorthand for\n");
	fprintf(out, "                      \"||||FILENAME\".  The format of SPECIFICATION is:\n");
	fprintf(out, "         [TYPE]|[MIME-TYPE]|[DESCRIPTION]|[WIDTHxHEIGHTxDEPTH[/COLORS]]|FILE\n");
	fprintf(out, "           TYPE is optional; it is a number from one of:\n");
	fprintf(out, "              0: Other\n");
	fprintf(out, "              1: 32x32 pixels 'file icon' (PNG only)\n");
	fprintf(out, "              2: Other file icon\n");
	fprintf(out, "              3: Cover (front)\n");
	fprintf(out, "              4: Cover (back)\n");
	fprintf(out, "              5: Leaflet page\n");
	fprintf(out, "              6: Media (e.g. label side of CD)\n");
	fprintf(out, "              7: Lead artist/lead performer/soloist\n");
	fprintf(out, "              8: Artist/performer\n");
	fprintf(out, "              9: Conductor\n");
	fprintf(out, "             10: Band/Orchestra\n");
	fprintf(out, "             11: Composer\n");
	fprintf(out, "             12: Lyricist/text writer\n");
	fprintf(out, "             13: Recording Location\n");
	fprintf(out, "             14: During recording\n");
	fprintf(out, "             15: During performance\n");
	fprintf(out, "             16: Movie/video screen capture\n");
	fprintf(out, "             17: A bright coloured fish\n");
	fprintf(out, "             18: Illustration\n");
	fprintf(out, "             19: Band/artist logotype\n");
	fprintf(out, "             20: Publisher/Studio logotype\n");
	fprintf(out, "             The default is 3 (front cover).  There may only be one picture each\n");
	fprintf(out, "             of type 1 and 2 in a file.\n");
	fprintf(out, "           MIME-TYPE is optional; if left blank, it will be detected from the\n");
	fprintf(out, "             file.  For best compatibility with players, use pictures with MIME\n");
	fprintf(out, "             type image/jpeg or image/png.  The MIME type can also be --> to\n");
	fprintf(out, "             mean that FILE is actually a URL to an image, though this use is\n");
	fprintf(out, "             discouraged.\n");
	fprintf(out, "           DESCRIPTION is optional; the default is an empty string\n");
	fprintf(out, "           The next part specfies the resolution and color information.  If\n");
	fprintf(out, "             the MIME-TYPE is image/jpeg, image/png, or image/gif, you can\n");
	fprintf(out, "             usually leave this empty and they can be detected from the file.\n");
	fprintf(out, "             Otherwise, you must specify the width in pixels, height in pixels,\n");
	fprintf(out, "             and color depth in bits-per-pixel.  If the image has indexed colors\n");
	fprintf(out, "             you should also specify the number of colors used.\n");
	fprintf(out, "           FILE is the path to the picture file to be imported, or the URL if\n");
	fprintf(out, "             MIME type is -->\n");
	fprintf(out, "--export-picture-to=FILE  Export PICTURE block to a file.  Use '-' for stdout.\n");
	fprintf(out, "                      Only one FLAC file may be specified.  The first PICTURE\n");
	fprintf(out, "                      block will be exported unless --export-picture-to is\n");
	fprintf(out, "                      preceded by a --block-number=# option to specify the exact\n");
	fprintf(out, "                      metadata block to extract.  Note that the block number is\n");
	fprintf(out, "                      the one shown by --list.\n");
	fprintf(out, "--add-replay-gain     Calculates the title and album gains/peaks of the given\n");
	fprintf(out, "                      FLAC files as if all the files were part of one album,\n");
	fprintf(out, "                      then stores them in the VORBIS_COMMENT block.  The tags\n");
	fprintf(out, "                      are the same as those used by vorbisgain.  Existing\n");
	fprintf(out, "                      ReplayGain tags will be replaced.  If only one FLAC file\n");
	fprintf(out, "                      is given, the album and title gains will be the same.\n");
	fprintf(out, "                      Since this operation requires two passes, it is always\n");
	fprintf(out, "                      executed last, after all other operations have been\n");
	fprintf(out, "                      completed and written to disk.  All FLAC files specified\n");
	fprintf(out, "                      must have the same resolution, sample rate, and number\n");
	fprintf(out, "                      of channels.  The sample rate must be one of 8, 11.025,\n");
	fprintf(out, "                      12, 16, 22.05, 24, 32, 44.1, or 48 kHz.\n");
	fprintf(out, "--scan-replay-gain    Like --add-replay-gain, but only analyzes the files\n");
	fprintf(out, "                      rather than writing them to tags.\n");
	fprintf(out, "--remove-replay-gain  Removes the ReplayGain tags.\n");
	fprintf(out, "--add-seekpoint={#|X|#x|#s}  Add seek points to a SEEKTABLE block\n");
	fprintf(out, "       #  : a specific sample number for a seek point\n");
	fprintf(out, "       X  : a placeholder point (always goes at the end of the SEEKTABLE)\n");
	fprintf(out, "       #x : # evenly spaced seekpoints, the first being at sample 0\n");
	fprintf(out, "       #s : a seekpoint every # seconds; # does not have to be a whole number\n");
	fprintf(out, "                      If no SEEKTABLE block exists, one will be created.  If\n");
	fprintf(out, "                      one already exists, points will be added to the existing\n");
	fprintf(out, "                      table, and any duplicates will be turned into placeholder\n");
	fprintf(out, "                      points.  You may use many --add-seekpoint options; the\n");
	fprintf(out, "                      resulting SEEKTABLE will be the unique-ified union of\n");
	fprintf(out, "                      all such values.  Example: --add-seekpoint=100x\n");
	fprintf(out, "                      --add-seekpoint=3.5s will add 100 evenly spaced\n");
	fprintf(out, "                      seekpoints and a seekpoint every 3.5 seconds.\n");
	fprintf(out, "--add-padding=length  Add a padding block of the given length (in bytes).\n");
	fprintf(out, "                      The overall length of the new block will be 4 + length;\n");
	fprintf(out, "                      the extra 4 bytes is for the metadata block header.\n");
	fprintf(out, "\n");
	fprintf(out, "Major operations:\n");
	fprintf(out, "--version\n");
	fprintf(out, "    Show the metaflac version number.\n");
	fprintf(out, "--list\n");
	fprintf(out, "    List the contents of one or more metadata blocks to stdout.  By default,\n");
	fprintf(out, "    all metadata blocks are listed in text format.  Use the following options\n");
	fprintf(out, "    to change this behavior:\n");
	fprintf(out, "\n");
	fprintf(out, "    --block-number=#[,#[...]]\n");
	fprintf(out, "    An optional comma-separated list of block numbers to display.  The first\n");
	fprintf(out, "    block, the STREAMINFO block, is block 0.\n");
	fprintf(out, "\n");
	fprintf(out, "    --block-type=type[,type[...]]\n");
	fprintf(out, "    --except-block-type=type[,type[...]]\n");
	fprintf(out, "    An optional comma-separated list of block types to be included or ignored\n");
	fprintf(out, "    with this option.  Use only one of --block-type or --except-block-type.\n");
	fprintf(out, "    The valid block types are: STREAMINFO, PADDING, APPLICATION, SEEKTABLE,\n");
	fprintf(out, "    VORBIS_COMMENT.  You may narrow down the types of APPLICATION blocks\n");
	fprintf(out, "    displayed as follows:\n");
	fprintf(out, "        APPLICATION:abcd        The APPLICATION block(s) whose textual repre-\n");
	fprintf(out, "                                sentation of the 4-byte ID is \"abcd\"\n");
	fprintf(out, "        APPLICATION:0xXXXXXXXX  The APPLICATION block(s) whose hexadecimal big-\n");
	fprintf(out, "                                endian representation of the 4-byte ID is\n");
	fprintf(out, "                                \"0xXXXXXXXX\".  For the example \"abcd\" above the\n");
	fprintf(out, "                                hexadecimal equivalalent is 0x61626364\n");
	fprintf(out, "\n");
	fprintf(out, "    NOTE: if both --block-number and --[except-]block-type are specified,\n");
	fprintf(out, "          the result is the logical AND of both arguments.\n");
	fprintf(out, "\n");
#if 0
	/*@@@ not implemented yet */
	fprintf(out, "    --data-format=binary|text\n");
	fprintf(out, "    By default a human-readable text representation of the data is displayed.\n");
	fprintf(out, "    You may specify --data-format=binary to dump the raw binary form of each\n");
	fprintf(out, "    metadata block.  The output can be read in using a subsequent call to\n");
	fprintf(out, "    "metaflac --append --from-file=..."\n");
	fprintf(out, "\n");
#endif
	fprintf(out, "    --application-data-format=hexdump|text\n");
	fprintf(out, "    If the application block you are displaying contains binary data but your\n");
	fprintf(out, "    --data-format=text, you can display a hex dump of the application data\n");
	fprintf(out, "    contents instead using --application-data-format=hexdump\n");
	fprintf(out, "\n");
#if 0
	/*@@@ not implemented yet */
	fprintf(out, "--append\n");
	fprintf(out, "    Insert a metadata block from a file.  The input file must be in the same\n");
	fprintf(out, "    format as generated with --list.\n");
	fprintf(out, "\n");
	fprintf(out, "    --block-number=#\n");
	fprintf(out, "    Specify the insertion point (defaults to last block).  The new block will\n");
	fprintf(out, "    be added after the given block number.  This prevents the illegal insertion\n");
	fprintf(out, "    of a block before the first STREAMINFO block.  You may not --append another\n");
	fprintf(out, "    STREAMINFO block.\n");
	fprintf(out, "\n");
	fprintf(out, "    --from-file=filename\n");
	fprintf(out, "    Mandatory 'option' to specify the input file containing the block contents.\n");
	fprintf(out, "\n");
	fprintf(out, "    --data-format=binary|text\n");
	fprintf(out, "    By default the block contents are assumed to be in binary format.  You can\n");
	fprintf(out, "    override this by specifying --data-format=text\n");
	fprintf(out, "\n");
#endif
	fprintf(out, "--remove\n");
	fprintf(out, "    Remove one or more metadata blocks from the metadata.  Unless\n");
	fprintf(out, "    --dont-use-padding is specified, the blocks will be replaced with padding.\n");
	fprintf(out, "    You may not remove the STREAMINFO block.\n");
	fprintf(out, "\n");
	fprintf(out, "    --block-number=#[,#[...]]\n");
	fprintf(out, "    --block-type=type[,type[...]]\n");
	fprintf(out, "    --except-block-type=type[,type[...]]\n");
	fprintf(out, "    See --list above for usage.\n");
	fprintf(out, "\n");
	fprintf(out, "    NOTE: if both --block-number and --[except-]block-type are specified,\n");
	fprintf(out, "          the result is the logical AND of both arguments.\n");
	fprintf(out, "\n");
	fprintf(out, "--remove-all\n");
	fprintf(out, "    Remove all metadata blocks (except the STREAMINFO block) from the\n");
	fprintf(out, "    metadata.  Unless --dont-use-padding is specified, the blocks will be\n");
	fprintf(out, "    replaced with padding.\n");
	fprintf(out, "\n");
	fprintf(out, "--merge-padding\n");
	fprintf(out, "    Merge adjacent PADDING blocks into single blocks.\n");
	fprintf(out, "\n");
	fprintf(out, "--sort-padding\n");
	fprintf(out, "    Move all PADDING blocks to the end of the metadata and merge them into a\n");
	fprintf(out, "    single block.\n");

	return message? 1 : 0;
}
Beispiel #10
0
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<std::string>(), 
            "order and types of metadata to read\ne=exif, i=iptc, f=file (default: eif)")
        ("unsorted,u", po::value<std::string>(), 
            "special directory to store unsorted files (default: unsorted)")
        ("dups,d", po::value<std::string>(), 
            "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<std::string> >(), 
            "exclude directories and files that contain arg (case sensitive on all platforms)")
        ("limit-depth,l", po::value<long>(), 
            "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 >(), "desination 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<std::string>();
    
            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<std::string>() );
        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<std::string>() );
        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<std::string>();
        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<std::string> excludes;
        if(vm.count("exclude"))
            excludes = vm["exclude"].as< std::vector<std::string> >();
        long limit_depth = LONG_MAX;
        if(vm.count("limit-depth")) {
            limit_depth = vm["limit-depth"].as<long>();
            // 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<std::string>();
        const fs::path dups_dir = dest_dir / dups;
    
        std::string unsorted = "unsorted";
        if(vm.count("unsorted"))
            unsorted = vm["unsorted"].as<std::string>();
        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;
    }
}