Project

General

Profile

Patch #1233 » geotag.cpp

Anton Keks, 26 Sep 2016 21:29

 
1
// ***************************************************************** -*- C++ -*-
2
// geotag.cpp, $Rev: 2286 $
3
// Sample program to read gpx files and update images with GPS tags
4

    
5
#include <exiv2/exiv2.hpp>
6

    
7
#include <iostream>
8
#include <iomanip>
9
#include <cassert>
10
#include <algorithm>
11

    
12
#include <stdio.h>
13
#include <stdlib.h>
14
#include <time.h>
15
#include <string.h>
16
#include <sys/stat.h>
17
#include <sys/types.h>
18

    
19
#include <expat.h>
20

    
21
#include <vector>
22
#include <string>
23

    
24
#if defined(__MINGW32__) || defined(__MINGW64__)
25
# ifndef  __MINGW__
26
#  define __MINGW__
27
# endif
28
#endif
29

    
30
using namespace std;
31

    
32
#ifndef  lengthof
33
#define  lengthof(x) (sizeof(*x)/sizeof(x))
34
#endif
35
#ifndef nil
36
#define nil NULL
37
#endif
38

    
39
#if defined(_MSC_VER) || defined(__MINGW__)
40
#include <windows.h>
41
char*    realpath(const char* file,char* path);
42
#define  lstat _stat
43
#define  stat  _stat
44
#if      _MSC_VER < 1400
45
#define strcpy_s(d,l,s) strcpy(d,s)
46
#define strcat_s(d,l,s) strcat(d,s)
47
#endif
48
#endif
49

    
50
#if ! defined(_MSC_VER)
51
#include <dirent.h>
52
#include <unistd.h>
53
#include <sys/param.h>
54
#define  stricmp strcasecmp
55
#endif
56

    
57
#ifndef _MAX_PATH
58
#define _MAX_PATH 1024
59
#endif
60

    
61
#define UNUSED(x) (void)(x)
62

    
63
// prototypes
64
class Options;
65
int getFileType(const char* path ,Options& options);
66
int getFileType(std::string& path,Options& options);
67

    
68
string getExifTime(const time_t t);
69
time_t parseTime(const char* ,bool bAdjust=false);
70
int    timeZoneAdjust();
71

    
72
// platform specific code
73
#if defined(_MSC_VER) || defined(__MINGW__)
74
char* realpath(const char* file,char* path)
75
{
76
    char* result = (char*) malloc(_MAX_PATH);
77
    if   (result) GetFullPathName(file,_MAX_PATH,result,NULL);
78
    return result ;
79
    UNUSED(path);
80
}
81
#endif
82

    
83
// Command-line parser
84
class Options  {
85
public:
86
    bool        verbose;
87
    bool        help;
88
    bool        version;
89
    bool        dst;
90
    bool        dryrun;
91

    
92
    Options()
93
    {
94
        verbose     = false;
95
        help        = false;
96
        version     = false;
97
        dst         = false;
98
        dryrun      = false;
99
    }
100

    
101
    virtual ~Options() {} ;
102
} ;
103

    
104
enum
105
{   resultOK=0
106
,   resultSyntaxError
107
,   resultSelectFailed
108
};
109

    
110
enum                        // keyword indices
111
{   kwHELP = 0
112
,   kwVERSION
113
,   kwDST
114
,   kwDRYRUN
115
,   kwVERBOSE
116
,   kwADJUST
117
,   kwTZ
118
,   kwDELTA
119
,   kwMAX                   // manages keyword array
120
,   kwNEEDVALUE             // bogus keywords for error reporting
121
,   kwSYNTAX                // -- ditto --
122
,   kwNOVALUE = -kwVERBOSE  // keywords <= kwNOVALUE are flags (no value needed)
123
};
124

    
125
// file types supported
126
enum
127
{   typeUnknown   = 0
128
,   typeDirectory = 1
129
,   typeImage     = 2
130
,   typeXML       = 3
131
,   typeFile      = 4
132
,   typeDoc       = 5
133
,   typeCode      = 6
134
,   typeMax       = 7
135
};
136

    
137
// Position (from gpx file)
138
class Position
139
{
140
public:
141
             Position(time_t time,double lat,double lon,double ele) : time_(time),lon_(lon),lat_(lat),ele_(ele) {};
142
             Position() { time_=0 ; lon_=0.0 ; lat_=0.0 ; ele_=0.0 ; };
143
    virtual ~Position() {} ;
144
//  copy constructor
145
    Position(const Position& o) : time_(o.time_),lon_(o.lon_),lat_(o.lat_),ele_(o.ele_) {};
146

    
147
//  instance methods
148
    bool good()                 { return time_ || lon_ || lat_ || ele_ ; }
149
    std::string getTimeString() { if ( times_.empty() ) times_ = getExifTime(time_) ;  return times_; }
150
    time_t      getTime()       { return time_ ; }
151
    std::string toString();
152

    
153
//  getters/setters
154
    double lat()            {return lat_   ;}
155
    double lon()            {return lon_   ;}
156
    double ele()            {return ele_   ;}
157
    int    delta()          {return delta_ ;}
158
    void   delta(int delta) {delta_=delta  ;}
159

    
160
//  data
161
private:
162
    time_t      time_;
163
    double      lon_ ;
164
    double      lat_ ;
165
    double      ele_ ;
166
    std::string times_;
167
    int         delta_;
168

    
169
// public static data
170
public:
171
    static int    adjust_  ;
172
    static int    tz_      ;
173
    static int    dst_     ;
174
    static time_t deltaMax_;
175

    
176
// public static member functions
177
public:
178
    static int    Adjust() {return Position::adjust_ + Position::tz_ + Position::dst_ ;}
179
    static int    tz()     {return tz_    ;}
180
    static int    dst()    {return dst_   ;}
181
    static int    adjust() {return adjust_;}
182

    
183
    static std::string toExifString(double d,bool bRational,bool bLat);
184
    static std::string toExifString(double d);
185
    static std::string toExifTimeStamp(std::string& t);
186
};
187

    
188
std::string Position::toExifTimeStamp(std::string& t)
189
{
190
    char        result[200];
191
    const char* arg = t.c_str();
192
    int HH = 0 ;
193
    int mm = 0 ;
194
    int SS = 0 ;
195
    if ( strstr(arg,":") || strstr(arg,"-") ) {
196
        int  YY,MM,DD    ;
197
        char a,b,c,d,e   ;
198
        sscanf(arg,"%d%c%d%c%d%c%d%c%d%c%d",&YY,&a,&MM,&b,&DD,&c,&HH,&d,&mm,&e,&SS);
199
    }
200
    sprintf(result,"%d/1 %d/1 %d/1",HH,mm,SS);
201
    return std::string(result);
202
}
203

    
204
std::string Position::toExifString(double d)
205
{
206
    char result[200];
207
    d *= 100;
208
    sprintf(result,"%d/100",abs((int)d));
209
    return std::string(result);
210
}
211

    
212
std::string Position::toExifString(double d,bool bRational,bool bLat)
213
{
214
    const char* NS   = d>=0.0?"N":"S";
215
    const char* EW   = d>=0.0?"E":"W";
216
	const char* NSEW = bLat  ? NS: EW;
217
    if ( d < 0 ) d = -d;
218
    int deg = (int) d;
219
        d  -= deg;
220
        d  *= 60;
221
    int min = (int) d ;
222
        d  -= min;
223
        d  *= 60;
224
    int sec = (int)d;
225
    char result[200];
226
    sprintf(result,bRational ? "%d/1 %d/1 %d/1%s" : "%03d.%02d'%02d\"%s" ,deg,min,sec,bRational?"":NSEW);
227
    return std::string(result);
228
}
229

    
230
std::string Position::toString()
231
{
232
    char result[200];
233
    std::string sLat = Position::toExifString(lat_,false,true );
234
    std::string sLon = Position::toExifString(lon_,false,false);
235
    sprintf(result,"%s %s %-8.3f",sLon.c_str(),sLat.c_str(),ele_);
236
    return std::string(result);
237
}
238

    
239
int    Position::adjust_   = 0;
240
int    Position::tz_       = timeZoneAdjust();
241
int    Position::dst_      = 0;
242
time_t Position::deltaMax_ = 60 ;
243

    
244
// globals
245
typedef std::map<time_t,Position>           TimeDict_t;
246
typedef std::map<time_t,Position>::iterator TimeDict_i;
247
typedef std::vector<std::string>            strings_t;
248
TimeDict_t   gTimeDict ;
249
strings_t    gFiles;
250

    
251
///////////////////////////////////////////////////////////
252
// UserData - used by XML Parser
253
class UserData
254
{
255
public:
256
    UserData(Options& options) : indent(0),count(0),nTrkpt(0),bTime(false),bEle(false),options_(options) {};
257
    virtual ~UserData() {} ;
258

    
259
//  public data members
260
    int         indent;
261
    size_t      count ;
262
    Position    now ;
263
    Position    prev;
264
    int         nTrkpt;
265
    bool        bTime ;
266
    bool        bEle  ;
267
    double      ele;
268
    double      lat;
269
    double      lon;
270
    std::string xmlt;
271
    std::string exift;
272
    time_t      time;
273
    Options&    options_;
274
// static public data memembers
275
};
276

    
277
// XML Parser Callbacks
278
static void startElement(void* userData, const char* name, const char** atts )
279
{
280
    UserData* me = (UserData*) userData;
281
    //for ( int i = 0 ; i < me->indent ; i++ ) printf(" ");
282
    //printf("begin %s\n",name);
283
    me->bTime = strcmp(name,"time")==0;
284
    me->bEle  = strcmp(name,"ele")==0;
285

    
286
    if ( strcmp(name,"trkpt")==0 ) {
287
        me->nTrkpt++;
288
        while ( *atts ) {
289
            const char* a=atts[0];
290
            const char* v=atts[1];
291
            if ( !strcmp(a,"lat") ) me->lat = atof(v);
292
            if ( !strcmp(a,"lon") ) me->lon = atof(v);
293
            atts += 2 ;
294
        }
295
    }
296
    me->count++  ;
297
    me->indent++ ;
298
}
299

    
300
static void endElement(void* userData, const char* name)
301
{
302
    UserData* me = (UserData*) userData;
303
    me->indent-- ;
304
    if ( strcmp(name,"trkpt")==0 ) {
305

    
306
        me->nTrkpt--;
307
        me->now = Position(me->time,me->lat,me->lon,me->ele) ;
308

    
309
        if ( !me->prev.good() && me->options_.verbose ) {
310
            printf("trkseg %s begin ",me->now.getTimeString().c_str());
311
        }
312

    
313
        // printf("lat,lon = %f,%f ele = %f xml = %s exif = %s\n",me->lat,me->lon,me->ele,me->xmlt.c_str(),me->exift.c_str());
314

    
315
        // if we have a good previous position
316
        // add missed entries to timedict
317
        //if ( me->prev.good() && (me->now.getTime() - me->prev.getTime()) < Position::timeDiffMax ) {
318
        //  time_t missed = me->prev.getTime() ;
319
        //  while ( ++missed < me->now.getTime() )
320
        //      gTimeDict[missed] = me->prev ; // Position(missed,me->lat,me->lon,me->ele) ;
321
        //}
322

    
323
        // remember our location and put it in gTimeDict
324
        gTimeDict[me->time] = me->now ;
325
        me->prev = me->now ;
326
    }
327
    if ( strcmp(name,"trkseg")==0 && me->options_.verbose ) {
328
        printf("%s end\n",me->now.getTimeString().c_str());
329
    }
330
}
331

    
332
void charHandler(void* userData,const char* s,int len)
333
{
334
    UserData* me = (UserData*) userData;
335

    
336
    if ( me->nTrkpt == 1 ) {
337
        char buffer[100];
338
        int  l_max = 98 ; // lengthof(buffer) -2 ;
339

    
340
        if ( me->bTime && len > 5 ) {
341
            if ( len < l_max ) {
342
                memcpy(buffer,s,len);
343
                buffer[len]=0;
344
                char* b = buffer ;
345
                while ( *b == ' ' && b < buffer+len ) b++ ;
346
                me->xmlt  = b ;
347
                me->time  = parseTime(me->xmlt.c_str());
348
                me->exift = getExifTime(me->time);
349
            }
350
            me->bTime=false;
351
        }
352
        if ( me->bEle && len > 2 ) {
353
            if ( len < l_max ) {
354
                memcpy(buffer,s,len);
355
                buffer[len]=0;
356
                char* b = buffer ;
357
                while ( *b == ' ' && b < buffer+len ) b++ ;
358
                me->ele = atof(b);
359
            }
360
            me->bEle=false;
361
        }
362
    }
363
}
364

    
365
///////////////////////////////////////////////////////////
366
// Time Functions
367
time_t parseTime(const char* arg,bool bAdjust)
368
{
369
    time_t result = 0 ;
370
    try {
371
        //559 rmills@rmills-imac:~/bin $ exiv2 -pa ~/R.jpg | grep -i date
372
        //Exif.Image.DateTime                          Ascii      20  2009:08:03 08:58:57
373
        //Exif.Photo.DateTimeOriginal                  Ascii      20  2009:08:03 08:58:57
374
        //Exif.Photo.DateTimeDigitized                 Ascii      20  2009:08:03 08:58:57
375
        //Exif.GPSInfo.GPSDateStamp                    Ascii      21  2009-08-03T15:58:57Z
376

    
377
        // <time>2012-07-14T17:33:16Z</time>
378

    
379
        if ( strstr(arg,":") || strstr(arg,"-") ) {
380
            int  YY,MM,DD,HH,mm,SS ;
381
            char a,b,c,d,e   ;
382
            sscanf(arg,"%d%c%d%c%d%c%d%c%d%c%d",&YY,&a,&MM,&b,&DD,&c,&HH,&d,&mm,&e,&SS);
383

    
384
            struct tm T;
385
    #if 0
386
            int tm_sec;     /* seconds (0 - 60) */
387
            int tm_min;     /* minutes (0 - 59) */
388
            int tm_hour;    /* hours (0 - 23) */
389
            int tm_mday;    /* day of month (1 - 31) */
390
            int tm_mon;     /* month of year (0 - 11) */
391
            int tm_year;    /* year - 1900 */
392
            int tm_wday;    /* day of week (Sunday = 0) */
393
            int tm_yday;    /* day of year (0 - 365) */
394
            int tm_isdst;   /* is summer time in effect? */
395
            char *tm_zone;  /* abbreviation of timezone name */
396
            long tm_gmtoff; /* offset from UTC in seconds */
397
    #endif
398
            memset(&T,0,sizeof(T));
399
            T.tm_min  = mm  ;
400
            T.tm_hour = HH  ;
401
            T.tm_sec  = SS  ;
402
            if ( bAdjust ) T.tm_sec -= Position::Adjust();
403
            T.tm_year = YY -1900 ;
404
            T.tm_mon  = MM -1    ;
405
            T.tm_mday = DD  ;
406
            T.tm_isdst = -1 ; // determine value automatically (otherwise hour may shift)
407
            result = mktime(&T);
408
        }
409
    } catch ( ... ) {};
410
    return result ;
411
}
412

    
413
// West of GMT is negative (PDT = Pacific Daylight = -07:00 == -25200 seconds
414
int timeZoneAdjust()
415
{
416
    time_t    now   = time(NULL);
417
    int       offset;
418

    
419
#if   defined(_MSC_VER) || defined(__MINGW__)
420
    TIME_ZONE_INFORMATION TimeZoneInfo;
421
    GetTimeZoneInformation( &TimeZoneInfo );
422
    offset = - (((int)TimeZoneInfo.Bias + (int)TimeZoneInfo.DaylightBias) * 60);
423
    UNUSED(now);
424
#elif defined(__CYGWIN__)
425
    struct tm lcopy = *localtime(&now);
426
    time_t    gmt   =  timegm(&lcopy) ; // timegm modifies lcopy
427
    offset          = (int) ( ((long signed int) gmt) - ((long signed int) now) ) ;
428
#elif defined(OS_SOLARIS)
429
    struct tm local = *localtime(&now) ;
430
    time_t local_tt = (int) mktime(&local);
431
    time_t time_gmt = (int) mktime(gmtime(&now));
432
    offset          = time_gmt - local_tt;
433
#else
434
    struct tm local = *localtime(&now) ;
435
    offset          = local.tm_gmtoff ;
436
#endif
437

    
438
#if 0
439
    // debugging code
440
    struct tm utc = *gmtime(&now);
441
    printf("utc  :  offset = %6d dst = %d time = %s", 0     ,utc  .tm_isdst, asctime(&utc  ));
442
    printf("local:  offset = %6d dst = %d time = %s", offset,local.tm_isdst, asctime(&local));
443
    printf("timeZoneAdjust = %6d\n",offset);
444
#endif
445
    return offset ;
446
}
447

    
448
string getExifTime(const time_t t)
449
{
450
    static char result[100];
451
    strftime(result,sizeof(result),"%Y-%m-%d %H:%M:%S",localtime(&t));
452
    return result ;
453
}
454

    
455
std::string makePath(std::string dir,std::string file)
456
{
457
    return dir + std::string(EXV_SEPARATOR_STR) + file ;
458
}
459

    
460
const char* makePath(const char* dir,const char* file)
461
{
462
    static char result[_MAX_PATH] ;
463
    std::string r = makePath(std::string(dir),std::string(file));
464
    strcpy(result,r.c_str());
465
    return result;
466
}
467

    
468
// file utilities
469
bool readDir(const char* path,Options& options)
470
{
471
    bool bResult = false;
472

    
473
#ifdef _MSC_VER
474
    DWORD attrs    =  GetFileAttributes(path);
475
    bool  bOKAttrs =  attrs != INVALID_FILE_ATTRIBUTES;
476
    bool  bIsDir   = (attrs  & FILE_ATTRIBUTE_DIRECTORY) ? true : false ;
477

    
478
    if( bOKAttrs && bIsDir ) {
479
        bResult = true ;
480

    
481
        char     search[_MAX_PATH+10];
482
        strcpy_s(search,_MAX_PATH,path);
483
        strcat_s(search,_MAX_PATH,"\\*");
484

    
485
        WIN32_FIND_DATA ffd;
486
        HANDLE  hFind = FindFirstFile(search, &ffd);
487
        BOOL    bGo = hFind != INVALID_HANDLE_VALUE;
488

    
489
        if ( bGo ) {
490
            while ( bGo ) {
491
                if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
492
                {
493
                    // _tprintf(TEXT("  %s   <DIR>\n"), ffd.cFileName);
494
                }
495
                else
496
                {
497
                    std::string pathName = makePath(path,std::string(ffd.cFileName));
498
                    if ( getFileType(pathName,options) == typeImage ) {
499
                        gFiles.push_back( pathName );
500
                    }
501
                }
502
                bGo = FindNextFile(hFind, &ffd) != 0;
503
            }
504
            // CloseHandle(hFind);
505
        }
506
    }
507
#else
508
    DIR*    dir = opendir (path);
509
    if (dir != NULL)
510
    {
511
        bResult = true;
512
        struct dirent*  ent;
513

    
514
        // print all the files and directories within directory
515
        while ((ent = readdir (dir)) != NULL)
516
        {
517
            std::string pathName = makePath(path,ent->d_name);
518
            struct stat  buf     ;
519
            lstat(path, &buf );
520
            if ( ent->d_name[0] != '.' ) {
521

    
522
                // printf("reading %s => %s\n",ent->d_name,pathName.c_str());
523
                if ( getFileType(pathName,options) == typeImage ) {
524
                    gFiles.push_back( pathName );
525
                }
526
            }
527
        }
528
        closedir (dir);
529
    }
530
#endif
531
    return bResult ;
532
}
533

    
534
inline size_t sip(FILE* f,char* buffer,size_t max_len,size_t len)
535
{
536
    while ( !feof(f) && len < max_len && buffer[len-1] != '>')
537
        buffer[len++] = fgetc(f);
538
    return len;
539
}
540

    
541
bool readXML(const char* path,Options& options)
542
{
543
    FILE*       f       = fopen(path,"r");
544
    XML_Parser  parser  = XML_ParserCreate(NULL);
545
    bool bResult        = f && parser ;
546
    if ( bResult ) {
547
        char   buffer[8*1024];
548
        UserData me(options) ;
549

    
550
        XML_SetUserData            (parser, &me);
551
        XML_SetElementHandler      (parser, startElement, endElement);
552
        XML_SetCharacterDataHandler(parser,charHandler);
553

    
554
        // a little sip at the data
555
        size_t len = fread(buffer,1,sizeof(buffer)-100,f);
556
        const char* lead   = "<?xml" ;
557
        bResult = strncmp(lead,buffer,strlen(lead))==0;
558

    
559
        // swallow it
560
        if ( bResult ) {
561
            len = sip(f,buffer,sizeof buffer,len);
562
            bResult = XML_Parse(parser, buffer,(int)len, len == 0 ) == XML_STATUS_OK;
563
        }
564

    
565
        // drink the rest of the file
566
        while ( bResult && len != 0 ) {
567
            len = fread(buffer,1,sizeof(buffer)-100,f);
568
            len = sip(f,buffer,sizeof buffer,len);
569
            bResult = XML_Parse(parser, buffer,(int)len, len == 0 ) == XML_STATUS_OK;
570
        };
571
    }
572

    
573
    if ( f      ) fclose(f);
574
    if ( parser ) XML_ParserFree(parser);
575

    
576
    return bResult ;
577
}
578

    
579
bool readImage(const char* path,Options& /* options */)
580
{
581
    using namespace Exiv2;
582
    bool bResult = false ;
583

    
584
    try {
585
        Image::AutoPtr image = ImageFactory::open(path);
586
        if ( image.get() ) {
587
            image->readMetadata();
588
            ExifData &exifData = image->exifData();
589
            bResult = !exifData.empty();
590
        }
591
    } catch ( ... ) {};
592
    return bResult ;
593
}
594

    
595
time_t readImageTime(std::string path,std::string* pS=NULL)
596
{
597
    using namespace Exiv2;
598

    
599
    time_t       result       = 0 ;
600
	static std::map<std::string,time_t> cache;
601
	if ( cache.count(path) == 1 ) return cache[path];
602

    
603
    const char* dateStrings[] =
604
    { "Exif.Photo.DateTimeOriginal"
605
    , "Exif.Photo.DateTimeDigitized"
606
    , "Exif.Image.DateTime"
607
    , NULL
608
    };
609
    const char* ds            = dateStrings[0] ;
610

    
611
    while ( !result && ds++  ) {
612
        try {
613
            Image::AutoPtr image = ImageFactory::open(path);
614
            if ( image.get() ) {
615
                image->readMetadata();
616
                ExifData &exifData = image->exifData();
617
            //  printf("%s => %s\n",(ds-1), exifData[ds-1].toString().c_str());
618
                result = parseTime(exifData[ds-1].toString().c_str(),true);
619
                if ( result && pS ) *pS = exifData[ds-1].toString();
620
            }
621
        } catch ( ... ) {};
622
    }
623
	if ( result ) cache[path] = result;
624
    return result ;
625
}
626

    
627
bool sina(const char* s,const char** a)
628
{
629
    bool bResult = false ;
630
    int i = 0 ;
631
    while ( *s == '-' ) s++;
632
    while ( !bResult && a[i]) {
633
        const char* A = a[i] ;
634
        while ( *A == '-' ) A++ ;
635
        bResult = stricmp(s,A)==0;
636
        i++;
637
    }
638
    return bResult;
639
}
640

    
641
int readFile(const char* path,Options /* options */)
642
{
643
    FILE* f     = fopen(path,"r");
644
    int nResult = f ? typeFile : typeUnknown;
645
    if (  f ) {
646
        const char* docs[] = { ".doc",".txt", nil };
647
        const char* code[] = { ".cpp",".h"  ,".pl" ,".py" ,".pyc", nil };
648
        const char*  ext   = strstr(path,".");
649
        if  ( ext ) {
650
            if ( sina(ext,docs) ) nResult = typeDoc;
651
            if ( sina(ext,code) ) nResult = typeCode;
652
        }
653
    }
654
    if ( f ) fclose(f) ;
655

    
656
    return nResult ;
657
}
658

    
659
Position* searchTimeDict(TimeDict_t& td, const time_t& time,long long delta)
660
{
661
    Position* result = NULL;
662
    for ( int t = 0 ; !result && t < delta ; t++ ) {
663
        for ( int x = 0 ; !result && x < 2 ; x++ ) {
664
            int T = t * ((x==0)?-1:1);
665
            if ( td.count(time+T) ) {
666
                result = &td[time+T];
667
                result->delta(T);
668
            }
669
        }
670
    }
671
    return result;
672
}
673

    
674
int getFileType(std::string& path,Options& options) { return getFileType(path.c_str(),options); }
675
int getFileType(const char* path,Options& options)
676
{
677
    return readXML  (path,options) ? typeXML
678
        :  readDir  (path,options) ? typeDirectory
679
        :  readImage(path,options) ? typeImage
680
        :  readFile (path,options)
681
        ;
682
}
683

    
684
int version(const char* program)
685
{
686
    printf("%s: %s %s\n",program,__DATE__,__TIME__);
687
    return 0;
688
}
689

    
690
int help(const char* program,char const* words[],int nWords,bool /*bVerbose*/)
691
{
692
    printf("usage: %s ",program);
693
    for ( int i = 0 ; i < nWords ; i++ ) {
694
        if ( words[i] )
695
            printf("%c-%s%s",i?'|':'{',words[i],i>(-kwNOVALUE)?" value":"");
696
    }
697
    printf("} path+\n");
698
    return 0;
699
}
700

    
701
int compare(const char* a,const char* b)
702
{
703
    int result=*a && *b;
704
    while ( result && *a && *b) {
705
        char A=*a++;
706
        char B=*b++;
707
        result=tolower(A)==tolower(B);
708
    }
709
    return result;
710
}
711

    
712
int find(const char* arg,char const* words[],int nWords)
713
{
714
    if ( arg[0] != '-' ) return kwSYNTAX;
715

    
716
    int result=0;
717
    int count =0;
718

    
719
    for ( int i = 0 ; i < nWords ; i++) {
720
        int j = 0 ;
721
        while ( arg[j] == '-' ) j++;
722
        if ( ::compare(arg+j,words[i]) ) {
723
            result = i ;
724
            count++;
725
        }
726
    }
727

    
728
    return count==1?result:kwSYNTAX;
729
}
730

    
731
int parseTZ(const char* adjust)
732
{
733
    int   h=0;
734
    int   m=0;
735
    char  c  ;
736
    try {
737
        sscanf(adjust,"%d%c%d",&h,&c,&m);
738
    } catch ( ... ) {} ;
739

    
740
    return (3600*h)+(60*m);
741
}
742

    
743
bool mySort(std::string a,std::string b)
744
{
745
    time_t A = readImageTime(a);
746
    time_t B = readImageTime(b);
747
    return (A<B);
748
}
749

    
750
int main(int argc,const char* argv[])
751
{
752
    int result=0;
753
    const char* program = argv[0];
754

    
755
    const char* types[typeMax];
756
    types[typeUnknown  ] = "unknown";
757
    types[typeDirectory] = "directory";
758
    types[typeImage    ] = "image";
759
    types[typeXML      ] = "xml";
760
    types[typeDoc      ] = "doc";
761
    types[typeCode     ] = "code";
762
    types[typeFile     ] = "file";
763

    
764
    char const* keywords[kwMAX];
765
    memset(keywords,0,sizeof(keywords));
766
    keywords[kwHELP    ] = "help";
767
    keywords[kwVERSION ] = "version";
768
    keywords[kwVERBOSE ] = "verbose";
769
    keywords[kwDRYRUN  ] = "dryrun";
770
    keywords[kwDST     ] = "dst";
771
    keywords[kwADJUST  ] = "adjust";
772
    keywords[kwTZ      ] = "tz";
773
    keywords[kwDELTA   ] = "delta";
774

    
775
    map<std::string,string> shorts;
776
    shorts["-?"] = "-help";
777
    shorts["-h"] = "-help";
778
    shorts["-v"] = "-verbose";
779
    shorts["-V"] = "-version";
780
    shorts["-d"] = "-dst";
781
    shorts["-a"] = "-adjust";
782
    shorts["-t"] = "-tz";
783
    shorts["-D"] = "-delta";
784
    shorts["-s"] = "-delta";
785
    shorts["-X"] = "-dryrun";
786

    
787
    Options options ;
788
    options.help    = sina(keywords[kwHELP   ],argv) || argc < 2;
789
    options.verbose = sina(keywords[kwVERBOSE],argv);
790
    options.dryrun  = sina(keywords[kwDRYRUN ],argv);
791
    options.version = sina(keywords[kwVERSION],argv);
792
    options.dst     = sina(keywords[kwDST    ],argv);
793
    options.dryrun  = sina(keywords[kwDRYRUN ],argv);
794

    
795
    for ( int i = 1 ; !result && i < argc ; i++ ) {
796
        const char* arg   = argv[i++];
797
        if ( shorts.count(arg) ) arg = shorts[arg].c_str();
798

    
799
        const char* value = argv[i  ];
800
        int        ivalue = ::atoi(value?value:"0");
801
        int         key   = ::find(arg,keywords,kwMAX);
802
        int         needv = key < kwMAX && key > (-kwNOVALUE);
803

    
804
        if (!needv ) i--;
805
        if ( needv && !value) key = kwNEEDVALUE;
806

    
807
        switch ( key ) {
808
            case kwDST      : options.dst     = true ; break;
809
            case kwHELP     : options.help    = true ; break;
810
            case kwVERSION  : options.version = true ; break;
811
            case kwDRYRUN   : options.dryrun  = true ; break;
812
            case kwVERBOSE  : options.verbose = true ; break;
813
            case kwTZ       : Position::tz_      = parseTZ(value);break;
814
            case kwADJUST   : Position::adjust_  = ivalue;break;
815
            case kwDELTA    : Position::deltaMax_= ivalue;break;
816
            case kwNEEDVALUE: fprintf(stderr,"error: %s requires a value\n",arg); result = resultSyntaxError ; break ;
817
            case kwSYNTAX   : default:
818
            {
819
                int  type   = getFileType(arg,options) ;
820
                if ( options.verbose ) printf("%s %s ",arg,types[type]) ;
821
                if ( type == typeImage ) {
822
                    time_t t    = readImageTime(std::string(arg)) ;
823
                    char*  path = realpath(arg,NULL);
824
                    if  ( t && path ) {
825
                        if ( options.verbose) printf("%s %ld %s",path,(long int)t,asctime(localtime(&t)));
826
                        gFiles.push_back(path);
827
                    }
828
                    if ( path ) :: free((void*) path);
829
                }
830
                if ( type == typeUnknown ) {
831
                    fprintf(stderr,"error: illegal syntax %s\n",arg);
832
                    result = resultSyntaxError ;
833
                }
834
                if ( options.verbose ) printf("\n") ;
835
            }break;
836
        }
837
    }
838

    
839
    if ( options.help    ) ::help(program,keywords,kwMAX,options.verbose);
840
    if ( options.version ) ::version(program);
841

    
842
    if ( !result ) {
843
        sort(gFiles.begin(),gFiles.end(),mySort);
844
        if ( options.dst ) Position::dst_ = 3600;
845
        if ( options.verbose ) {
846
            int t = Position::tz();
847
            int d = Position::dst();
848
            int a = Position::adjust();
849
            int A = Position::Adjust();
850
            int s = A     ;
851
            int h = s/3600;
852
                s-= h*3600;
853
                s = abs(s);
854
            int m = s/60  ;
855
                s-= m*60  ;
856
            printf("tz,dst,adjust = %d,%d,%d total = %dsecs (= %d:%d:%d)\n",t,d,a,A,h,m,s);
857
        }
858
        for ( size_t p = 0 ; !options.dryrun && p < gFiles.size() ; p++ ) {
859
            std::string arg = gFiles[p] ;
860
            std::string stamp ;
861
            try {
862
                time_t t       = readImageTime(arg,&stamp) ;
863
                Position* pPos = searchTimeDict(gTimeDict,t,Position::deltaMax_);
864
                Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open(gFiles[p]);
865
                if ( image.get() ) {
866
                    image->readMetadata();
867
                    Exiv2::ExifData& exifData = image->exifData();
868
#if 0
869
                    /*
870
					char* keys[]={ "Exif.Image.GPSTag"
871
						         , "Exif.GPSInfo.GPSProcessingMethod"
872
					             , "Exif.GPSInfo.GPSAltitudeRef"
873
							     , "Exif.GPSInfo.GPSVersionID"
874
                                 , "Exif.GPSInfo.GPSProcessingMethod"
875
                                 , "Exif.GPSInfo.GPSVersionID"
876
                                 , "Exif.GPSInfo.GPSMapDatum"
877
                                 , "Exif.GPSInfo.GPSLatitude"
878
                                 , "Exif.GPSInfo.GPSLongitude"
879
                                 , "Exif.GPSInfo.GPSAltitude"
880
                                 , "Exif.GPSInfo.GPSAltitudeRef"
881
                                 , "Exif.GPSInfo.GPSLatitudeRef"
882
                                 , "Exif.GPSInfo.GPSLongitudeRef"
883
                                 , "Exif.GPSInfo.GPSDateStamp"
884
                                 , "Exif.GPSInfo.GPSTimeStamp"
885
					};
886
					static int bPrint = true ;
887
					for ( int k = 0 ; k < 15 ;   k++ ) {
888
						try {
889
							if ( bPrint ) printf("erasing %s\n",keys[k]);
890
							Exiv2::ExifKey  key = Exiv2::ExifKey(keys[k]);
891
							Exiv2::ExifData::iterator kk = exifData.findKey(key);
892
							if ( kk != exifData.end() ) exifData.erase(kk);
893
						} catch (...) {};
894
					}
895
					bPrint = false;
896
                    */
897
#endif
898
#if 0
899
					Exiv2::ExifData::const_iterator end = exifData.end();
900
					for (Exiv2::ExifData::iterator i = exifData.begin(); i != end; ++i) {
901
						char name[100];
902
						strcpy(name,i->key().c_str());
903
						// std::cout << "sniff " << i->key() << std::endl;
904
						if ( strstr(name,"GPS") )  {
905
							Exiv2::ExifData::iterator pos;
906
							Exiv2::ExifKey exifKey = Exiv2::ExifKey(name);
907
							pos = exifData.findKey(exifKey);
908
							while( pos != exifData.end()) {
909
								exifData.erase(pos);
910
							}
911
						}
912
					}
913
#endif
914
					if ( pPos ) {
915
						/*
916
						   struct _stat buf;
917
   int result;
918
   char timebuf[26];
919
   char* filename = "crt_stat.c";
920
   errno_t err;
921

    
922
   // Get data associated with "crt_stat.c":
923
   result = _stat( filename, &buf );
924

    
925
   int _utime(
926
   const char *filename,
927
   struct _utimbuf *times
928
);
929
   */
930

    
931
                        exifData["Exif.GPSInfo.GPSProcessingMethod" ] = "65 83 67 73 73 0 0 0 72 89 66 82 73 68 45 70 73 88"; // ASCII HYBRID-FIX
932
                        exifData["Exif.GPSInfo.GPSVersionID"        ] = "2 2 0 0";
933
                        exifData["Exif.GPSInfo.GPSMapDatum"         ] = "WGS-84";
934

    
935
                        exifData["Exif.GPSInfo.GPSLatitude"         ] = Position::toExifString(pPos->lat(),true,true);
936
                        exifData["Exif.GPSInfo.GPSLongitude"        ] = Position::toExifString(pPos->lon(),true,false);
937
                        exifData["Exif.GPSInfo.GPSAltitude"         ] = Position::toExifString(pPos->ele());
938

    
939
                        exifData["Exif.GPSInfo.GPSAltitudeRef"      ] = pPos->ele()<0.0?"1":"0";
940
						exifData["Exif.GPSInfo.GPSLatitudeRef"      ] = pPos->lat()>0?"N":"S";
941
						exifData["Exif.GPSInfo.GPSLongitudeRef"     ] = pPos->lon()>0?"E":"W";
942

    
943
                        exifData["Exif.GPSInfo.GPSDateStamp"        ] = stamp;
944
                        exifData["Exif.GPSInfo.GPSTimeStamp"        ] = Position::toExifTimeStamp(stamp);
945
						exifData["Exif.Image.GPSTag"                ] = 4908;
946

    
947
						printf("%s %s % 2d\n",arg.c_str(),pPos->toString().c_str(),pPos->delta());
948
                    } else {
949
                        printf("%s *** not in time dict ***\n",arg.c_str());
950
                    }
951
                    image->writeMetadata();
952
				}
953
            } catch ( ... ) {};
954
        }
955
    }
956

    
957
    return result ;
958
}
959

    
960
// That's all Folks!
961
////
(1-1/2)