Updated 2011-07-04 22:23:19 by RLE

I am synchronising the content of a (root) directory between a number of machines, in different physical places, in order to provide a solid backup of all my digital pictures across time. However, until a while ago, I have been synchronising by hand. I have now started to look into automatising the process and discovered that the best way to cross machines on the Internet is either FTP or SSH and that most software that perform automatic synchronisation do this based on the time and (possibly) size of files. (and yes I am aware of rsync).

I wrote a the little script below in order to automate the process somewhat, given that the date and time of the picture creation are usually contained in the EXIF data. Since I was parsing the tree of pictures, the script is also performing some other routine operations such as the removal of various files that are automatically created by some software or the printing of files that are not pictures in the directory (and thus, to which you should perhaps pay attention).

I hope that somebody will find this useful. It is far from being clean code, it just suits my needs.
 ###
 # fixpic.tcl
 #
 # Copyright Emmanuel Frecon - emmanuel@sics.se
 # This file is under the BSD license.
 #
 # This script takes a list of directories (current directory if none
 # specified) and performs a number of operations on these.  It is able
 # to remove automatically generated files such as the windows
 # thumbnail database.  It changes the file/time attribute of the file
 # so that it reflects the date/time contained in the EXIF data, if
 # such and possible.  Finally, it is able to print out the name of any
 # file in the directory that has not been modified (or attempted to)
 # in both operations above, which will allow you to detect files that
 # should not be there.  EXIF data extraction is done via the jpeg
 # library of the tcllib or via jhead if loading the jpeg library would
 # not work.
 
 array set options {
     -jhead    off
     -clean    "folder.jpg thumbs.db AlbumArt.jpg digikam.xml descript.ion"
     -touch    "*.jpg *.jpeg"
     -print    "*"
 }
 
 if { [string is false $options(-jhead)] && [catch {package require jpeg}] } {
     puts "ERR: Forcing use of jhead, no jpeg library found"
     set options(-jhead) on
 }
 
 if { [string is true $options(-jhead)] } {
     set topdir [file dirname [info script]]
     set options(jhead) [auto_execok [file join $topdir jhead]]
 }
 
 if { $argc == 0 } {
     set dlist [list .]
 } else {
     set dlist $argv
 }
 
 proc multimatch { ptns str } {
     foreach ptn $ptns {
         if { [string match -nocase $ptn $str] } {
             return 1
         }
     }
 
     return 0
 }
 
 
 proc fix { d } {
     global options
 
     foreach f [glob -nocomplain -directory $d *] {
         if { [file isdirectory $f] } {
             fix $f
         } else {
             set fname [file tail $f]
             if { [multimatch $options(-clean) $fname] } {
                 file delete -force -- $f
             } else {
                 if { [multimatch $options(-touch) $fname] } {
                     if { [string is true $options(-jhead)] } {
                         if { [catch {exec $options(jhead) -ft $f} err] } {
                             puts "ERR: Error when trying to fix date: $err"
                         }
                     } else {
                         if { [::jpeg::isJPEG $f] } {
                             array set exif [::jpeg::getExif $f]
                             set dtfields [array names exif DateTime*]
                             if { [llength $dtfields] > 0 } {
                                 set date $exif([lindex [lsort $dtfields] 0])
                                 set date_p [string replace $date 4 4]
                                 set date_p [string replace $date_p 6 6]
                                 if { [catch {clock scan $date_p} dt] } {
                                     puts "ERR: Cannot parse date of $f: $date"
                                 } else {
                                     file mtime $f $dt
                                 }
                             } else {
                                 puts "ERR: $f has no EXIF date field"
                             }
                         } else {
                             puts "ERR: $f is not a JPEG file"
                         }
                     }
                 } elseif { [multimatch $options(-print) $fname] } {
                     puts "Unknown file type: $f"
                 }
             }
         }
     }
 }
 
 
 foreach d $dlist {
     fix $d
 }