Project

General

Profile

Bug #1164 » exiv2.cpp

Added a signal handler - Nicolas THOMASSON, 02 Mar 2016 13:35

 
1
// ***************************************************************** -*- C++ -*-
2
/*
3
 * Copyright (C) 2004-2015 Andreas Huggel <ahuggel@gmx.net>
4
 *
5
 * This program is part of the Exiv2 distribution.
6
 *
7
 * This program is free software; you can redistribute it and/or
8
 * modify it under the terms of the GNU General Public License
9
 * as published by the Free Software Foundation; either version 2
10
 * of the License, or (at your option) any later version.
11
 *
12
 * This program is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 * GNU General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU General Public License
18
 * along with this program; if not, write to the Free Software
19
 * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA.
20
 */
21
/*
22
  Abstract:  Command line program to display and manipulate image metadata.
23

    
24
  File:      exiv2.cpp
25
  Version:   $Rev: 3777 $
26
  Author(s): Andreas Huggel (ahu) <ahuggel@gmx.net>
27
  History:   10-Dec-03, ahu: created
28
 */
29
// *****************************************************************************
30
#include "rcsid_int.hpp"
31
EXIV2_RCSID("@(#) $Id: exiv2.cpp 3777 2015-05-02 11:55:40Z ahuggel $")
32

    
33
// included header files
34
#include "config.h"
35

    
36
#include "exiv2app.hpp"
37
#include "actions.hpp"
38
#include "utils.hpp"
39
#include "convert.hpp"
40
#include "i18n.h"      // NLS support.
41
#include "xmp.hpp"
42

    
43
#include <string>
44
#include <iostream>
45
#include <fstream>
46
#include <iomanip>
47
#include <cstring>
48
#include <cassert>
49
#include <cctype>
50

    
51
#if EXV_HAVE_REGEX
52
#include <regex.h>
53
#endif
54

    
55

    
56
#include <signal.h>
57

    
58
static void handler(int sig, siginfo_t *dont_care, void *dont_care_either)
59
{
60
	throw(Exiv2::BasicError<char>(-1));
61
}
62

    
63
// *****************************************************************************
64
// local declarations
65
namespace {
66

    
67
    //! List of all command identifiers and corresponding strings
68
    static const CmdIdAndString cmdIdAndString[] = {
69
        { add, "add" },
70
        { set, "set" },
71
        { del, "del" },
72
        { reg, "reg" },
73
        { invalidCmdId, "invalidCmd" }          // End of list marker
74
    };
75

    
76
    // Return a command Id for a command string
77
    CmdId commandId(const std::string& cmdString);
78

    
79
    // Evaluate [-]HH[:MM[:SS]], returns true and sets time to the value
80
    // in seconds if successful, else returns false.
81
    bool parseTime(const std::string& ts, long& time);
82

    
83
    /*!
84
      @brief Parse the oparg string into a bitmap of common targets.
85
      @param optarg Option arguments
86
      @param action Action being processed
87
      @return A bitmap of common targets or -1 in case of a parse error
88
     */
89
    int parseCommonTargets(const std::string& optarg,
90
                           const std::string& action);
91

    
92
    /*!
93
      @brief Parse numbers separated by commas into container
94
      @param previewNumbers Container for the numbers
95
      @param optarg Option arguments
96
      @param j Starting index into optarg
97
      @return Number of characters processed
98
     */
99
    int parsePreviewNumbers(Params::PreviewNumbers& previewNumbers,
100
                            const std::string& optarg,
101
                            int j);
102

    
103
    /*!
104
      @brief Parse metadata modification commands from multiple files
105
      @param modifyCmds Reference to a structure to store the parsed commands
106
      @param cmdFiles Container with the file names
107
     */
108
    bool parseCmdFiles(ModifyCmds& modifyCmds,
109
                       const Params::CmdFiles& cmdFiles);
110

    
111
    /*!
112
      @brief Parse metadata modification commands from a container of commands
113
      @param modifyCmds Reference to a structure to store the parsed commands
114
      @param cmdLines Container with the commands
115
     */
116
    bool parseCmdLines(ModifyCmds& modifyCmds,
117
                       const Params::CmdLines& cmdLines);
118

    
119
    /*!
120
      @brief Parse one line of the command file
121
      @param modifyCmd Reference to a command structure to store the parsed
122
             command
123
      @param line Input line
124
      @param num Line number (used for error output)
125
     */
126
    bool parseLine(ModifyCmd& modifyCmd,
127
                   const std::string& line, int num);
128

    
129
    /*!
130
      @brief Parses a string containing backslash-escapes
131
      @param input Input string, assumed to be UTF-8
132
     */
133
    std::string parseEscapes(const std::string& input);
134
}
135

    
136
// *****************************************************************************
137
// Main
138
int main(int argc, char* const argv[])
139
{
140
#ifdef EXV_ENABLE_NLS
141
    setlocale(LC_ALL, "");
142
    bindtextdomain(EXV_PACKAGE, EXV_LOCALEDIR);
143
    textdomain(EXV_PACKAGE);
144
#endif
145
  struct sigaction sa;
146

    
147
    memset(&sa, 0, sizeof(sa));
148
      sigemptyset(&sa.sa_mask);
149

    
150
        sa.sa_flags     = SA_NODEFER;
151
	  sa.sa_sigaction = handler;
152

    
153
	  sigaction(SIGSEGV, &sa, NULL); /*  ignore whether it works or not */  
154

    
155
    // Handle command line arguments
156
    Params& params = Params::instance();
157
    if (params.getopt(argc, argv)) {
158
        params.usage();
159
        return 1;
160
    }
161
    if (params.help_) {
162
        params.help();
163
        return 0;
164
    }
165
    if (params.version_) {
166
        params.version(params.verbose_);
167
        return 0;
168
    }
169

    
170
    // Create the required action class
171
    Action::TaskFactory& taskFactory = Action::TaskFactory::instance();
172
    Action::Task::AutoPtr task
173
        = taskFactory.create(Action::TaskType(params.action_));
174
    assert(task.get());
175

    
176
    // Process all files
177
    int rc = 0;
178
    int n = 1;
179
    int s = static_cast<int>(params.files_.size());
180
    int w = s > 9 ? s > 99 ? 3 : 2 : 1;
181
    for (Params::Files::const_iterator i = params.files_.begin();
182
         i != params.files_.end(); ++i) {
183
        if (params.verbose_) {
184
            std::cout << _("File") << " " << std::setw(w) << std::right << n++ << "/" << s << ": "
185
                      << *i << std::endl;
186
        }
187
        int ret = task->run(*i);
188
        if (rc == 0) rc = ret;
189
    }
190

    
191
    taskFactory.cleanup();
192
    params.cleanup();
193
    Exiv2::XmpParser::terminate();
194

    
195
    // Return a positive one byte code for better consistency across platforms
196
    return static_cast<unsigned int>(rc) % 256;
197
} // main
198

    
199
// *****************************************************************************
200
// class Params
201
Params* Params::instance_ = 0;
202

    
203
const Params::YodAdjust Params::emptyYodAdjust_[] = {
204
    { false, "-Y", 0 },
205
    { false, "-O", 0 },
206
    { false, "-D", 0 },
207
};
208

    
209
Params& Params::instance()
210
{
211
    if (0 == instance_) {
212
        instance_ = new Params;
213
    }
214
    return *instance_;
215
}
216

    
217
void Params::cleanup()
218
{
219
    delete instance_;
220
    instance_ = 0;
221
}
222

    
223
void Params::version(bool verbose,std::ostream& os) const
224
{
225
    bool  b64    = sizeof(void*)==8;
226
    const char* sBuild = b64 ? "(64 bit build)" : "(32 bit build)" ;
227
    os << EXV_PACKAGE_STRING << " " << Exiv2::versionNumberHexString() << " " << sBuild << "\n";
228
    if ( Params::instance().greps_.empty() ) {
229
    os << _("Copyright (C) 2004-2015 Andreas Huggel.\n")
230
       << "\n"
231
       << _("This program is free software; you can redistribute it and/or\n"
232
            "modify it under the terms of the GNU General Public License\n"
233
            "as published by the Free Software Foundation; either version 2\n"
234
            "of the License, or (at your option) any later version.\n")
235
       << "\n"
236
       << _("This program is distributed in the hope that it will be useful,\n"
237
            "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
238
            "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
239
            "GNU General Public License for more details.\n")
240
       << "\n"
241
       << _("You should have received a copy of the GNU General Public\n"
242
            "License along with this program; if not, write to the Free\n"
243
            "Software Foundation, Inc., 51 Franklin Street, Fifth Floor,\n"
244
            "Boston, MA 02110-1301 USA\n");
245
    }
246

    
247
    if ( verbose ) Exiv2::dumpLibraryInfo(os,Params::instance().greps_);
248
}
249

    
250
void Params::usage(std::ostream& os) const
251
{
252
    os << _("Usage:") << " " << progname()
253
       << " " << _("[ options ] [ action ] file ...\n\n")
254
       << _("Manipulate the Exif metadata of images.\n");
255
}
256

    
257
void Params::help(std::ostream& os) const
258
{
259
    usage(os);
260
    os << _("\nActions:\n")
261
       << _("  ad | adjust   Adjust Exif timestamps by the given time. This action\n"
262
            "                requires at least one of the -a, -Y, -O or -D options.\n")
263
       << _("  pr | print    Print image metadata.\n")
264
       << _("  rm | delete   Delete image metadata from the files.\n")
265
       << _("  in | insert   Insert metadata from corresponding *.exv files.\n"
266
            "                Use option -S to change the suffix of the input files.\n")
267
       << _("  ex | extract  Extract metadata to *.exv, *.xmp and thumbnail image files.\n")
268
       << _("  mv | rename   Rename files and/or set file timestamps according to the\n"
269
            "                Exif create timestamp. The filename format can be set with\n"
270
            "                -r format, timestamp options are controlled with -t and -T.\n")
271
       << _("  mo | modify   Apply commands to modify (add, set, delete) the Exif and\n"
272
            "                IPTC metadata of image files or set the JPEG comment.\n"
273
            "                Requires option -c, -m or -M.\n")
274
       << _("  fi | fixiso   Copy ISO setting from the Nikon Makernote to the regular\n"
275
            "                Exif tag.\n")
276
       << _("  fc | fixcom   Convert the UNICODE Exif user comment to UCS-2. Its current\n"
277
            "                character encoding can be specified with the -n option.\n")
278
       << _("\nOptions:\n")
279
       << _("   -h      Display this help and exit.\n")
280
       << _("   -V      Show the program version and exit.\n")
281
       << _("   -v      Be verbose during the program run.\n")
282
       << _("   -q      Silence warnings and error messages during the program run (quiet).\n")
283
       << _("   -Q lvl  Set log-level to d(ebug), i(nfo), w(arning), e(rror) or m(ute).\n")
284
       << _("   -b      Show large binary values.\n")
285
       << _("   -u      Show unknown tags.\n")
286
       << _("   -g key  Only output info for this key (grep).\n")
287
       << _("   -K key  Only output info for this key (exact match).\n")
288
       << _("   -n enc  Charset to use to decode UNICODE Exif user comments.\n")
289
       << _("   -k      Preserve file timestamps (keep).\n")
290
       << _("   -t      Also set the file timestamp in 'rename' action (overrides -k).\n")
291
       << _("   -T      Only set the file timestamp in 'rename' action, do not rename\n"
292
            "           the file (overrides -k).\n")
293
       << _("   -f      Do not prompt before overwriting existing files (force).\n")
294
       << _("   -F      Do not prompt before renaming files (Force).\n")
295
       << _("   -a time Time adjustment in the format [-]HH[:MM[:SS]]. This option\n"
296
            "           is only used with the 'adjust' action.\n")
297
       << _("   -Y yrs  Year adjustment with the 'adjust' action.\n")
298
       << _("   -O mon  Month adjustment with the 'adjust' action.\n")
299
       << _("   -D day  Day adjustment with the 'adjust' action.\n")
300
       << _("   -p mode Print mode for the 'print' action. Possible modes are:\n")
301
       << _("             s : print a summary of the Exif metadata (the default)\n")
302
       << _("             a : print Exif, IPTC and XMP metadata (shortcut for -Pkyct)\n")
303
       << _("             t : interpreted (translated) Exif data (-PEkyct)\n")
304
       << _("             v : plain Exif data values (-PExgnycv)\n")
305
       << _("             h : hexdump of the Exif data (-PExgnycsh)\n")
306
       << _("             i : IPTC data values (-PIkyct)\n")
307
       << _("             x : XMP properties (-PXkyct)\n")
308
       << _("             c : JPEG comment\n")
309
       << _("             p : list available previews\n")
310
       << _("             S : print structure of image\n")
311
       << _("             X : extract XMP from image\n")
312
       << _("   -P flgs Print flags for fine control of tag lists ('print' action):\n")
313
       << _("             E : include Exif tags in the list\n")
314
       << _("             I : IPTC datasets\n")
315
       << _("             X : XMP properties\n")
316
       << _("             x : print a column with the tag number\n")
317
       << _("             g : group name\n")
318
       << _("             k : key\n")
319
       << _("             l : tag label\n")
320
       << _("             n : tag name\n")
321
       << _("             y : type\n")
322
       << _("             c : number of components (count)\n")
323
       << _("             s : size in bytes\n")
324
       << _("             v : plain data value\n")
325
       << _("             t : interpreted (translated) data\n")
326
       << _("             h : hexdump of the data\n")
327
       << _("   -d tgt  Delete target(s) for the 'delete' action. Possible targets are:\n")
328
       << _("             a : all supported metadata (the default)\n")
329
       << _("             e : Exif section\n")
330
       << _("             t : Exif thumbnail only\n")
331
       << _("             i : IPTC data\n")
332
       << _("             x : XMP packet\n")
333
       << _("             c : JPEG comment\n")
334
       << _("   -i tgt  Insert target(s) for the 'insert' action. Possible targets are\n"
335
            "           the same as those for the -d option, plus a modifier:\n"
336
            "             X : Insert metadata from an XMP sidecar file <file>.xmp\n"
337
            "           Only JPEG thumbnails can be inserted, they need to be named\n"
338
            "           <file>-thumb.jpg\n")
339
       << _("   -e tgt  Extract target(s) for the 'extract' action. Possible targets\n"
340
            "           are the same as those for the -d option, plus a target to extract\n"
341
            "           preview images and a modifier to generate an XMP sidecar file:\n"
342
            "             p[<n>[,<m> ...]] : Extract preview images.\n"
343
            "             X : Extract metadata to an XMP sidecar file <file>.xmp\n")
344
       << _("   -r fmt  Filename format for the 'rename' action. The format string\n"
345
            "           follows strftime(3). The following keywords are supported:\n")
346
       << _("             :basename:   - original filename without extension\n")
347
       << _("             :dirname:    - name of the directory holding the original file\n")
348
       << _("             :parentname: - name of parent directory\n")
349
       << _("           Default filename format is ")
350
       <<               format_ << ".\n"
351
       << _("   -c txt  JPEG comment string to set in the image.\n")
352
       << _("   -m file Command file for the modify action. The format for commands is\n"
353
            "           set|add|del <key> [[<type>] <value>].\n")
354
       << _("   -M cmd  Command line for the modify action. The format for the\n"
355
            "           commands is the same as that of the lines of a command file.\n")
356
       << _("   -l dir  Location (directory) for files to be inserted from or extracted to.\n")
357
       << _("   -S .suf Use suffix .suf for source files for insert command.\n\n");
358
} // Params::help
359

    
360
int Params::option(int opt, const std::string& optarg, int optopt)
361
{
362
    int rc = 0;
363
    switch (opt) {
364
    case 'h': help_ = true; break;
365
    case 'V': version_ = true; break;
366
    case 'v': verbose_ = true; break;
367
    case 'q': Exiv2::LogMsg::setLevel(Exiv2::LogMsg::mute); break;
368
    case 'Q': rc = setLogLevel(optarg); break;
369
    case 'k': preserve_ = true; break;
370
    case 'b': binary_ = false; break;
371
    case 'u': unknown_ = false; break;
372
    case 'f': force_ = true; fileExistsPolicy_ = overwritePolicy; break;
373
    case 'F': force_ = true; fileExistsPolicy_ = renamePolicy; break;
374
    case 'g': rc = evalGrep(optarg); printMode_ = pmList; break;
375
    case 'K': rc = evalKey(optarg); printMode_ = pmList; break;
376
    case 'n': charset_ = optarg; break;
377
    case 'r': rc = evalRename(opt, optarg); break;
378
    case 't': rc = evalRename(opt, optarg); break;
379
    case 'T': rc = evalRename(opt, optarg); break;
380
    case 'a': rc = evalAdjust(optarg); break;
381
    case 'Y': rc = evalYodAdjust(yodYear, optarg); break;
382
    case 'O': rc = evalYodAdjust(yodMonth, optarg); break;
383
    case 'D': rc = evalYodAdjust(yodDay, optarg); break;
384
    case 'p': rc = evalPrint(optarg); break;
385
    case 'P': rc = evalPrintFlags(optarg); break;
386
    case 'd': rc = evalDelete(optarg); break;
387
    case 'e': rc = evalExtract(optarg); break;
388
    case 'i': rc = evalInsert(optarg); break;
389
    case 'c': rc = evalModify(opt, optarg); break;
390
    case 'm': rc = evalModify(opt, optarg); break;
391
    case 'M': rc = evalModify(opt, optarg); break;
392
    case 'l': directory_ = optarg; break;
393
    case 'S': suffix_ = optarg; break;
394
    case ':':
395
        std::cerr << progname() << ": " << _("Option") << " -" << static_cast<char>(optopt)
396
                   << " " << _("requires an argument\n");
397
        rc = 1;
398
        break;
399
    case '?':
400
        std::cerr << progname() << ": " << _("Unrecognized option") << " -"
401
                  << static_cast<char>(optopt) << "\n";
402
        rc = 1;
403
        break;
404
    default:
405
        std::cerr << progname()
406
                  << ": " << _("getopt returned unexpected character code") << " "
407
                  << std::hex << opt << "\n";
408
        rc = 1;
409
        break;
410
    }
411
    return rc;
412
} // Params::option
413

    
414
int Params::setLogLevel(const std::string& optarg)
415
{
416
    int rc = 0;
417
    const char logLevel = tolower(optarg[0]);
418
    switch (logLevel) {
419
    case 'd': Exiv2::LogMsg::setLevel(Exiv2::LogMsg::debug); break;
420
    case 'i': Exiv2::LogMsg::setLevel(Exiv2::LogMsg::info); break;
421
    case 'w': Exiv2::LogMsg::setLevel(Exiv2::LogMsg::warn); break;
422
    case 'e': Exiv2::LogMsg::setLevel(Exiv2::LogMsg::error); break;
423
    case 'm': Exiv2::LogMsg::setLevel(Exiv2::LogMsg::mute); break;
424
    default:
425
        std::cerr << progname() << ": " << _("Option") << " -Q: "
426
                  << _("Invalid argument") << " \"" << optarg << "\"\n";
427
        rc = 1;
428
        break;
429
    }
430
    return rc;
431
} // Params::setLogLevel
432

    
433
int Params::evalGrep( const std::string& optarg)
434
{
435
    int result=0;
436
#if EXV_HAVE_REGEX
437
    // try to compile a reg-exp from the input argument and store it in the vector
438
    const size_t i = greps_.size();
439
    greps_.resize(i + 1);
440
    regex_t *pRegex = &greps_[i];
441
    int errcode = regcomp( pRegex, optarg.c_str(), REG_NOSUB);
442

    
443
    // there was an error compiling the regexp
444
    if( errcode ) {
445
        size_t length = regerror (errcode, pRegex, NULL, 0);
446
        char *buffer = new char[ length];
447
        regerror (errcode, pRegex, buffer, length);
448
        std::cerr << progname()
449
              << ": " << _("Option") << " -g: "
450
              << _("Invalid regexp") << " \"" << optarg << "\": " << buffer << "\n";
451

    
452
        // free the memory and drop the regexp
453
        delete[] buffer;
454
        regfree( pRegex);
455
        greps_.resize(i);
456
        result=1;
457
    }
458
#else
459
    greps_.push_back(optarg);
460
#endif
461
    return result;
462
} // Params::evalGrep
463

    
464
int Params::evalKey( const std::string& optarg)
465
{
466
    int result=0;
467
    keys_.push_back(optarg);
468
    return result;
469
} // Params::evalKey
470

    
471
int Params::evalRename(int opt, const std::string& optarg)
472
{
473
    int rc = 0;
474
    switch (action_) {
475
    case Action::none:
476
        action_ = Action::rename;
477
        switch (opt) {
478
        case 'r':
479
            format_ = optarg;
480
            formatSet_ = true;
481
            break;
482
        case 't': timestamp_ = true; break;
483
        case 'T': timestampOnly_ = true; break;
484
        }
485
        break;
486
    case Action::rename:
487
        if (opt == 'r' && (formatSet_ || timestampOnly_)) {
488
            std::cerr << progname()
489
                      << ": " << _("Ignoring surplus option") << " -r \"" << optarg << "\"\n";
490
        }
491
        else {
492
            format_ = optarg;
493
            formatSet_ = true;
494
        }
495
        break;
496
    default:
497
        std::cerr << progname()
498
                  << ": " << _("Option") << " -" << (char)opt
499
                  << " " << _("is not compatible with a previous option\n");
500
        rc = 1;
501
        break;
502
    }
503
    return rc;
504
} // Params::evalRename
505

    
506
int Params::evalAdjust(const std::string& optarg)
507
{
508
    int rc = 0;
509
    switch (action_) {
510
    case Action::none:
511
    case Action::adjust:
512
        if (adjust_) {
513
            std::cerr << progname()
514
                      << ": " << _("Ignoring surplus option -a")  << " " << optarg << "\n";
515
            break;
516
        }
517
        action_ = Action::adjust;
518
        adjust_ = parseTime(optarg, adjustment_);
519
        if (!adjust_) {
520
            std::cerr << progname() << ": " << _("Error parsing -a option argument") << " `"
521
                      << optarg << "'\n";
522
            rc = 1;
523
        }
524
        break;
525
    default:
526
        std::cerr << progname()
527
                  << ": " << _("Option -a is not compatible with a previous option\n");
528
        rc = 1;
529
        break;
530
    }
531
    return rc;
532
} // Params::evalAdjust
533

    
534
int Params::evalYodAdjust(const Yod& yod, const std::string& optarg)
535
{
536
    int rc = 0;
537
    switch (action_) {
538
    case Action::none: // fall-through
539
    case Action::adjust:
540
        if (yodAdjust_[yod].flag_) {
541
            std::cerr << progname()
542
                      << ": " << _("Ignoring surplus option") << " "
543
                      << yodAdjust_[yod].option_ << " " << optarg << "\n";
544
            break;
545
        }
546
        action_ = Action::adjust;
547
        yodAdjust_[yod].flag_ = true;
548
        if (!Util::strtol(optarg.c_str(), yodAdjust_[yod].adjustment_)) {
549
            std::cerr << progname() << ": " << _("Error parsing") << " "
550
                      << yodAdjust_[yod].option_ << " "
551
                      << _("option argument") << " `" << optarg << "'\n";
552
            rc = 1;
553
        }
554
        break;
555
    default:
556
        std::cerr << progname()
557
                  << ": " << _("Option") << " "
558
                  << yodAdjust_[yod].option_ << " "
559
                  << _("is not compatible with a previous option\n");
560
        rc = 1;
561
        break;
562
    }
563
    return rc;
564
} // Params::evalYodAdjust
565

    
566
int Params::evalPrint(const std::string& optarg)
567
{
568
    int rc = 0;
569
    switch (action_) {
570
    case Action::none:
571
        switch (optarg[0]) {
572
        case 's': action_ = Action::print; printMode_ = pmSummary; break;
573
        case 'a': rc = evalPrintFlags("kyct"); break;
574
        case 't': rc = evalPrintFlags("Ekyct"); break;
575
        case 'v': rc = evalPrintFlags("Exgnycv"); break;
576
        case 'h': rc = evalPrintFlags("Exgnycsh"); break;
577
        case 'i': rc = evalPrintFlags("Ikyct"); break;
578
        case 'x': rc = evalPrintFlags("Xkyct"); break;
579
        case 'c': action_ = Action::print; printMode_ = pmComment  ; break;
580
        case 'p': action_ = Action::print; printMode_ = pmPreview  ; break;
581
        case 'S': action_ = Action::print; printMode_ = pmStructure; break;
582
        case 'X': action_ = Action::print; printMode_ = pmXMP      ; break;
583
        default:
584
            std::cerr << progname() << ": " << _("Unrecognized print mode") << " `"
585
                      << optarg << "'\n";
586
            rc = 1;
587
            break;
588
        }
589
        break;
590
    case Action::print:
591
        std::cerr << progname() << ": "
592
                  << _("Ignoring surplus option -p") << optarg << "\n";
593
        break;
594
    default:
595
        std::cerr << progname() << ": "
596
                  << _("Option -p is not compatible with a previous option\n");
597
        rc = 1;
598
        break;
599
    }
600
    return rc;
601
} // Params::evalPrint
602

    
603
int Params::evalPrintFlags(const std::string& optarg)
604
{
605
    int rc = 0;
606
    switch (action_) {
607
    case Action::none:
608
        action_ = Action::print;
609
        printMode_ = pmList;
610
        for (std::size_t i = 0; i < optarg.length(); ++i) {
611
            switch (optarg[i]) {
612
            case 'E': printTags_  |= Exiv2::mdExif; break;
613
            case 'I': printTags_  |= Exiv2::mdIptc; break;
614
            case 'X': printTags_  |= Exiv2::mdXmp;  break;
615
            case 'x': printItems_ |= prTag;   break;
616
            case 'g': printItems_ |= prGroup; break;
617
            case 'k': printItems_ |= prKey;   break;
618
            case 'l': printItems_ |= prLabel; break;
619
            case 'n': printItems_ |= prName;  break;
620
            case 'y': printItems_ |= prType;  break;
621
            case 'c': printItems_ |= prCount; break;
622
            case 's': printItems_ |= prSize;  break;
623
            case 'v': printItems_ |= prValue; break;
624
            case 't': printItems_ |= prTrans; break;
625
            case 'h': printItems_ |= prHex;   break;
626
            default:
627
                std::cerr << progname() << ": " << _("Unrecognized print item") << " `"
628
                          << optarg[i] << "'\n";
629
                rc = 1;
630
                break;
631
            }
632
        }
633
        break;
634
    case Action::print:
635
        std::cerr << progname() << ": "
636
                  << _("Ignoring surplus option -P") << optarg << "\n";
637
        break;
638
    default:
639
        std::cerr << progname() << ": "
640
                  << _("Option -P is not compatible with a previous option\n");
641
        rc = 1;
642
        break;
643
    }
644
    return rc;
645
} // Params::evalPrintFlags
646

    
647
int Params::evalDelete(const std::string& optarg)
648
{
649
    int rc = 0;
650
    switch (action_) {
651
    case Action::none:
652
        action_ = Action::erase;
653
        target_ = 0;
654
        // fallthrough
655
    case Action::erase:
656
        rc = parseCommonTargets(optarg, "erase");
657
        if (rc > 0) {
658
            target_ |= rc;
659
            rc = 0;
660
        }
661
        else {
662
            rc = 1;
663
        }
664
        break;
665
    default:
666
        std::cerr << progname() << ": "
667
                  << _("Option -d is not compatible with a previous option\n");
668
        rc = 1;
669
        break;
670
    }
671
    return rc;
672
} // Params::evalDelete
673

    
674
int Params::evalExtract(const std::string& optarg)
675
{
676
    int rc = 0;
677
    switch (action_) {
678
    case Action::none:
679
    case Action::modify:
680
        action_ = Action::extract;
681
        target_ = 0;
682
        // fallthrough
683
    case Action::extract:
684
        rc = parseCommonTargets(optarg, "extract");
685
        if (rc > 0) {
686
            target_ |= rc;
687
            rc = 0;
688
        }
689
        else {
690
            rc = 1;
691
        }
692
        break;
693
    default:
694
        std::cerr << progname() << ": "
695
                  << _("Option -e is not compatible with a previous option\n");
696
        rc = 1;
697
        break;
698
    }
699
    return rc;
700
} // Params::evalExtract
701

    
702
int Params::evalInsert(const std::string& optarg)
703
{
704
    int rc = 0;
705
    switch (action_) {
706
    case Action::none:
707
    case Action::modify:
708
        action_ = Action::insert;
709
        target_ = 0;
710
        // fallthrough
711
    case Action::insert:
712
        rc = parseCommonTargets(optarg, "insert");
713
        if (rc > 0) {
714
            target_ |= rc;
715
            rc = 0;
716
        }
717
        else {
718
            rc = 1;
719
        }
720
        break;
721
    default:
722
        std::cerr << progname() << ": "
723
                  << _("Option -i is not compatible with a previous option\n");
724
        rc = 1;
725
        break;
726
    }
727
    return rc;
728
} // Params::evalInsert
729

    
730
int Params::evalModify(int opt, const std::string& optarg)
731
{
732
    int rc = 0;
733
    switch (action_) {
734
    case Action::none:
735
        action_ = Action::modify;
736
        // fallthrough
737
    case Action::modify:
738
    case Action::extract:
739
    case Action::insert:
740
        if (opt == 'c') jpegComment_ = parseEscapes(optarg);
741
        if (opt == 'm') cmdFiles_.push_back(optarg);  // parse the files later
742
        if (opt == 'M') cmdLines_.push_back(optarg);  // parse the commands later
743
        break;
744
    default:
745
        std::cerr << progname() << ": "
746
                  << _("Option") << " -" << (char)opt << " "
747
                  << _("is not compatible with a previous option\n");
748
        rc = 1;
749
        break;
750
    }
751
    return rc;
752
} // Params::evalModify
753

    
754
int Params::nonoption(const std::string& argv)
755
{
756
    int rc = 0;
757
    bool action = false;
758
    if (first_) {
759
        // The first non-option argument must be the action
760
        first_ = false;
761
        if (argv == "ad" || argv == "adjust") {
762
            if (action_ != Action::none && action_ != Action::adjust) {
763
                std::cerr << progname() << ": "
764
                          << _("Action adjust is not compatible with the given options\n");
765
                rc = 1;
766
            }
767
            action = true;
768
            action_ = Action::adjust;
769
        }
770
        if (argv == "pr" || argv == "print") {
771
            if (action_ != Action::none && action_ != Action::print) {
772
                std::cerr << progname() << ": "
773
                          << _("Action print is not compatible with the given options\n");
774
                rc = 1;
775
            }
776
            action = true;
777
            action_ = Action::print;
778
        }
779
        if (argv == "rm" || argv == "delete") {
780
            if (action_ != Action::none && action_ != Action::erase) {
781
                std::cerr << progname() << ": "
782
                          << _("Action delete is not compatible with the given options\n");
783
                rc = 1;
784
            }
785
            action = true;
786
            action_ = Action::erase;
787
        }
788
        if (argv == "ex" || argv == "extract") {
789
            if (   action_ != Action::none
790
                && action_ != Action::extract
791
                && action_ != Action::modify) {
792
                std::cerr << progname() << ": "
793
                          << _("Action extract is not compatible with the given options\n");
794
                rc = 1;
795
            }
796
            action = true;
797
            action_ = Action::extract;
798
        }
799
        if (argv == "in" || argv == "insert") {
800
            if (   action_ != Action::none
801
                && action_ != Action::insert
802
                && action_ != Action::modify) {
803
                std::cerr << progname() << ": "
804
                          << _("Action insert is not compatible with the given options\n");
805
                rc = 1;
806
            }
807
            action = true;
808
            action_ = Action::insert;
809
        }
810
        if (argv == "mv" || argv == "rename") {
811
            if (action_ != Action::none && action_ != Action::rename) {
812
                std::cerr << progname() << ": "
813
                          << _("Action rename is not compatible with the given options\n");
814
                rc = 1;
815
            }
816
            action = true;
817
            action_ = Action::rename;
818
        }
819
        if (argv == "mo" || argv == "modify") {
820
            if (action_ != Action::none && action_ != Action::modify) {
821
                std::cerr << progname() << ": "
822
                          << _("Action modify is not compatible with the given options\n");
823
                rc = 1;
824
            }
825
            action = true;
826
            action_ = Action::modify;
827
        }
828
        if (argv == "fi" || argv == "fixiso") {
829
            if (action_ != Action::none && action_ != Action::fixiso) {
830
                std::cerr << progname() << ": "
831
                          << _("Action fixiso is not compatible with the given options\n");
832
                rc = 1;
833
            }
834
            action = true;
835
            action_ = Action::fixiso;
836
        }
837
        if (argv == "fc" || argv == "fixcom" || argv == "fixcomment") {
838
            if (action_ != Action::none && action_ != Action::fixcom) {
839
                std::cerr << progname() << ": "
840
                          << _("Action fixcom is not compatible with the given options\n");
841
                rc = 1;
842
            }
843
            action = true;
844
            action_ = Action::fixcom;
845
        }
846
        if (action_ == Action::none) {
847
            // if everything else fails, assume print as the default action
848
            action_ = Action::print;
849
        }
850
    }
851
    if (!action) {
852
        files_.push_back(argv);
853
    }
854
    return rc;
855
} // Params::nonoption
856

    
857
typedef std::map<std::string,std::string> long_t;
858

    
859
int Params::getopt(int argc, char* const Argv[])
860
{
861
	char** argv = new char* [argc+1];
862
	argv[argc] = NULL;
863
	long_t longs;
864

    
865
	longs["--adjust"   ] = "-a";
866
	longs["--binary"   ] = "-b";
867
	longs["--comment"  ] = "-c";
868
	longs["--delete"   ] = "-d";
869
	longs["--days"     ] = "-D";
870
	longs["--force"    ] = "-f";
871
	longs["--Force"    ] = "-F";
872
	longs["--grep"     ] = "-g";
873
	longs["--help"     ] = "-h";
874
	longs["--insert"   ] = "-i";
875
	longs["--keep"     ] = "-k";
876
	longs["--key"      ] = "-K";
877
	longs["--location" ] = "-l";
878
	longs["--modify"   ] = "-m";
879
	longs["--Modify"   ] = "-M";
880
	longs["--encode"   ] = "-n";
881
	longs["--months"   ] = "-O";
882
	longs["--print"    ] = "-p";
883
	longs["--Print"    ] = "-P";
884
	longs["--quiet"    ] = "-q";
885
	longs["--log"      ] = "-Q";
886
	longs["--rename"   ] = "-r";
887
	longs["--suffix"   ] = "-S";
888
	longs["--timestamp"] = "-t";
889
	longs["--Timestamp"] = "-T";
890
	longs["--unknown"  ] = "-u";
891
	longs["--verbose"  ] = "-v";
892
	longs["--Version"  ] = "-V";
893
	longs["--version"  ] = "-V";
894
	longs["--years"    ] = "-Y";
895

    
896
	for ( int i = 0 ; i < argc ; i++ ) {
897
		std::string* arg = new std::string(Argv[i]);
898
		if (longs.find(*arg) != longs.end() ) {
899
			argv[i] = ::strdup(longs[*arg].c_str());
900
		} else {
901
			argv[i] = ::strdup(Argv[i]);
902
		}
903
		delete arg;
904
	}
905

    
906
    int rc = Util::Getopt::getopt(argc, argv, optstring_);
907
    // Further consistency checks
908
    if (help_ || version_) return 0;
909
    if (action_ == Action::none) {
910
        // This shouldn't happen since print is taken as default action
911
        std::cerr << progname() << ": " << _("An action must be specified\n");
912
        rc = 1;
913
    }
914
    if (   action_ == Action::adjust
915
        && !adjust_
916
        && !yodAdjust_[yodYear].flag_
917
        && !yodAdjust_[yodMonth].flag_
918
        && !yodAdjust_[yodDay].flag_) {
919
        std::cerr << progname() << ": "
920
                  << _("Adjust action requires at least one -a, -Y, -O or -D option\n");
921
        rc = 1;
922
    }
923
    if (   action_ == Action::modify
924
        && cmdFiles_.empty() && cmdLines_.empty() && jpegComment_.empty()) {
925
        std::cerr << progname() << ": "
926
                  << _("Modify action requires at least one -c, -m or -M option\n");
927
        rc = 1;
928
    }
929
    if (0 == files_.size()) {
930
        std::cerr << progname() << ": " << _("At least one file is required\n");
931
        rc = 1;
932
    }
933
    if (rc == 0 && !cmdFiles_.empty()) {
934
        // Parse command files
935
        if (!parseCmdFiles(modifyCmds_, cmdFiles_)) {
936
            std::cerr << progname() << ": " << _("Error parsing -m option arguments\n");
937
            rc = 1;
938
        }
939
    }
940
    if (rc == 0 && !cmdLines_.empty()) {
941
        // Parse command lines
942
        if (!parseCmdLines(modifyCmds_, cmdLines_)) {
943
            std::cerr << progname() << ": " << _("Error parsing -M option arguments\n");
944
            rc = 1;
945
        }
946
    }
947
    if (rc == 0 && (!cmdFiles_.empty() || !cmdLines_.empty())) {
948
        // We'll set them again, after reading the file
949
        Exiv2::XmpProperties::unregisterNs();
950
    }
951
    if (   !directory_.empty()
952
        && !(action_ == Action::insert || action_ == Action::extract)) {
953
        std::cerr << progname() << ": "
954
                  << _("-l option can only be used with extract or insert actions\n");
955
        rc = 1;
956
    }
957
    if (!suffix_.empty() && !(action_ == Action::insert)) {
958
        std::cerr << progname() << ": "
959
                  << _("-S option can only be used with insert action\n");
960
        rc = 1;
961
    }
962
    if (timestamp_ && !(action_ == Action::rename)) {
963
        std::cerr << progname() << ": "
964
                  << _("-t option can only be used with rename action\n");
965
        rc = 1;
966
    }
967
    if (timestampOnly_ && !(action_ == Action::rename)) {
968
        std::cerr << progname() << ": "
969
                  << _("-T option can only be used with rename action\n");
970
        rc = 1;
971
    }
972

    
973
	// cleanup the argument vector
974
	for ( int i = 0 ; i < argc ; i++ ) ::free((void*)argv[i]);
975
    delete [] argv;
976

    
977
    return rc;
978
} // Params::getopt
979

    
980
// *****************************************************************************
981
// local implementations
982
namespace {
983

    
984
    bool parseTime(const std::string& ts, long& time)
985
    {
986
        std::string hstr, mstr, sstr;
987
        char *cts = new char[ts.length() + 1];
988
        strcpy(cts, ts.c_str());
989
        char *tmp = ::strtok(cts, ":");
990
        if (tmp) hstr = tmp;
991
        tmp = ::strtok(0, ":");
992
        if (tmp) mstr = tmp;
993
        tmp = ::strtok(0, ":");
994
        if (tmp) sstr = tmp;
995
        delete[] cts;
996

    
997
        int sign = 1;
998
        long hh(0), mm(0), ss(0);
999
        // [-]HH part
1000
        if (!Util::strtol(hstr.c_str(), hh)) return false;
1001
        if (hh < 0) {
1002
            sign = -1;
1003
            hh *= -1;
1004
        }
1005
        // check for the -0 special case
1006
        if (hh == 0 && hstr.find('-') != std::string::npos) sign = -1;
1007
        // MM part, if there is one
1008
        if (mstr != "") {
1009
            if (!Util::strtol(mstr.c_str(), mm)) return false;
1010
            if (mm > 59) return false;
1011
            if (mm < 0) return false;
1012
        }
1013
        // SS part, if there is one
1014
        if (sstr != "") {
1015
            if (!Util::strtol(sstr.c_str(), ss)) return false;
1016
            if (ss > 59) return false;
1017
            if (ss < 0) return false;
1018
        }
1019

    
1020
        time = sign * (hh * 3600 + mm * 60 + ss);
1021
        return true;
1022
    } // parseTime
1023

    
1024
    int parseCommonTargets(const std::string& optarg,
1025
                           const std::string& action)
1026
    {
1027
        int rc = 0;
1028
        int target = 0;
1029
        for (size_t i = 0; rc == 0 && i < optarg.size(); ++i) {
1030
            switch (optarg[i]) {
1031
            case 'e': target |= Params::ctExif; break;
1032
            case 'i': target |= Params::ctIptc; break;
1033
            case 'x': target |= Params::ctXmp; break;
1034
            case 'c': target |= Params::ctComment; break;
1035
            case 't': target |= Params::ctThumb; break;
1036
            case 'a': target |=   Params::ctExif
1037
                                | Params::ctIptc
1038
                                | Params::ctComment
1039
                                | Params::ctXmp; break;
1040
            case 'X':
1041
                target |= Params::ctXmpSidecar;
1042
                if (optarg == "X") target |= Params::ctExif | Params::ctIptc | Params::ctXmp;
1043
                break;
1044
            case 'p':
1045
            {
1046
                if (strcmp(action.c_str(), "extract") == 0) {
1047
                    i += (size_t) parsePreviewNumbers(Params::instance().previewNumbers_, optarg, (int) i + 1);
1048
                    target |= Params::ctPreview;
1049
                    break;
1050
                }
1051
                // fallthrough
1052
            }
1053
            default:
1054
                std::cerr << Params::instance().progname() << ": " << _("Unrecognized ")
1055
                          << action << " " << _("target") << " `"  << optarg[i] << "'\n";
1056
                rc = -1;
1057
                break;
1058
            }
1059
        }
1060
        return rc ? rc : target;
1061
    } // parseCommonTargets
1062

    
1063
    int parsePreviewNumbers(Params::PreviewNumbers& previewNumbers,
1064
                            const std::string& optarg,
1065
                            int j)
1066
    {
1067
        size_t k = j;
1068
        for (size_t i = j; i < optarg.size(); ++i) {
1069
            std::ostringstream os;
1070
            for (k = i; k < optarg.size() && isdigit(optarg[k]); ++k) {
1071
                os << optarg[k];
1072
            }
1073
            if (k > i) {
1074
                bool ok = false;
1075
                int num = Exiv2::stringTo<int>(os.str(), ok);
1076
                if (ok && num >= 0) {
1077
                    previewNumbers.insert(num);
1078
                }
1079
                else {
1080
                    std::cerr << Params::instance().progname() << ": "
1081
                              << _("Invalid preview number") << ": " << num << "\n";
1082
                }
1083
                i = k;
1084
            }
1085
            if (!(k < optarg.size() && optarg[i] == ',')) break;
1086
        }
1087
        int ret = static_cast<int>(k - j);
1088
        if (ret == 0) {
1089
            previewNumbers.insert(0);
1090
        }
1091
#ifdef DEBUG
1092
        std::cout << "\nThe set now contains: ";
1093
        for (Params::PreviewNumbers::const_iterator i = previewNumbers.begin();
1094
             i != previewNumbers.end();
1095
             ++i) {
1096
            std::cout << *i << ", ";
1097
        }
1098
        std::cout << std::endl;
1099
#endif
1100
        return (int) (k - j);
1101
    } // parsePreviewNumbers
1102

    
1103
    bool parseCmdFiles(ModifyCmds& modifyCmds,
1104
                       const Params::CmdFiles& cmdFiles)
1105
    {
1106
        Params::CmdFiles::const_iterator end = cmdFiles.end();
1107
        Params::CmdFiles::const_iterator filename = cmdFiles.begin();
1108
        for ( ; filename != end; ++filename) {
1109
            try {
1110
                std::ifstream file(filename->c_str());
1111
                if (!file) {
1112
                    std::cerr << *filename << ": "
1113
                              << _("Failed to open command file for reading\n");
1114
                    return false;
1115
                }
1116
                int num = 0;
1117
                std::string line;
1118
                while (std::getline(file, line)) {
1119
                    ModifyCmd modifyCmd;
1120
                    if (parseLine(modifyCmd, line, ++num)) {
1121
                        modifyCmds.push_back(modifyCmd);
1122
                    }
1123
                }
1124
            }
1125
            catch (const Exiv2::AnyError& error) {
1126
                std::cerr << *filename << ", " << _("line") << " " << error << "\n";
1127
                return false;
1128
            }
1129
        }
1130
        return true;
1131
    } // parseCmdFile
1132

    
1133
    bool parseCmdLines(ModifyCmds& modifyCmds,
1134
                       const Params::CmdLines& cmdLines)
1135
    {
1136
        try {
1137
            int num = 0;
1138
            Params::CmdLines::const_iterator end = cmdLines.end();
1139
            Params::CmdLines::const_iterator line = cmdLines.begin();
1140
            for ( ; line != end; ++line) {
1141
                ModifyCmd modifyCmd;
1142
                if (parseLine(modifyCmd, *line, ++num)) {
1143
                    modifyCmds.push_back(modifyCmd);
1144
                }
1145
            }
1146
            return true;
1147
        }
1148
        catch (const Exiv2::AnyError& error) {
1149
            std::cerr << _("-M option") << " " << error << "\n";
1150
            return false;
1151
        }
1152
    } // parseCmdLines
1153

    
1154
#if defined(_MSC_VER) || defined(__MINGW__)
1155
    static std::string formatArg(const char* arg)
1156
    {
1157
        std::string result = "";
1158
        char        b  = ' ' ;
1159
        char        e  = '\\'; std::string E = std::string("\\");
1160
        char        q  = '\''; std::string Q = std::string("'" );
1161
        bool        qt = false;
1162
        char* a    = (char*) arg;
1163
        while  ( *a ) {
1164
            if ( *a == b || *a == e || *a == q ) qt = true;
1165
            if ( *a == q ) result += E;
1166
            if ( *a == e ) result += E;
1167
            result += std::string(a,1);
1168
            a++ ;
1169
        }
1170
        if (qt) result = Q + result + Q;
1171

    
1172
        return result;
1173
    }
1174
#endif
1175

    
1176
    bool parseLine(ModifyCmd& modifyCmd, const std::string& line, int num)
1177
    {
1178
        const std::string delim = " \t";
1179

    
1180
        // Skip empty lines and comments
1181
        std::string::size_type cmdStart = line.find_first_not_of(delim);
1182
        if (cmdStart == std::string::npos || line[cmdStart] == '#') return false;
1183

    
1184
        // Get command and key
1185
        std::string::size_type cmdEnd = line.find_first_of(delim, cmdStart+1);
1186
        std::string::size_type keyStart = line.find_first_not_of(delim, cmdEnd+1);
1187
        std::string::size_type keyEnd = line.find_first_of(delim, keyStart+1);
1188
        if (   cmdStart == std::string::npos
1189
            || cmdEnd == std::string::npos
1190
            || keyStart == std::string::npos) {
1191
            std::string cmdLine ;
1192
#if defined(_MSC_VER) || defined(__MINGW__)
1193
            for ( int i = 1 ; i < __argc ; i++ ) { cmdLine += std::string(" ") + formatArg(__argv[i]) ; }
1194
#endif
1195
            throw Exiv2::Error(1, Exiv2::toString(num)
1196
                               + ": " + _("Invalid command line:") + cmdLine);
1197
        }
1198

    
1199
        std::string cmd(line.substr(cmdStart, cmdEnd-cmdStart));
1200
        CmdId cmdId = commandId(cmd);
1201
        if (cmdId == invalidCmdId) {
1202
            throw Exiv2::Error(1, Exiv2::toString(num)
1203
                               + ": " + _("Invalid command") + " `" + cmd + "'");
1204
        }
1205

    
1206
        Exiv2::TypeId defaultType = Exiv2::invalidTypeId;
1207
        std::string key(line.substr(keyStart, keyEnd-keyStart));
1208
        MetadataId metadataId = invalidMetadataId;
1209
        if (cmdId != reg) {
1210
            try {
1211
                Exiv2::IptcKey iptcKey(key);
1212
                metadataId = iptc;
1213
                defaultType = Exiv2::IptcDataSets::dataSetType(iptcKey.tag(),
1214
                                                               iptcKey.record());
1215
            }
1216
            catch (const Exiv2::AnyError&) {}
1217
            if (metadataId == invalidMetadataId) {
1218
                try {
1219
                    Exiv2::ExifKey exifKey(key);
1220
                    metadataId = exif;
1221
                    defaultType = exifKey.defaultTypeId();
1222
                }
1223
                catch (const Exiv2::AnyError&) {}
1224
            }
1225
            if (metadataId == invalidMetadataId) {
1226
                try {
1227
                    Exiv2::XmpKey xmpKey(key);
1228
                    metadataId = xmp;
1229
                    defaultType = Exiv2::XmpProperties::propertyType(xmpKey);
1230
                }
1231
                catch (const Exiv2::AnyError&) {}
1232
            }
1233
            if (metadataId == invalidMetadataId) {
1234
                throw Exiv2::Error(1, Exiv2::toString(num)
1235
                                   + ": " + _("Invalid key") + " `" + key + "'");
1236
            }
1237
        }
1238
        std::string value;
1239
        Exiv2::TypeId type = defaultType;
1240
        bool explicitType = false;
1241
        if (cmdId != del) {
1242
            // Get type and value
1243
            std::string::size_type typeStart = std::string::npos;
1244
            if (keyEnd != std::string::npos) typeStart = line.find_first_not_of(delim, keyEnd+1);
1245
            std::string::size_type typeEnd = std::string::npos;
1246
            if (typeStart != std::string::npos) typeEnd = line.find_first_of(delim, typeStart+1);
1247
            std::string::size_type valStart = typeStart;
1248
            std::string::size_type valEnd = std::string::npos;
1249
            if (valStart != std::string::npos) valEnd = line.find_last_not_of(delim);
1250

    
1251
            if (   cmdId == reg
1252
                && (   keyEnd == std::string::npos
1253
                    || valStart == std::string::npos)) {
1254
                throw Exiv2::Error(1, Exiv2::toString(num)
1255
                                   + ": " + _("Invalid command line") + " " );
1256
            }
1257

    
1258
            if (   cmdId != reg
1259
                && typeStart != std::string::npos
1260
                && typeEnd != std::string::npos) {
1261
                std::string typeStr(line.substr(typeStart, typeEnd-typeStart));
1262
                Exiv2::TypeId tmpType = Exiv2::TypeInfo::typeId(typeStr);
1263
                if (tmpType != Exiv2::invalidTypeId) {
1264
                    valStart = line.find_first_not_of(delim, typeEnd+1);
1265
                    if (valStart == std::string::npos) {
1266
                        throw Exiv2::Error(1, Exiv2::toString(num)
1267
                                           + ": " + _("Invalid command line") + " " );
1268
                    }
1269
                    type = tmpType;
1270
                    explicitType = true;
1271
                }
1272
            }
1273

    
1274
            if (valStart != std::string::npos) {
1275
                value = parseEscapes(line.substr(valStart, valEnd+1-valStart));
1276
                std::string::size_type last = value.length()-1;
1277
                if (   (value[0] == '"' && value[last] == '"')
1278
                       || (value[0] == '\'' && value[last] == '\'')) {
1279
                    value = value.substr(1, value.length()-2);
1280
                }
1281
            }
1282
        }
1283

    
1284
        modifyCmd.cmdId_ = cmdId;
1285
        modifyCmd.key_ = key;
1286
        modifyCmd.metadataId_ = metadataId;
1287
        modifyCmd.typeId_ = type;
1288
        modifyCmd.explicitType_ = explicitType;
1289
        modifyCmd.value_ = value;
1290

    
1291
        if (cmdId == reg) {
1292
            // Registration needs to be done immediately as the new namespaces are
1293
            // looked up during parsing of subsequent lines (to validate XMP keys).
1294
            Exiv2::XmpProperties::registerNs(modifyCmd.value_, modifyCmd.key_);
1295
        }
1296

    
1297
        return true;
1298
    } // parseLine
1299

    
1300
    CmdId commandId(const std::string& cmdString)
1301
    {
1302
        int i = 0;
1303
        for (;   cmdIdAndString[i].cmdId_ != invalidCmdId
1304
                 && cmdIdAndString[i].cmdString_ != cmdString; ++i) {}
1305
        return cmdIdAndString[i].cmdId_;
1306
    }
1307

    
1308
    std::string parseEscapes(const std::string& input)
1309
    {
1310
        std::string result = "";
1311
        for (unsigned int i = 0; i < input.length(); ++i) {
1312
            char ch = input[i];
1313
            if (ch != '\\') {
1314
                result.push_back(ch);
1315
                continue;
1316
            }
1317
            int escapeStart = i;
1318
            if (!(input.length() - 1 > i)) {
1319
                result.push_back(ch);
1320
                continue;
1321
            }
1322
            ++i;
1323
            ch = input[i];
1324
            switch (ch) {
1325
            case '\\':                          // Escaping of backslash
1326
                result.push_back('\\');
1327
                break;
1328
            case 'r':                           // Escaping of carriage return
1329
                result.push_back('\r');
1330
                break;
1331
            case 'n':                           // Escaping of newline
1332
                result.push_back('\n');
1333
                break;
1334
            case 't':                           // Escaping of tab
1335
                result.push_back('\t');
1336
                break;
1337
            case 'u':                           // Escaping of unicode
1338
                if (input.length() - 4 > i) {
1339
                    int acc = 0;
1340
                    for (int j = 0; j < 4; ++j) {
1341
                        ++i;
1342
                        acc <<= 4;
1343
                        if (input[i] >= '0' && input[i] <= '9') {
1344
                            acc |= input[i] - '0';
1345
                        }
1346
                        else if (input[i] >= 'a' && input[i] <= 'f') {
1347
                            acc |= input[i] - 'a' + 10;
1348
                        }
1349
                        else if (input[i] >= 'A' && input[i] <= 'F') {
1350
                            acc |= input[i] - 'A' + 10;
1351
                        }
1352
                        else {
1353
                            acc = -1;
1354
                            break;
1355
                        }
1356
                    }
1357
                    if (acc == -1) {
1358
                        result.push_back('\\');
1359
                        i = escapeStart;
1360
                        break;
1361
                    }
1362

    
1363
                    std::string ucs2toUtf8 = "";
1364
                    ucs2toUtf8.push_back((char) ((acc & 0xff00) >> 8));
1365
                    ucs2toUtf8.push_back((char) (acc & 0x00ff));
1366

    
1367
                    if (Exiv2::convertStringCharset (ucs2toUtf8, "UCS-2BE", "UTF-8")) {
1368
                        result.append (ucs2toUtf8);
1369
                    }
1370
                }
1371
                else {
1372
                    result.push_back('\\');
1373
                    result.push_back(ch);
1374
                }
1375
                break;
1376
            default:
1377
                result.push_back('\\');
1378
                result.push_back(ch);
1379
            }
1380
        }
1381
        return result;
1382
    }
1383

    
1384
}
1385

    
(2-2/3)