#
#  gpsman --- GPS Manager: a manager for GPS receiver data
#
# Copyright (c) 1998-2008 Miguel Filgueiras mig@ncc.up.pt Universidade do Porto
#
#    This program is free software; you can redistribute it and/or modify
#      it under the terms of the GNU General Public License as published by
#      the Free Software Foundation; either version 2 of the License, or
#      (at your option) any later version.
#
#      This program is distributed in the hope that it will be useful,
#      but WITHOUT ANY WARRANTY; without even the implied warranty of
#      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#      GNU General Public License for more details.
#
#      You should have received a copy of the GNU General Public License
#      along with this program.
#
#  File: files_foreign.tcl
#  Last change:  25 September 2008
#
# See below for guidelines on how to add support for new foreign formats
#
# Includes contributions by
#  - Brian Baulch (baulchb _AT_ onthenet.com.au) marked "BSB contribution"
#  - Matt Martin (matt.martin _AT_ ieee.org) marked "MGM contribution"
#  - Miguel Filgueiras (to code by others) marked "MF contribution"
#  - Valere Robin (valere.robin _AT_ wanadoo.fr) marked "VR contribution"
#  - Alessandro Palmas (alpalmas _AT_ tin.it) marked "AP contribution"
#  - Paul Scorer (p.scorer _AT_ leedsmet.ac.uk) marked "PS contribution"
#
# and changes by
#  - Mariusz Dabrowski (mgd4 _AT_ poczta.onet.pl) marked "MD changes"
#
# Includes an adaptation of a Perl script by Niki Hammler, http://www.nobaq.net
#  that converts exported FUGAWI data to DDD GPSman data
#

#### guidelines on how to add support for new foreign formats
#
# - choose a name for the new format: start with a capital letter,
#   keep the name short as it will be used in menus, and check that
#   it is not already in use
#
# - each file format must be described in the FILEFORMAT array that is
#   defined in metadata.tcl; see the comment there that documents the
#   entries in the array and prepare a description of the new format
#
# - find out an existing format similar to the new format, in particular
#   having the same "filetype" and the same "mode" and use its implementation
#   as a model (the GPSMan format is implemented in files.tcl, and part of
#   this code is used in the support for GPStrans; it is a good idea to
#   choose the implementation of other formats as models)
#
# - for importation a procedure Import_$FMT must be written whose arguments
#   are
#      - if the "filemode" is "unique" (only a possible items type): file
#        channel to read from, and reading mode that is either "normal"
#        or "inGR" (importing elements in a group; disregard if dealing
#        with groups is not supported)
#      - otherwise: the file channel, a list of types to import or
#        "Data", and the reading mode ("normal" or "inGR")
#
# - for exportation a procedure Export_$FMT must be written whose arguments
#   are
#      - if the "filemode" is "data": the file channel to write to and
#        a list of pairs with type and indices of the items to export
#      - otherwise: the file channel, the type and the list of indices
#
# - care must be taken to avoid clashes with existing global variables; it
#   is suggested that an array with name FF_$FMT (with $FMT the name
#   of the new format) is used for storing all global information needed
#   for its implementation

##### utility

proc NextLine {n file} {
    # read next $n-th (>=1) non empty line
    # return trimmed line or "" if end of file found
    global MESS

    set k 0
    while 1 {
	if { [eof $file] } {
	    GMMessage $MESS(badfile)
	    return ""
	}
	gets $file line ; set line [string trim $line]
	if { $line != "" && [incr k] == $n } { return $line }
    }
    # not used
    return
}

##### reading/writing binary files

## binary coding of files:
#   little- and big-endian integral numbers
#    int, int_big: 16 bits;  long, long_big: 32 bits
#   fixed-length strings: as sequence of bytes with given length
#   variable-length strings: length as int (little-endian), then the
#    sequence of bytes
#   floats and doubles: IEEE little-endian
#   booleans as ints
#   arrays of multiples: e.g., array@3=double,long is an array of pairs
#    whose length is given by element at index 3 (from 0) on the data list
#    (index < index of array); element type cannot be array! a list of lists
#    is used as the internal representation of the array

array set BinConv {
    byte   c    byte,l 1
    int    s    int,l  2
    bool   s    bool,l 2
    int_big S    int_big,l 2
    long   i    long,l 4
    long_big I   long_big,l 4
}

proc ReadBinData {file types} {
    # adapted from proc UnPackData (garmin.tcl)
    # read binary data and convert to list of elements conforming to the
    #  types in the list $types
    # accepted types: byte, int, int_big, long, long_big, float, double,
    #                 charray=*, varstring, unused=*, bool, array@*=*
    #  bool is the same as int
    # delete leading and trailing spaces of strings and char arrays
    # return list of values read in
    global tcl_platform ReadBinError BinConv

    set vals ""
    foreach t $types {
	switch -glob $t {
	    byte {
		set n 1
		binary scan [read $file $n] "c" x
		set x [expr ($x+0x100)%0x100]
	    }
	    bool -   int -   int_big -   long - 
	    long_big {
		set n $BinConv($t,l)
		binary scan [read $file $n] $BinConv($t) x
	    }
	    float {
		# this only works with machines following the
		#  IEEE standard floating point representation
		set n 4
		set bs [read $file $n] 
		if { $tcl_platform(byteOrder) == "littleEndian" } {
		    binary scan $bs "f" x
		} else {
		    set id ""
		    foreach k "3 2 1 0" {
			append id [string index $bs $k]
		    }
		    binary scan $id "f" x
		}
	    }
	    double {
		# this only works with machines following the
		#  IEEE standard floating point representation
		set n 8
		set bs [read $file $n]
		if { $tcl_platform(byteOrder) == "littleEndian" } {
		    binary scan $bs "d" x
		} else {
		    set id ""
		    foreach k "7 6 5 4 3 2 1 0" {
			append id [string index $bs $k]
		    }
		    binary scan $id "d" x
		}
	    }
	    varstring {
		if { [set lg [ReadBinData $file int]] < 0 } {
		    set ReadBinError 1
		    return $vals
		}
		set x [read $file $lg]
		set n [expr 2+$lg]
		set x [string trim $x " "]
	    }
	    charray=* {
		regsub charray= $t "" n
		set x [read $file $n]
		set x [string trim $x " "]
	    }
	    array*@* {
		if { ! [regexp {array@([0-9]+)=(.+)$} $t m ix lst] || \
			[set lg [lindex $vals $ix]] < 0 || \
			! [regexp {^[0-9]+$} $lg] } {
		    set ReadBinError 1
		    return $vals
		}
		set x "" ; set sts [split $lst ","]
		while { $lg } {
		    incr lg -1
		    lappend x [ReadBinData $file $sts]
		    if { $ReadBinError } { return [lappend vals $x] }
		}
	    }
	    unused=* {
		regsub unused= $t "" n
		read $file $n
		set x UNUSED
	    }
	    default {
		set ReadBinError 1
		return $vals
	    }
	}
	lappend vals $x
    }
    return $vals
}

proc WriteBinData {file types vals} {
    # adapted from proc DataToStr (garmin.tcl)
    # write binary data the result of converting from list of elements
    #  conforming to the types in $types
    # accepted types: byte, int, int_big, long, long_big, float, double,
    #                 charray=*, varstring, unused=*, bool, array@*=*
    #  bool is the same as int
    # return 0 on error
    global tcl_platform BinConv

    foreach t $types v $vals {
	switch -glob $t {
	    byte -  bool -  int -  int_big -  long -
	    long_big {
		puts -nonewline $file [binary format $BinConv($t) $v]
	    }
	    float {
		# this only works with machines following the
		#  IEEE standard floating point representation
		set s [binary format "f" $v]
		if { $tcl_platform(byteOrder) != "littleEndian" } {
		    set l [split "$s" ""]
		    set s ""
		    foreach k "3 2 1 0" {
			append s [lindex $l $k]
		    }
		}
		puts -nonewline $file $s
	    }
	    double {
		# this only works with machines following the
		#  IEEE standard floating point representation
		set s [binary format "d" $v]
		if { $tcl_platform(byteOrder) != "littleEndian" } {
		    set l [split "$s" ""]
		    set s ""
		    foreach k "7 6 5 4 3 2 1 0" {
			append s [lindex $l $k]
		    }
		}
		puts -nonewline $file $s
	    }
	    varstring {
		WriteBinData $file int [string length $v]
		puts -nonewline $file [binary format "a*" $v]
	    }
	    charray=* {
		regsub charray= $t "" n
		puts -nonewline $file [binary format "A$n" $v]
	    }
	    array*@* {
		if { ! [regexp {array@([0-9]+)=(.+)$} $t m ix lst] || \
			[set lg [lindex $vals $ix]] < 0 || \
			! [regexp {^[0-9]+$} $lg] } {
		    BUG "error in specification to WriteBinData: $t $lg"
		    return 0
		}
		set sts [split $lst ","]
		while { $lg } {
		    incr lg -1
		    if { ! [WriteBinData $file $sts [lindex $v 0]] } {
			return 0
		    }
		    set v [lreplace $v 0 0]
		}
	    }
	    unused=* {
		regsub unused= $t "" n
		puts -nonewline $file [binary format "x$n"]
	    }
	    default {
		BUG "unimplemented data type when converting to binary: $t"
		return 0
	    }
	}
    }
    flush $file
    return 1
}

##### exporting

proc ExportFile {how fmt what} {
    # write data to file in foreign format
    #  $fmt such that "out" is in $FILEFORMAT($fmt,mode) except GPSMan
    #  $how==all: all items if $what==Data, otherwise items of type $what 
    #  $how==select: items of type $what chosen from list
    # possible types must be described by $FILEFORMAT($fmt,types) or the
    #  the second element of the pair $FILEFORMAT($fmt,io_types) and
    #  if $FILEFORMAT($fmt,filetype)!="data" there is a single type
    # return 0 on success, 1 on failure

    return [ExportFileTo "" $how $fmt $what]
}

proc ExportFileTo {file how fmt what} {
    # if $file!="stdout" it will be closed at the end
    global SFilePFrmt SFileDatum Storage TXT FILEFORMAT

    switch $how {
	all {
	    # the order of the list is that imposed by $FILEFORMAT($fmt,types)
	    #  or the second element of the pair $FILEFORMAT($fmt,io_types);
	    #  this may be important when writing to files in a format that
	    #  imposes a specific order in the data
	    if { [catch {set ts $FILEFORMAT($fmt,types)}] } {
		set ts [lindex $FILEFORMAT($fmt,io_types) 1]
	    }
	    set lp [AllIndicesForType $what $ts]
	}
	select {
	    if { [set ixs [ChooseItems $what]] == "" } { return 1 }
	    set lp [list [list $what $ixs]]
	}
    }
    if { $FILEFORMAT($fmt,filetype) != "data" } {
	set ixs [lindex [lindex $lp 0] 1]
	set ft type
    } else { set ft data }
    if { $fmt == "Shapefile" } {
	# single type
	return [ExportShapefileTo $file $what $ixs]
    }
    if { $file == "" && \
	    [set file [GMOpenFile $TXT(exportto) $what w]] == ".." } {
	return 1
    }
    SlowOpWindow $TXT(export)
    if { $ft == "data" } {
	Export_$fmt $file $lp
    } else { Export_$fmt $file $what $ixs }
    foreach v "PFrmt PFType Datum" { catch {unset SFile$v($file)} }
    if { $file != "stdout" } { close $file }
    SlowOpFinish ""
    return 0
}

proc ExportGREls {how fmt args} {
    # write data on elements of group to file in foreign format
    #  $fmt such that "out" is in $FILEFORMAT($fmt,mode) except GPSMan, and
    #      $FILEFORMAT($fmt,GREls) is defined
    #  $how==all: from all groups, but types are selected
    #  $how==select: groups and types are selected by the user
    #  $args not used but in call-back
    # possible types must be described by $FILEFORMAT($fmt,types) or
    #  the second element of the pair $FILEFORMAT($fmt,io_types) and
    #  if $FILEFORMAT($fmt,filetype)!="data" there is a single type
    global GRName SFilePFrmt SFileDatum MESS TXT FILEFORMAT

    if { [catch {set types $FILEFORMAT($fmt,types)}] } {
	set types [lindex $FILEFORMAT($fmt,io_types) 1]
    }
    lappend types GR
    switch $how {
	all {
	    set ixs [array names GRName]
	}
	select {
	    if { [set ixs [ChooseItems GR]] == "" } { return }
	}
    }
    set ts ""
    foreach k $types { lappend ts $TXT(name$k) }
    if { $FILEFORMAT($fmt,filetype) == "data" } {
	set ft data
    } else { set ft type }
    while 1 {
	# select types of items to export keeping the order in $types
	set whs [GMChooseFrom many $MESS(putwhat) 6 $ts $types]
	if { [set i [lsearch -exact $whs GR]] != -1 } {
	    set whs [lreplace $whs $i $i]
	    set rec 1
	} else { set rec 0 }
	if { [set n [llength $whs]] == 0 } { return }
	if { $n > 1 && $ft == "type" } {
	    GMMessage $MESS(exportonly1)
	} else {
	    break
	}
    }
    set lp "" ; set none 1
    foreach wh $whs {
	if { [set eixs [GRsElements $ixs $rec $wh]] != "" } { set none 0 }
	lappend lp [list $wh $eixs]
    }
    if { $none } { return }
    if { $fmt == "Shapefile" } {
	# single type
	ExportShapefileTo "" $whs $eixs
	return
    }
    set f [GMOpenFile $TXT(exportto) GR w]
    if { $f != ".." } {
	SlowOpWindow $TXT(export)
	if { $ft == "data" } {
	    Export_$fmt $f $lp
	} else { Export_$fmt $f $whs $eixs }
	catch {
	    unset SFilePFrmt($f) ; unset SFileDatum($f)
	}
	close $f
	SlowOpFinish ""
    }
    return
}

## GPStrans format

proc Export_GPStrans {file what ixs} {

    ExportGPStransHeader $file
    ExportGPStrans$what $file $ixs 1
    return
}

proc ExportGPStransHeader {file} {
    # write header to file in GPStrans format, using $pformt for positions
    global TimeOffset Datum SFilePFrmt SFileDatum

    set dix [DatumRefId $Datum]
    if { ! [regexp {^[0-9]+$} $dix] } {
	# datum not defined in GPStrans
	set datum "WGS 84"
	set dix [DatumRefId $datum]
    } else { set datum $Datum }
    set h [format {Format: DDD  UTC Offset: %6.2f hrs  Datum[%3d]: %s} \
	    $TimeOffset $dix $datum]
    puts $file $h
    set SFileDatum($file) $datum
            # following values assumed below
    set SFilePFrmt($file) DDD
    return
}

proc ExportGPStransWP {file ixs tofile} {
    # write WPs with indices in list $ixs either to file in GPStrans format,
    #  or to auxiliary file for putting data into receiver
    global WPName WPCommt WPPosn WPDatum WPDate
    global CREATIONDATE SFileDatum

    set d [Today MMDDYYYY]
    foreach i $ixs {
	if { [SlowOpAborted] } { return }
	if { $i != -1 } {
	    if { $CREATIONDATE } {
		set d $WPDate($i)
	    }
	    set latd [lindex $WPPosn($i) 0] ; set longd [lindex $WPPosn($i) 1]
	    if { $WPDatum($i) != $SFileDatum($file) } {
		set p [ToDatum $latd $longd $WPDatum($i) $SFileDatum($file)]
		set latd [lindex $p 0] ; set longd [lindex $p 1]
	    }
	    if { $tofile } {
		set m [format "W\t%s\t%40s\t%20s\t%03.7f\t%04.7f" \
			      $WPName($i) $WPCommt($i) $d $latd $longd]
	    } else {
		set m [format "W\t%s\t%40s\t%20s\t%03.7f\t%04.7f" \
		       $WPName($i) [JustfLeft 40 $WPCommt($i)] $d $latd $longd]
	    }
	    puts $file $m
	}
    }
    return
}

proc ExportGPStransRT {file ixs tofile} {
    # write RTs with indices in list $ixs either to file in GPStrans format,
    #  or to auxiliary file for putting data into receiver
    global RTIdNumber RTCommt RTWPoints MAXROUTES MESS

    set badids 0
    foreach i $ixs {
	if { [SlowOpAborted] } { return }
	if { ! [CheckNumber Ignore $RTIdNumber($i)] } {
	    incr badids
	} else {
	    set wps [Apply "$RTWPoints($i)" IndexNamed WP]
	    if { [Undefined $wps] } {
		GMMessage [format $MESS(undefWP) $RTIdNumber($i)]
	    } elseif { !$tofile && $RTIdNumber($i)>$MAXROUTES } {
		GMMessage [format $MESS(bigRT) $RTIdNumber($i)]
	    } else {
		puts $file [format "R\t%d\t%40s" $RTIdNumber($i) \
			                [JustfLeft 40 $RTCommt($i)]]
		ExportGPStransWP $file $wps $tofile
	    }
	}
    }
    if { $badids > 0 } { GMMessage [format $MESS(cantsaveRTid) $badids] }
    return
}

proc ExportGPStransTR {file ixs tofile} {
    # write TRs with indices in list $ixs either to file in GPStrans format,
    #  or to auxiliary file for putting data into receiver
    global TRTPoints TRDatum SFileDatum

    foreach i $ixs {
	if { [SlowOpAborted] } { return }
	if { $TRDatum($i) != $SFileDatum($file) } {
	    set tps [ChangeTPsDatum $TRTPoints($i) $TRDatum($i) \
			 $SFileDatum($file)]
	} else { set tps $TRTPoints($i) }
	foreach tp $tps {
	    if { [SlowOpAborted] } { return }
	    puts $file [format "T\t%s\t%lf\t%lf" [lindex $tp 4] \
			    [lindex $tp 0] [lindex $tp 1]]
	}
	puts $file ""
    }
    return
}

proc WriteXMLTag { type value } {
   regsub -all {&}  $value {\&amp;}  value
   regsub -all {>}  $value {\&gt;}  value
   regsub -all {<}  $value {\&lt;}   value
   regsub -all {"}  $value {\&quot;} value
   regsub -all {\'} $value {\&apos;} value
   return "<$type>$value</$type>\n"	
}

## GPX format
## Contributed by Valere Robin (valere.robin@wanadoo.fr)

# MF change: using system encoding
set GPX_Header "<?xml version=\"1.0\" encoding=\"$SYSENC\" standalone=\"yes\"?>
<gpx
 version=\"1.0\"
 creator=\"GPSMan\" 
 xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"
 xmlns=\"http://www.topografix.com/GPX/1/0\"
 xmlns:topografix=\"http://www.topografix.com/GPX/Private/TopoGrafix/0/2\"
 xsi:schemaLocation=\"http://www.topografix.com/GPX/1/0 http://www.topografix.com/GPX/1/0/gpx.xsd http://www.topografix.com/GPX/Private/TopoGrafix/0/2 http://www.topografix.com/GPX/Private/TopoGrafix/0/2/topografix.xsd\">
 <author>an author</author>
 <email>an_email@somewhere</email>
 <url>an_url</url>
 <urlname>a_url_name</urlname>"

proc Export_GPX {file typesixs} {
    # MF contribution: code by VR moved out from general export procs
    
    ExportGPXHeader $file
    foreach p $typesixs {
	ExportGPX[lindex $p 0] $file [lindex $p 1] 1
    }
    ExportGPXTrailer $file
    return
}

proc ExportGPXHeader {file} {
    # write header to file in GPX format, using $pformt for positions
    global GPX_Header SFilePFrmt SFileDatum

    puts $file $GPX_Header
    puts $file "<time>[TodayUTC ISO8601 ]</time>"
    set SFileDatum($file) "WGS 84"
    set SFilePFrmt($file) DDD
    return
}

proc ExportGPXTrailer {file} {
    puts $file "</gpx>"
}

proc ExportGPXWP {file ixs tofile } {
    # write WPs with indices in list $ixs either to file in GPX format,
    #  or to auxiliary file for putting data into receiver
    # type can be "wpt" or "rtept"
    ExportGPXPT $file $ixs $tofile wpt
}

proc ExportGPXData {file ixs tofile } {
    # write WPs with indices in list $ixs either to file in GPX format,
    #  or to auxiliary file for putting data into receiver
    # type can be "wpt" or "rtept"
    ExportGPXWP $file $ixs $tofile 
    ExportGPXRT $file $ixs $tofile
    ExportGPXTR $file $ixs $tofile
}

proc ExportGPXPT {file ixs tofile type} {
    # write WPs with indices in list $ixs either to file in GPX format,
    #  or to auxiliary file for putting data into receiver
    # type can be "wpt" or "rtept"
    global WPName WPCommt WPPosn WPDatum WPDate WPObs WPSymbol WPAlt
    global SFileDatum

    foreach i $ixs {
	if { [SlowOpAborted] } { return }
	if { $i != -1 } {
	    set latd [lindex $WPPosn($i) 0] ; set longd [lindex $WPPosn($i) 1]
	    if { $WPDatum($i) != $SFileDatum($file) } {
		# MF change: calling ToDatum instead of ConvertDatum
		set p [ToDatum $latd $longd $WPDatum($i) $SFileDatum($file)]
		set latd [lindex $p 0] ; set longd [lindex $p 1]
	    }
	    puts $file [format "<$type lat=\"%03.7f\" lon=\"%04.7f\">" \
			    $latd $longd ]
	    # MF contribution: elevation field
	    #  altitude in metres
	    if { [set alt [lindex $WPAlt($i) 0]] != "" } {
		puts $file " <ele>$alt</ele>"
	    }
	    #---
	    puts $file [ WriteXMLTag "name" $WPName($i) ]
	    # puts $file [format " <name>%s</name>" $WPName($i) $WPObs($i)]
	    set date $WPDate($i)
	    if { $date != "" } {
	        set datesecs [lindex [CheckConvDate $date ] 1]
		set dints [ DateIntsFromSecs $datesecs ] 
	    	set utcdints [ eval LocalTimeAndUTC $dints "local"] 
	    	set time [eval FormatDate "ISO8601" $utcdints]
		puts $file " <time>$time</time>"
	    }
	    if { $WPSymbol($i) != "" } {
		puts $file [format " <symbol>%s</symbol>\n" $WPSymbol($i) ]
	    }
	    if { $WPCommt($i) != "" } {
		puts $file [ WriteXMLTag "cmt" $WPCommt($i) ]
	    }
	    if { $WPObs($i) != "" } {
		puts $file [ WriteXMLTag "desc" $WPObs($i) ]
	    }
	    puts $file "</$type>\n"
	}
    }
    return
}

proc ExportGPXRT {file ixs tofile} {
    # write RTs with indices in list $ixs either to file in GPX format,
    #  or to auxiliary file for putting data into receiver
    global RTIdNumber RTCommt RTObs RTWPoints MAXROUTES MESS

    set badids 0
    foreach i $ixs {
	if { [SlowOpAborted] } { return }
	set wps [Apply "$RTWPoints($i)" IndexNamed WP]
	if { [Undefined $wps] } {
	    GMMessage [format $MESS(undefWP) $RTIdNumber($i)]
	} elseif { !$tofile && $RTIdNumber($i)>$MAXROUTES } {
	    GMMessage [format $MESS(bigRT) $RTIdNumber($i)]
	} else {
	    puts $file "<rte>"
	    puts $file [ WriteXMLTag "name" $RTIdNumber($i) ]
	    if { $RTCommt($i) != "" } {
		puts $file [ WriteXMLTag "desc" $RTCommt($i) ]
	    }
	    if { $RTObs($i) != "" } {
		puts $file [ WriteXMLTag "cmt" $RTObs($i) ]
	    }
	    ExportGPXPT $file $wps $tofile rtept
	    puts $file "</rte>"
	}
    }
    if { $badids > 0 } { GMMessage [format $MESS(cantsaveRTid) $badids] }
    return
}

proc ExportGPXTR {file ixs tofile} {
    # write TRs with indices in list $ixs either to file in GPX format,
    #  or to auxiliary file for putting data into receiver
    global TRTPoints TRObs TRDatum SFileDatum DataIndex TRName TRSegStarts

    set idt $DataIndex(TPdate)
    set ial $DataIndex(TPalt) ; set idp $DataIndex(TPdepth)
    set ids $DataIndex(TPsecs)

    foreach i $ixs {
        puts $file "<trk>"
	puts $file [ WriteXMLTag "name" $TRName($i) ]
	if { $TRObs($i) != "" } {
	    puts $file [ WriteXMLTag "desc" $TRObs($i) ]
	}
        puts $file " <trkseg>\n"
	if { [SlowOpAborted] } { return }
	# MF change: using proc ChangeTPsDatum
	if { $TRDatum($i) != $SFileDatum($file) } {
	    set tps [ChangeTPsDatum $TRTPoints($i) $TRDatum($i) \
			 $SFileDatum($file)]
	} else { set tps $TRTPoints($i) }
	# MF contribution: segment starters
	set ssts $TRSegStarts($i)
	set tpn 0 ; set nsst [lindex $ssts 0]
	#--
	foreach tp $tps {
	    if { [SlowOpAborted] } { return }
	    # MF contribution: segment starters
	    if { $nsst == $tpn } {
		puts $file "</trkseg><trkseg>\n"
		set ssts [lreplace $ssts 0 0]
		set nsst [lindex $ssts 0]
	    }
	    incr tpn
	    #--
	    set latd [lindex $tp 0] ; set longd [lindex $tp 1]
	    set alt [lindex $tp $ial ]
	    set secs [lindex $tp $ids ]
	    puts $file "<trkpt lat=\"$latd\" lon=\"$longd\">"
	    set dints [ DateIntsFromSecs $secs ] 
	    set utcdints [ eval LocalTimeAndUTC $dints "local"] 
	    set time [eval FormatDate "ISO8601" $utcdints]
	    puts $file "  <ele>$alt</ele>\n  <time>$time</time>\n</trkpt>"
	}
        puts $file "</trkseg></trk>"
    }
    return
}


## KML format
## Contributed by Valere Robin (valere . robin _at_ wanadoo.fr)

# MF change: using system encoding
set KML_Header "<?xml version=\"1.0\" encoding=\"$SYSENC\"?>
<kml xmlns=\"http://earth.google.com/kml/2.2\">
<Document>
	<name>GPSMan Export</name>
	<open>1</open>
	<description><!\[CDATA\[This document was exported from <a href=\"http://www.ncc.up.pt/gpsman/\">GPSMan</a>.\]\]></description>
	<Style id=\"RedLine\">
		<LineStyle>
			<color>7f0000ff</color>
			<width>4</width>
		</LineStyle>
	</Style>
	<Style id=\"YellowLine\">
		<LineStyle>
			<color>ff00ffff</color>
			<width>2</width>
		</LineStyle>
	</Style>
	<Style id=\"NormalPoint\">
		<IconStyle>
			<color>ffff00ff</color>
			<scale>0.7</scale>
			<Icon>
				<href>http://maps.google.com/mapfiles/kml/shapes/shaded_dot.png</href>
			</Icon>
		</IconStyle>
	</Style>
	<Style id=\"HighlightedPoint\">
		<IconStyle>
			<color>ff7f00ff</color>
			<scale>0.9</scale>
			<Icon>
				<href>http://maps.google.com/mapfiles/kml/shapes/shaded_dot.png</href>
			</Icon>
		</IconStyle>
	</Style>
	<StyleMap id=\"WayPoint\">
		<Pair>
			<key>normal</key>
			<styleUrl>#NormalPoint</styleUrl>
		</Pair>
		<Pair>
			<key>highlight</key>
			<styleUrl>#HighlightedPoint</styleUrl>
		</Pair>
	</StyleMap>
"

proc Export_KML {file typesixs} {
    ExportKMLHeader $file
    foreach p $typesixs {
	ExportKML[lindex $p 0] $file [lindex $p 1] 1
    }
    ExportKMLTrailer $file
    return
}

proc ExportKMLHeader {file} {
    # write header to file in KML format
    global KML_Header SFilePFrmt SFileDatum

    puts $file $KML_Header
    puts $file "<time>[TodayUTC ISO8601 ]</time>"
    set SFileDatum($file) "WGS 84"
    set SFilePFrmt($file) DDD
    return
}

proc ExportKMLTrailer {file} {
    puts $file "</Document></kml>"
}

proc ExportKMLWP {file ixs tofile } {
    # write WPs with indices in list $ixs either to file in KML format,
    puts $file "<Folder><name>Waypoints</name>"
    ExportKMLPT $file $ixs $tofile wpt
    puts $file "</Folder>"
}

proc ExportKMLData {file ixs tofile } {
    # write WPs with indices in list $ixs either to file in KML format,
    ExportKMLWP $file $ixs $tofile 
    ExportKMLRT $file $ixs $tofile
    ExportKMLTR $file $ixs $tofile
}

proc ExportKMLPT {file ixs tofile type} {
    # write WPs with indices in list $ixs either to file in KML format,
    # type can be "wpt" or "rtept"
    global WPName WPCommt WPPosn WPDatum WPDate WPObs WPSymbol WPAlt
    global SFileDatum

    foreach i $ixs {
	if { [SlowOpAborted] } { return }
	if { $i != -1 } {
	    set latd [lindex $WPPosn($i) 0] ; set longd [lindex $WPPosn($i) 1]
	    if { $WPDatum($i) != $SFileDatum($file) } {
		# MF change: calling ToDatum instead of ConvertDatum
		set p [ToDatum $latd $longd $WPDatum($i) $SFileDatum($file)]
		set latd [lindex $p 0] ; set longd [lindex $p 1]
	    }
	    if { [set alt [lindex $WPAlt($i) 0]] != "" } {
	    } else {
		set alt 0
	    }
	    set ptdesc [format "<Placemark> <Point><coordinates>%03.7f,%04.7f,%04.7f</coordinates></Point>" \
			    $longd $latd $alt]
	    append ptdesc [ WriteXMLTag "name" $WPName($i) ]
	    set date $WPDate($i)
            set comment ""
	    if { $date != "" } {
	        set datesecs [lindex [CheckConvDate $date ] 1]
		set dints [ DateIntsFromSecs $datesecs ] 
	    	set utcdints [ eval LocalTimeAndUTC $dints "local"] 
	    	set time [eval FormatDate "ISO8601" $utcdints]
		#append comment "<!\[CDATA\[<b>time:</b>$time<br>\]\]>"
	    }
	    if { $WPSymbol($i) != "" } {
		#puts $file [format " <symbol>%s</symbol>\n" $WPSymbol($i) ]
	    }
	    if { $WPCommt($i) != "" } {
		#puts $file [ WriteXMLTag "cmt" $WPCommt($i) ]
		append comment "$WPCommt($i)"
	    }
	    if { $WPObs($i) != "" } {
		#puts $file [ WriteXMLTag "desc" $WPObs($i) ]
		append comment "$WPObs($i)"
	    }
	    append ptdesc "<description>$comment</description>"
	    append ptdesc "<styleUrl>#WayPoint</styleUrl>"
	    append ptdesc "</Placemark>"
	    if { $type == "wpt" } {
		puts $file $ptdesc
	    } elseif { $type == "rtept" } {
		puts $file "$longd,$latd,$alt"
            }
	}
    }

    return
}

proc ExportKMLRT {file ixs tofile} {
    # write RTs with indices in list $ixs either to file in KML format,
    global RTIdNumber RTCommt RTObs RTWPoints MAXROUTES MESS

    set badids 0
    puts $file "<Folder><name>Routes</name><visibility>1</visibility>"
    puts $file "<open>0</open>"
    set RTcoordinates ""
    foreach i $ixs {
	if { [SlowOpAborted] } { return }
	set wps [Apply "$RTWPoints($i)" IndexNamed WP]
	if { [Undefined $wps] } {
	    GMMessage [format $MESS(undefWP) $RTIdNumber($i)]
	} elseif { !$tofile && $RTIdNumber($i)>$MAXROUTES } {
	    GMMessage [format $MESS(bigRT) $RTIdNumber($i)]
	} else {
    	    puts $file "<Placemark>"
	    puts $file [ WriteXMLTag "name" $RTIdNumber($i) ]
	    puts $file "<open>0</open>"
	    set comment ""
            if { $RTCommt($i) != "" } {
		append comment "$RTCommt($i)"
	    }
	    if { $RTObs($i) != "" } {
		append comment " - $RTObs($i)"
	    }
            puts $file [ WriteXMLTag "description" $comment ]
    	    puts $file "<styleUrl>#YellowLine</styleUrl><LineString>"
            puts $file "<tessellate>1</tessellate><coordinates>"
	    ExportKMLPT $file $wps $tofile rtept
	    puts $file "</coordinates></LineString></Placemark>"
       }
    }
    puts $file "</Folder>"
    if { $badids > 0 } { GMMessage [format $MESS(cantsaveRTid) $badids] }
    return
}

proc ExportKMLTR {file ixs tofile} {
    # write TRs with indices in list $ixs either to file in KML format,
    global TRTPoints TRObs TRDatum SFileDatum DataIndex TRName TRSegStarts

    set idt $DataIndex(TPdate)
    set ial $DataIndex(TPalt) ; set idp $DataIndex(TPdepth)
    set ids $DataIndex(TPsecs)

    puts $file "<Folder><name>Tracks</name>"
    puts $file "<Visibility>1</Visibility>"
    puts $file "<Open>0</Open>"
    foreach i $ixs {
	puts $file "<Folder>"
	puts $file [ WriteXMLTag "name" $TRName($i) ]
	if { $TRObs($i) != "" } {
	    puts $file [ WriteXMLTag "description" "$TRObs($i)" ]
	}
        puts $file "<Placemark id=\"Path\">"
        puts $file "<name>Segment</name>"
        puts $file "<styleUrl>#RedLine</styleUrl>"
        puts $file " <LineString><tessellate>1</tessellate><coordinates>\n"
	if { [SlowOpAborted] } { return }
	# MF change: using proc ChangeTPsDatum
	if { $TRDatum($i) != $SFileDatum($file) } {
	    set tps [ChangeTPsDatum $TRTPoints($i) $TRDatum($i) \
			 $SFileDatum($file)]
	} else { set tps $TRTPoints($i) }
	# MF contribution: segment starters
	set ssts $TRSegStarts($i)
	set tpn 0 ; set nsst [lindex $ssts 0]
	#--
	foreach tp $tps {
	    if { [SlowOpAborted] } { return }
	    # MF contribution: segment starters
	    if { $nsst == $tpn } {
		puts $file "</coordinates></LineString>"
		puts $file "</Placemark>"
	        puts $file "<Placemark id=\"Path\">"
			puts $file "<name>Segment</name>"
	        puts $file "<styleUrl>#RedLine</styleUrl>"
		puts $file "<LineString><tessellate>1</tessellate><coordinates>\n"
		set ssts [lreplace $ssts 0 0]
		set nsst [lindex $ssts 0]
	    }
	    incr tpn
	    #--
	    set latd [lindex $tp 0] ; set longd [lindex $tp 1]
	    set alt [lindex $tp $ial ]
	    set secs [lindex $tp $ids ]
	    puts $file "$longd,$latd,$alt"
	    #set dints [ DateIntsFromSecs $secs ] 
	    #set utcdints [ eval LocalTimeAndUTC $dints "local"] 
	    #set time [eval FormatDate "ISO8601" $utcdints]
	    #puts $file "  <ele>$alt</ele>\n  <time>$time</time>\n</trkpt>"
	}
        puts $file "</coordinates></LineString></Placemark>"
        puts $file "</Folder>"
    }
    puts $file "</Folder>"
    return
}

## Ozi format
# Contributed by Alessandro Palmas (alpalmas@tin.it)
# Thanks to:
# Alex Mottram - gpsbabel staff - http://gpsbabel.sourceforge.net
# Dave Patton  - http://www.confluence.org/

proc Export_Ozi {file what ixs} {
    # MF contribution: code moved out from general export procs

    ExportOzi$what $file $ixs
    return
}

proc DelphiTime {date} {
    # compute Delphi time (starting 1899-12-30 0:0:0) from GPSMan date
    # return the empty list on error

    if { [set l [ScanDate $date]] == "" } { return "" }
    foreach "Y m D hh mm ss" $l {}
    set seconds_a [DateToSecsFrom $Y $m $D $hh $mm $ss 1950]
    set seconds_b [DateToSecsFrom 1950 1 1 0 0 0 1899]
    set seconds_0 [DateToSecsFrom 1899 12 30 0 0 0 1899]
    set delphi_1 [expr 1.0 * ($seconds_b - $seconds_0)/86400.0]
    set delphi_2 [expr 1.0 * $seconds_a/86400.0]
    set delphi [expr $delphi_1 + $delphi_2]
    return $delphi
}

proc ExportOziWP {file ixs} {
    # derived from proc ExportGPStransWP
    # write WPs with indices in list $ixs to file in OZI format
    global WPName WPCommt WPPosn WPDatum WPDate
    global Datum
    global CREATIONDATE SFileDatum
    global ALSCALEFOR MESS TXT
    global WPAlt

    set d [Today MM/DD/YYYY]

    # $file must end in .wpt
    # we will have some checks here... To Be Done

    puts $file "OziExplorer Waypoint File Version 1.1"
    puts $file "WGS 84"
    puts $file "Reserved 2"	;# reserved for future use
    puts $file "Reserved 3"	;# GPS Symbol Set, unused

    # since max wp number is 1000 per file, we must trace this in $n;
    set n 0	;# count wp in current $file
    set nf 0	;# count for $file name
    foreach i $ixs {
        if { [SlowOpAborted] } { return }
        if { $i != -1 } {
            if { $CREATIONDATE } {
                set d $WPDate($i)
            }
            set latd [lindex $WPPosn($i) 0] ; set longd [lindex $WPPosn($i) 1]
            if { $WPDatum($i) != "WGS 84" } {
		# MF change: calling ToDatum instead of ConvertDatum
                set p [ToDatum $latd $longd $WPDatum($i) "WGS 84"]
                set latd [lindex $p 0] ; set longd [lindex $p 1]
            }
	    set n [expr $n+1]
            if {$n > 1000} {
                GMMessage [format $MESS(toomany) $TXT(waypoint) 1000]
            	return
            }
            puts -nonewline $file "$n,"			;# Field  1: number
            puts -nonewline $file "$WPName($i),"	;# Field  2: Name (short)
            puts -nonewline $file "$latd,"		;# Field  3: lat DDD
            puts -nonewline $file "$longd,"		;# Field  4: long DDD

            set delphitime [DelphiTime $d]
            puts -nonewline $file "$delphitime,"	;# Field  5: date, in delphi format

            puts -nonewline $file "0,"			;# Field  6: symbol
            puts -nonewline $file "1,"			;# Field  7: fixed
            puts -nonewline $file "3,"			;# Field  8: map display format
            puts -nonewline $file "0,"			;# Field  9: Foreground color: Black
            puts -nonewline $file "65535,"		;# Field 10: Background color: White
            set wpcomm40 [string range $WPCommt($i) 0 39]
            puts -nonewline $file "$wpcomm40,"			;# Field 11: Comment (Max 40, no commas) TBD
            puts -nonewline $file ","			;# Field 12: Direction
            puts -nonewline $file "0,"			;# Field 13: Garmin Display Format
            puts -nonewline $file "0,"			;# Field 14: Proximity Distance( 0 is off)

            set mt [lindex $WPAlt($i) 0]
            # if not valid set ft -777
	    if {$mt  == ""} {
	    	set ft -777 
	    } else {
            	set ft [expr $mt/$ALSCALEFOR(FT)]
            }
            puts -nonewline $file "$ft,"		;# Field 15: Altitude, in feet. -777 is non valid
            puts -nonewline $file "6,"			;# Field 16: Font size
            puts -nonewline $file "0,"			;# Field 17: Font style: 0 normal, 1 bold
            puts -nonewline $file "17"			;# Field 18: Symbol Size, 17 is normal
            puts $file ""
        }
    }
    return
}

proc ExportOziTR {file ixs} {
    # write TRs with indices in list $ixs to file in Ozi format
    global TRTPoints TRDatum SFileDatum
    global TRName
    global ALSCALEFOR
    global DataIndex

    # It seems that in ozi 1 file <-> 1 track, anyway let's go...
    foreach i $ixs {
        if { [SlowOpAborted] } { return }
        if { $TRDatum($i) != "WGS 84" } {
            set tps [ChangeTPsDatum $TRTPoints($i) $TRDatum($i) \
                         "WGS 84"]
        } else { set tps $TRTPoints($i) }

	#Line 1: File type & version 
	puts $file "OziExplorer Track Point File Version 2.1"
	#Line 2: Datum
	puts $file "WGS 84"
	#Line 3: Reminder
	puts $file "Altitude is in Feet"
	#Line 4: Reserved line
	puts $file "Reserved 4"
	#Line 5: Various fields
	# in GPSBabel they use: 0,2,255,ComplimentsOfGPSBabel,0,0,2,8421376
        puts -nonewline $file "0,"	;# Field 1: fixed
        puts -nonewline $file "2,"	;# Field 2: track size
        puts -nonewline $file "255,"	;# Field 3: track color

        regsub -all -- {(?=[[:punct:]])} $TRName($i) "-" trackname
        puts -nonewline $file "$trackname,"	;# Field 4: track name ( no commas! TBD regexp) 

        puts $file "0,0,2,8421376"	;# Field 5,6,7,8: track type & style
	#Line 6: Unused, if you want, put here number of track points
	puts $file "0"
        
        foreach tp $tps {
            if { [SlowOpAborted] } { return }

	    puts -nonewline $file "[lindex $tp 0],"	; # 1- Lat, DDD
	    puts -nonewline $file "[lindex $tp 1],"	; # 2- Lon, DDD
	    puts -nonewline $file "0,"			; # 3-  0: no break line, 1: break line

	    set mt [lindex $tp $DataIndex(TPalt)]
	    # if not valid set ft -777
	    if {$mt  == ""} {
	    	set ft -777 
	    } else {
            	set ft [expr $mt/$ALSCALEFOR(FT)]
            }
	    puts -nonewline $file "$ft,"		; # 4- altitude
	    
	    set date [lindex $tp 4]
            set delphitime [DelphiTime $date]
	    puts -nonewline $file "$delphitime,"	; # 5- Date Delphi format TBD
	    puts -nonewline $file ","			; # 6- Date MM/DD/YYYY TBD
	    puts -nonewline $file ","			; # 7- Time TBD
	    puts $file ""
        }
    }
    return
}

# ------------------------------------------------------------ #

## Shapefile format

set SHPUndef -1e40
# datum must be kept compatible with position format
set SHPDatum "WGS 84"
set SHPPFormt DDD
set SHPZone ""
set SHPDUnit $ALTUNIT
set SHPAUnit $ALTUNIT
set SHPDim 3

proc ExportShapefileTo {fname what ixs} {
    # export to Shapefile format items with indices $ixs of type $what
    #  $fname may be empty in which case the user is asked to give a file name
    #  $what in {WP, RT, TR, LN}
    # all data converted to $SHPDatum (currently set to "WGS 84")
    # return 1 on error, 0 on success
    global WPName WPCommt WPDate WPPFrmt WPPosn WPDatum WPAlt RTIdNumber \
	RTCommt RTWPoints TRName TRObs TRDatum TRTPoints TRSegStarts \
	LNName LNObs LNPFrmt LNDatum LNLPoints LNSegStarts DataIndex MESS \
	TXT SHPUndef SHPPFormt SHPDatum SHPDUnit SHPAUnit SHPDim GFPFormt \
	GFDUnit GFAUnit NNUMPFORMATS POSTYPE ALSCALEFOR INVTXT GSHPVersion

    if { $fname == "" } {
	set ok 0
	set GFPFormt $TXT($SHPPFormt)
	set GFDUnit $TXT($SHPDUnit) ; set GFAUnit $TXT($SHPAUnit)
	set vs "SHPDim GFPFormt SHPDatum GFDUnit GFAUnit"
	set pfas [list $NNUMPFORMATS =GFPFormt TXT]
	set us [list $TXT(M) $TXT(FT)]
	set ds [list +$TXT(dimens)/[list 3 2] \
		    !$TXT(optPositionFormat)=FillPFormtMenu/$pfas \
		    !$TXT(datum)=FillDatumMenu/ \
		    +$TXT(distunit)/$us +$TXT(altunit)/$us]
	while { [set fn [GMGetFileName $TXT(exportto) $what w $vs $ds]] \
		!= ".." } {
	    set basename [file rootname $fn]
	    switch -- [set ext [file extension $fn]] {
		.shp -  .shx -  .dbf -  "" {
		    set ok 1
		}
		default {
		    if { [GMConfirm [format $MESS(shpext) $ext]] } {
			set ok 1
		    }
		}
	    }
	    if { $ok && ( [file exists $basename.shp] || \
			      [file exists $basename.shx] || \
			      [file exists $basename.dbf] ) } {
		if { [GMSelect $MESS(filexists) \
			  [list $TXT(ovwrt) $TXT(cancel)] "0 1"] } {
		    set ok 0
		}
	    }
	    if { $ok } {
		set SHPPFormt $INVTXT($GFPFormt)
		if { [BadDatumFor $SHPPFormt $SHPDatum GMMessage] != 0 } {
		    set ok 0
		} else {
		    set SHPDUnit $INVTXT($GFDUnit)
		    set SHPAUnit $INVTXT($GFAUnit)
		    break
		}
	    }
	}
	if { ! $ok } { return 1 }
    } else {
	# SHPDim, SHPPFormt, SHPDatum, SHPDUnit and SHPAUnit assumed to be
	#  defined
	set basename [file rootname $fname]
    }
    if { $what == "LN" } {
	set shpwh TR
    } else { set shpwh $what }
    if { [set fsid [GSHPCreateFiles $basename $shpwh $SHPDim]] < 1 } {
	switch -- $fsid {
	    0 { set m shpcntopen }
	    -1 -
	    -2 { BUG invalid type or dim }
	    -3 { set m shpcntcrtfs }
	    -4 { set m shpoutmem }
	}
	GMMessage $MESS($m) ; GSHPCloseFiles $fsid
	return 1
    }
    foreach "xix yix" $POSTYPE($SHPPFormt,xyixs) {}
    if { $POSTYPE($SHPPFormt) == "latlong" } {
	set scdist 1
    } else { set scdist $ALSCALEFOR($SHPDUnit) }
    set scalt $ALSCALEFOR($SHPAUnit)
    SlowOpWindow $TXT(export)
    switch $what {
	WP {
	    foreach ix $ixs {
		if { [SlowOpAborted] } { break }
		set p $WPPosn($ix)
		if { $WPPFrmt($ix) != $SHPPFormt || \
			 $WPDatum($ix) != $SHPDatum } {
		    set p [lindex [FormatPosition [lindex $p 0] [lindex $p 1] \
				       $WPDatum($ix) $SHPPFormt $SHPDatum] 0]
		}
		if { [lindex $p 2] == "--" } {
		    GMMessage $MESS(outofgrid)
		    continue
		}
		set x [expr $scdist*[lindex $p $xix]]
		set y [expr $scdist*[lindex $p $yix]]
		if { $SHPDim == 3 } {
		    if { [set alt [lindex $WPAlt($ix) 0]] == "" } {
			set alt $SHPUndef
		    } else { set alt [expr $alt*$scalt] }
		    set r [GSHPWriteWP $fsid $x $y $alt $WPName($ix) \
			       $WPCommt($ix) $WPDate($ix)]
		} else {
		    set r [GSHPWriteWP $fsid $x $y $WPName($ix) $WPCommt($ix) \
			       $WPDate($ix)]
		}
		switch -- $r {
		    -3 {
			SlowOpFinishStop $MESS(shpoutmem)
			return 1
		    }
		    -4 { SlowOpFinish $MESS(shpcntwrtfs) ; return 1 }
		}
	    }
	}
	RT {
	    foreach ix $ixs {
		if { [SlowOpAborted] } { break }
		GSHPCreateRT $SHPDim $RTIdNumber($ix) $RTCommt($ix)
		set wpixs [Apply "$RTWPoints($ix)" IndexNamed WP]
		if { [Undefined $wpixs] } {
		    SlowOpFinish [format $MESS(undefWP) $RTIdNumber($ix)]
		    return 1
		} else {
		    foreach wpix $wpixs {
			if { [SlowOpAborted] } { break }
			set p $WPPosn($wpix)
			if { $WPPFrmt($wpix) != $SHPPFormt || \
				 $WPDatum($wpix) != $SHPDatum } {
			    set p [lindex [FormatPosition [lindex $p 0] \
					       [lindex $p 1] $WPDatum($wix) \
					       $SHPPFormt $SHPDatum] 0]
			}
			if { [lindex $p 2] == "--" } {
			    GMMessage $MESS(outofgrid)
			    continue
			}
			set x [expr $scdist*[lindex $p $xix]]
			set y [expr $scdist*[lindex $p $yix]]
			if { $SHPDim == 3 } {
			    if { [set alt [lindex $WPAlt($wpix) 0]] == "" } {
				set alt $SHPUndef
			    } else { set alt [expr $scalt*$alt] }
			    set r [GSHPAddWPToRT $x $y $alt]
			} else {
			    set r [GSHPAddWPToRT $x $y]
			}
			if { $r == -2 } {
			    SlowOpFinish $MESS(shpoutmem)
			    return 1
			}
		    }
		    switch -- [GSHPWriteRT $fsid 1] {
			-5 { SlowOpFinish $MESS(shpoutmem) ; return 1 }
			-6 { SlowOpFinish $MESS(shpcntwrtfs) ; return 1 }
		    }
		}
	    }
	}
	TR {
	    set ilt $DataIndex(TPlatd) ; set ilg $DataIndex(TPlongd)
	    set ial $DataIndex(TPalt)
	    set wsegs [expr $GSHPVersion >= 1.1]
	    foreach ix $ixs {
		if { [SlowOpAborted] } { break }
		if { $wsegs && $TRSegStarts($ix) != "" } {
		    switch -- [GSHPCreateTR $SHPDim $TRName($ix) $TRObs($ix) \
				   $TRSegStarts($ix)] {
			-2 { SlowOpFinish $MESS(shpoutmem) ; return 1 }
			-3 { BUG invalid segment starters }
		    }
		} else {
		    GSHPCreateTR $SHPDim $TRName($ix) $TRObs($ix)
		}
		if { $TRDatum($ix) != $SHPDatum } {
		    set tps [ChangeTPsDatum $TRTPoints($ix) \
			    $TRDatum($ix) $SHPDatum]
		} else { set tps $TRTPoints($ix) }
		foreach tp $tps {
		    if { [SlowOpAborted] } { break }
		    if { $SHPPFormt != "DMS" } {
			set p [lindex \
			  [FormatPosition [lindex $tp $ilt] [lindex $tp $ilg] \
				   $SHPDatum $SHPPFormt $SHPDatum] 0]
			if { [lindex $p 2] == "--" } {
			    GMMessage $MESS(outofgrid)
			    continue
			}
		    } else { set p $tp }
		    set x [expr $scdist*[lindex $p $xix]]
		    set y [expr $scdist*[lindex $p $yix]]
		    if { $SHPDim == 3 } {
			if { [set alt [lindex [lindex $tp $ial] 0]] == "" } {
			    set alt $SHPUndef
			} else { set alt [expr $scalt*$alt] }
			set r [GSHPAddTPToTR $x $y $alt]
		    } else {
			set r [GSHPAddTPToTR $x $y]
		    }
		    if { $r == -2 } {
			SlowOpFinish $MESS(shpoutmem)
			return 1
		    }
		}
		switch -- [GSHPWriteTR $fsid 1] {
		    -5 { SlowOpFinish $MESS(shpoutmem) ; return 1 }
		    -6 { SlowOpFinish $MESS(shpcntwrtfs) ; return 1 }
		    -7 { BUG segment starter too large }
		}
	    }
	}
	LN {
	    set ipos $DataIndex(LPposn) ; set ial $DataIndex(LPalt)
	    set wsegs [expr $GSHPVersion >= 1.1]
	    foreach ix $ixs {
		if { [SlowOpAborted] } { break }
		if { $wsegs && $LNSegStarts($ix) != "" } {
		    switch -- [GSHPCreateTR $SHPDim $LNName($ix) $LNObs($ix) \
				   $LNSegStarts($ix)] {
			-2 { SlowOpFinish $MESS(shpoutmem) ; return 1 }
			-3 { BUG invalid segment starters }
		    }
		} else {
		    GSHPCreateTR $SHPDim $LNName($ix) $LNObs($ix)
		}
		if { $LNDatum($ix) != $SHPDatum } {
		    if { [set lps [ChangeLPsDatum $LNLPoints($ix) \
			         $LNDatum($ix) $SHPDatum $SHPPFormt]] == -1 } {
			SlowOpFinish ""
			return 1
		    }
		    set pformt $SHPPFormt
		} else {
		    set lps $LNLPoints($ix) ; set pformt $LNPFrmt($ix)
		}
		foreach lp $lps {
		    if { [SlowOpAborted] } { break }
		    set p [lindex $lp $ipos]
		    if { $pformt != $SHPPFormt } {
			set p [lindex \
				  [FormatPosition [lindex $p 0] [lindex $p 1] \
					$SHPDatum $SHPPFormt $SHPDatum] 0]
			if { [lindex $p 2] == "--" } {
			    GMMessage $MESS(outofgrid)
			    continue
			}
		    }
		    set x [expr $scdist*[lindex $p $xix]]
		    set y [expr $scdist*[lindex $p $yix]]
		    if { $SHPDim == 3 } {
			if { [set alt [lindex [lindex $lp $ial] 0]] == "" } {
			    set alt $SHPUndef
			} else { set alt [expr $scalt*$alt] }
			set r [GSHPAddTPToTR $x $y $alt]
		    } else {
			set r [GSHPAddTPToTR $x $y]
		    }
		    if { $r == -2 } {
			SlowOpFinish $MESS(shpoutmem)
			return 1
		    }
		}
		switch -- [GSHPWriteTR $fsid 1] {
		    -5 { SlowOpFinish $MESS(shpoutmem) ; return 1 }
		    -6 { SlowOpFinish $MESS(shpcntwrtfs) ; return 1 }
		    -7 { BUG segment starter too large }
		}
	    }
	}
    }
    GSHPCloseFiles $fsid
    SlowOpFinish ""
    return 0
}

## SimpleText format
# based on the Garmin Simple Text Output protocol and the following rules
# 	- position status: g or G, for 2D or 3D, depending on altitude being
#         defined
# 	- EPH: always ___
# 	- altitude: _____ if undefined, with position status g
# 	- horizontal speed: computed from previous point if any, or undefined
# 	- vertical speed: computed from previous point if any, or undefined
# 	- the first TP of a TR after a different TR is preceded by two
#         sentences with all fields as undefined
#       - the first TP of a TR segment (not the first one) is preceded by
#         a sentence with all fields undefined

proc Export_SimpleText {file type ixs} {
    # write TRs with indices in list $ixs to file in SimpleText format
    #  $type assumed to be TR
    global TRTPoints TRDatum TRSegStarts DataIndex

    if { $type != "TR" } { BUG Export_SimpleText not on TR? }
    foreach d "latd longd date secs alt" { set ix$d $DataIndex(TP$d) }
    set segstart "@______________________________________________________\r"
    set first 1
    foreach ix $ixs {
	if { $TRDatum($ix) != "WGS 84" } {
	    set tps [ChangeTPsDatum $TRTPoints($ix) $TRDatum($ix) "WGS 84"]
	} else { set tps $TRTPoints($ix) }
	set sgsts $TRSegStarts($ix)
	if { $first } {
	    set ssg [lindex $sgsts 0] ; set sgsts [lreplace $sgsts 0 0]
	    set first 0
	    set platd ""
	} else {
	    set ssg 0
	    puts $file $segstart
	}
	set tpn 0
	foreach tp $tps {
	    if { [SlowOpAborted] } { return }
	    if { $tpn == $ssg } {
		puts $file $segstart
		set ssg [lindex $sgsts 0] ; set sgsts [lreplace $sgsts 0 0]
		set platd ""
	    }
	    incr tpn
	    foreach d "latd longd date secs alt" {
		set $d [lindex $tp [set ix$d]]
	    }
	    set sent @
	    # date
	    set vs [DateIntsFromSecs $secs]
	    append sent [format %02d [expr [lindex $vs 0]%100]]
	    foreach v [lreplace $vs 0 0] { append sent [format %02d $v] }
	    # position
	    foreach x [list $latd $longd] hs "{N S} {E W}" wd "2 3" {
		if { $x < 0 } {
		    set h [lindex $hs 1] ; set x [expr -$x]
		} else { set h [lindex $hs 0] }
		set ds [expr int(floor($x))]
		append sent $h [format "%0${wd}d" $ds] \
		    [format %05d [expr round(($x-$ds)*60000)]]
	    }
	    # status, EPH and altitude
	    if { [set alt [lindex $alt 0]] == "" } {
		set status g ; set ealt ______
	    } else {
		set status G
		if { $alt < 0 } {
		    set s - ; set ealt [expr -$alt]
		} else { set s + ; set ealt $alt }
		set ealt "$s[format %05d [expr round($ealt)]]"
	    }
	    append sent $status ___ $ealt
	    # velocity
	    if { $platd == "" || $secs-$psecs == 0 } {
		# no previous TP or with the same time-stamp
		append sent "_______________\r"
	    } else {
		set dt [expr $secs-$psecs]
		set cosmlat [expr cos(($platd+$latd)*0.00872664625997164788)]
		# dm/s
		set velx [expr 1111200.0*($longd-$plongd)*$cosmlat/$dt]
		set vely [expr 1111200.0*($latd-$platd)/$dt]
		foreach c [list $vely $velx] hs "{E W} {N S}" {
		    if { $c < 0 } {
			append sent [lindex $hs 1] ; set c [expr -$c]
		    } else { append sent [lindex $hs 0] }
		    append sent [format %04d [expr round($c)]]
		}
		# vertical speed
		if { $alt != "" && $palt != "" } {
		    # cm/s
		    set velh [expr round(($alt-$palt)*100.0/$dt)]
		    if { $velh < 0 } {
			append sent D ; set velh [expr -$velh]
		    } else { append sent U }
		    append sent [format %04d $velh] \r
		} else { append sent "_____\r" }
	    }
	    set platd $latd ; set plongd $longd ; set palt $alt
	    set psecs $secs
	    
	    puts $file $sent
	}
    }
    return
}



##### importing

proc OpenImportFileFails {what fmt} {
    # open file in foreign format and set initial values for importing data
    #  $what in $TYPES
    # return 0 unless operation is to be cancelled

    return [OpenInputFileFails $what $fmt]
}

proc OpenFileWithExtension {filename what exts} {
    # open file with base name $filename and extension in $exts and
    #  if they cannot be opened for reading, ask the user
    # return ".." on error, otherwise the file channel
    # the actual file path is stored in the global File($what)
    #  $what is in $FileTypes
    global File TXT

    set bn [file rootname $filename]
    foreach ext $exts {
	if { ! [catch {set f [open $bn.$ext r]}] } {
	    set File($what) [file join [pwd] $bn.$ext]
	    return $f
	}
    }
    if { [set f [GMOpenFile $TXT(loadfrm) $what r]] == ".." } {
	return ".."
    }
    return $f
}

### geo-referencing files

## Tiff World File format

proc ImportTFW {filename} {
    # read coordinates tranformation parameters from TFW file
    # try files with base name $filename and extension .tfw or .TFW and
    #  if they cannot be opened for reading, ask the user
    # return 0 on failure, or pair with empty list (no known points to be
    #  projected) and list with the values in the first six lines:
    #  x-scale factor, rotation angle(?) of x-axis, rotation angle(?) of
    #  y-axis, y-scale factor, false easting (minimum x), false northing
    #  (max y)
    global File MESS

    if { [set f [OpenFileWithExtension $filename MapBkInfo "tfw TFW"]] == \
	     ".." } {
	return 0
    }
    set filename [file tail $File(MapBkInfo)]
    set i 0 ; set vals ""
    while { ! [eof $f] } {
	gets $f line
	if { $line != "" } {
	    if { [scan $line %f v] != 1 } {
		set vals 0
		break
	    }
	    lappend vals $v
	    incr i
	}
    }
    close $f
    if { $i != 6 } {
	GMMessage "$MESS(badmapinfo): $filename"
    } elseif { [lindex $vals 1] != 0 || [lindex $vals 2] != 0 } {
	GMMessage "Non-null rotation parameters: result may be wrong. Please send a message to mig@ncc.up.pt"
    }
    return [list "" $vals]
}

## partial support for OziExplorer .map files
# with thanks to Paulo Quaresma (pq@di.uevora.pt) for the information on
#  the different forms of line 4 and to Kari Likovuori
#  (kari.likovuori@gmail.com) for information leading to not dealing with
#  projected points

array set OziMapFAs {
    start  {prefix {OziExplorer Map Data File} line 5}
    5  {datum 0 line 8}
    8  {prefix {Magnetic Variation,,,} line 9}
    9  {info "" search {Projection Setup,} ps}
    ps {info "" search {MMPXY,} xy}
    xy {loop mmpxy search {MMPLL,} ll}
    ll {loop mmpll end}

    mmpxy,numxy {^MMPXY,([0-9]+),([0-9]+),([0-9]+)$}
    mmpll,latlong {^MMPLL,([0-9]+),([^,]*),(.+)$}
}

array set OziDatum {
    {Ascension Island 1958} {Ascension Island `58}
    {Astro Beacon 1945} {Astro Beacon "E"}
    {Astronomic Stn 1952} {Astronomic Stn `52}
    {Australian Geocentric 1994 (GDA94)} {WGS 84}
    {Australian Geod 1984} {Australian Geod `84}
    {Australian Geod 1966} {Australian Geod `66}
    {European 1950 (Mean France)} {European 1950; NW Europe}
    {European 1950 (Spain and Portugal)} {European 1950; Portugal+Spain}
    {Geodetic Datum 1949} {Geodetic Datum `49}
    {Hartebeeshoek94} {WGS 84}
    {Indian Bangladesh} {Indian (Bangladesh)}
    {ISTS 073 Astro 1969} {ISTS 073 Astro `69}
    {NGO1948} {NGO 1948}
    {NTF France} {NTF (Nouvelle Triangulation de France)}
    {Potsdam Rauenberg DHDN} Potsdam
    {Prov So Amrican 1956} {Prov So Amrican `56}
    {Prov So Chilean 1963} {Prov So Chilean `63}
    {Pulkovo 1942 (1)} {Pulkovo 1942}
    {Pulkovo 1942 (2)} {Pulkovo 1942}
    Rijksdriehoeksmeting {Rijks Driehoeksmeting}
    S42 {S-42 (Pulkovo 1942); Hungary}
    {South American 1969} {South American `69}
    {Wake-Eniwetok 1960} {Wake-Eniwetok `60}
}

proc ImportOziMap {filename} {
    # read pixel and geodetic coordinates of points in an OziExplorer
    #  .map file
    # try files with base name $filename and extension .map or .MAP and
    #  if they cannot be opened for reading, ask the user
    # projected points cannot be dealt with because there is no way to
    #  deal with the projection
    # return 0 on failure, or pair with list of latd,longd,datum and
    #  list of pairs with pixel coordinates (in Tcl sign convention)
    #  aligned with the previous one
    global OziMapFAs OziDatum File TXT MESS Datum

    if { [set f [OpenFileWithExtension $filename MapBkInfo "map MAP"]] == \
	     ".." } {
	return 0
    }
    set filename [file tail $File(MapBkInfo)]
    set getline 1 ; set lno 0 ; set err 0 ; set datum ""
    set state start
    while { ! [eof $f] } {
	set trans $OziMapFAs($state)
	if { $getline } {
	    gets $f line
	    incr lno
	}
	# puts "\#$lno; trans=$trans"
	set getline 1
	switch [lindex $trans 0] {
	    prefix {
		if { [string first [lindex $trans 1] $line] != 0 } {
		    incr err
		    break
		}
	    }
	    datum {
		set field [lindex $trans 1]
		set datum [lindex [split $line ","] $field]
		if { ! [catch {set d $OziDatum($datum)}] } {
		    set datum $d
		} elseif { [DatumRefId $datum] == -1 } {
		    incr err
		    break
		}
	    }
	    info {
		regsub -all {,} $line "\n\t" line
		DisplayInfo $line
	    }
	    loop {
		switch [set sst [lindex $trans 1]] {
		    mmpxy {
			set patt1 $OziMapFAs(mmpxy,numxy)
			set patt2 ""
		    }
		    mmpll {
			set patt1 $OziMapFAs(mmpll,latlong)
			set patt2 ""
		    }
		}
		while 1 {
		    if { ! [regexp $patt1 $line m pn a2 a3] } {
			set getline 0
			break
		    }
		    scan $pn %0d pn
		    set data($pn,$sst) [list $a2 $a3]
		    if { $patt2 != "" } {
			if { [regexp $patt2 $line m b1 b2 b3 b4 b5 b6] } {
			    set data($pn,$sst,2) \
				[list $b1 $b2 $b3 $b4 $b5 $b6]
			}
		    }
		    if { [eof $f] } { break }
		    gets $f line
		    incr lno		    
		}
	    }
	}
	switch [lindex $trans 2] {
	    line {
		set state [lindex $trans 3]
		while { $lno < $state && ! [eof $f] } {
		    gets $f line
		    incr lno		    
		}
		if { $lno < $state } {
		    incr err
		    break
		}
		set getline 0
	    }
	    search {
		set prefix [lindex $trans 3]
		while 1 {
		    if { [string first $prefix $line] == 0 } { break }
		    if { [eof $f] } {
			incr err
			break
		    }
		    gets $f line
		    incr lno
		}
		if { $err } { break }
		set state [lindex $trans 4]
		set getline 0
	    }
	    end {
		set state end
		break
	    }
	}
    }
    close $f
    if { $err } {
	GMMessage "$MESS(badmapinfo): $filename"
	return 0
    }
    if { $datum == "" || [catch {set es [array names data]}] } {
	incr err
    } else {
	set llds "" ; set pixs ""
	set es [lsort -dictionary $es]
	set prev "" ; set ll "" ; set pix ""
	foreach e $es {
	    regexp {^([0-9]+),([^,]+)(.*)$} $e m pn cat cat2
	    if { $pn != $prev } {
		if { $ll != "" && $pix != "" } {
		    lappend ll $datum
		    lappend llds $ll ; lappend pixs $pix
		}
		set prev $pn ; set ll "" ; set pix ""
	    }
	    set d $data($e)
	    switch $cat {
		mmpxy {
		    foreach "x y" $d {}
		    scan $x %0d x ; scan $y %0d y
		    if { $pix == "" } {
			set pix [list $x $y]
		    } elseif { $pix != [list $x $y] } {
			set pix ""
		    }
		}
		mmpll {
		    foreach "long lat" $d {}
		    if { [scan $lat %f lat] && [scan $long %f long] && \
			     [CheckLat Ignore $lat DDD] && \
			     [CheckLong Ignore $long DDD] } {
			if { $ll == "" } {
			    set ll [list $lat $long]
			} elseif { $ll != [list $lat $long] } {
			    set ll ""
			}			
		    }
		}
	    }
	}
	if { $ll != "" && $pix != "" } {
	    lappend ll $datum
	    lappend llds $ll ; lappend pixs $pix
	}
	if { $llds == "" } { incr err }
    }
    if { $err } {
	GMMessage "$MESS(badmapinfo): $filename"
	return 0
    }
    set Datum $datum
    return [list $llds $pixs]
}

### file dates

proc GetFilesDates {files mdate dhour} {
    # find the dates of the given $files
    #  $mdate=="pict": for digital pictures: get the date from an
    #                 EXIF tag (Tcl exif package, or exif or metacam utilities)
    #        =="exif": files contain EXIF tags as produced by exif or metacam,
    #                 the date is taken from a tag
    #        =="fmod": use the file last modification time
    #  $dhour is difference in hours to apply to file dates
    # files whose date could not be retrieved are discarded with an error
    # return list of triples of date in seconds from $YEAR0, file, and
    #  date in "%Y:%m:%d %H:%M:%S" format, sorted by seconds
    global YEAR0 MESS

    set sfds "" ; set dates ""
    set dsecs [expr 3600*$dhour]
    switch $mdate {
	pict {
	    # check availability of the exif package and the exif utility
	    set exiflib [expr ! [catch {package require exif}]]
	    set exif [expr ! [catch {set x [exec exif]}]]
	    # check whether the metacam utility is available
	    # a catch on metacam will always yield true...
	    if { [catch {exec which which}] } {
		if { [catch {exec whereis -b whereis}] } {
		    # maybe metacam is available after all...
		    set metacam 1
		} else {
		    set metacam [regexp {:.+$} [exec whereis -b metacam]]
		}
	    } else {
		set metacam [expr ! [catch {exec which metacam}]]
	    }
	    foreach f $files {
		set date ""
		# the exif package gives an error when finding unknown tags
		if { $exiflib && \
			 ! [catch {set avs [exif::analyzeFile $f]}] && \
			 $avs != "" } {
		    catch {unset d}
		    array set d $avs
		    set date $d(DateTime)
		}
		if { $date == "" && $exif && [set et [exec exif $f]] != "" } {
		    foreach ln [split $et "\n"] {
			if { [string first "Date and Time" $ln] == 0 } {
			    set ln [string trimright $ln]
			    # this may fail but is checked below
			    regexp {\|(.+)$} $ln x date
			    break
			}
		    }
		}
		if { $date == "" && $metacam && \
			 ! [catch {set et [exec metacam $f]}] } {
		    foreach ln [split $et "\n"] {
			set ln [string trim $ln]
			if { [string first "Image Creation" $ln] == 0 } {
			    set ln [string trimright $ln]
			    regexp {: (.+)$} $ln x date
			    break  
			}
		    }
		}
		set date [string trim $date]
		if { [lsearch -exact $dates $date] != -1 || \
		   [scan $date "%d:%0d:%0d %0d:%0d:%0d" y m d h mn s] != 6 || \
			 ! [CheckDateEls $y $m $d $h $mn $s] } {
		    GMMessage "$MESS(badfile): $f"
		    continue
		}
		set secs [DateToSecsFrom $y $m $d $h $mn $s $YEAR0]
		if { $dhour != 0 } {
		    set secs [expr $secs+$dsecs]
		    # assume use of $YEAR0
		    set date [DateFromSecs $secs]
		}
		lappend sfds [list $secs $f $date]
		lappend dates $date
	    }
	}
	exif {
	    # EXIF files may have been produced by exif or metacam
	    foreach f $files {
		if { [catch {set fc [open $f r]}] } {
		    GMMessage "$MESS(badfile): $f"
		    continue
		}
		set date ""
		while { ! [eof $fc] } {
		    gets $fc ln
		    if { [string first "Date and Time" $ln] == 0 } {
			set ln [string trimright $ln]
			if { [regexp {\|(.+)$} $ln x date] } { break }
		    } else {
			set ln [string trim $ln]
			if { [string first "Image Creation" $ln] == 0 } {
			    set ln [string trimright $ln]
			    regexp {: (.+)$} $ln x date
			    break  
			}
		    }
		}
		close $fc
		set date [string trim $date]
		if { [lsearch -exact $dates $date] != -1 || \
		   [scan $date "%d:%0d:%0d %0d:%0d:%0d" y m d h mn s] != 6 || \
			 ! [CheckDateEls $y $m $d $h $mn $s] } {
		    GMMessage "$MESS(badfile): $f"
		    continue
		}
		set secs [DateToSecsFrom $y $m $d $h $mn $s $YEAR0]
		if { $dhour != 0 } {
		    set secs [expr $secs+$dsecs]
		    # assume use of $YEAR0
		    set date [DateFromSecs $secs]
		}
		lappend sfds [list $secs $f $date]
		lappend dates $date
	    }
	}
	fmod {
	    foreach f $files {
		if { ! [catch {set mt [file mtime $f]}] } {
		    set date [clock format $mt -format "%Y:%m:%d %H:%M:%S"]
		    if { [lsearch -exact $dates $date] == -1 } {
			scan $date "%d:%0d:%0d %0d:%0d:%0d" y m d h mn s
			set secs [DateToSecsFrom $y $m $d $h $mn $s $YEAR0]
			if { $dhour != 0 } {
			    set secs [expr $secs+$dsecs]
			    # assume use of $YEAR0
			    set date [DateFromSecs $secs]
			}
			lappend sfds [list $secs $f $date]
			lappend dates $date
		    }
		} else { GMMessage "$MESS(badfile): $f" }
	    }
	}
    }
    return [lsort -index 0 -integer -increasing $sfds]
}

### data files

proc ImportFile {what fmt} {
    # open file in foreign format and load data from it
    #  $fmt such that "in" is in $FILEFORMAT($fmt,mode) except GPSMan
    #  $what either may be "Data" if $FILEFORMAT($fmt,filetype) == "data'
    #        or a list of types in $FILEFORMAT($fmt,types) or the first
    #        element of the pair $FILEFORMAT($fmt,io_types)

    return [ImportFileFrom "" $what $fmt]
}

proc ImportFileFrom {file what fmt} {
    # if $file=="" then ask user to select a file
    # return 1 on error, 0 on success
    global LChannel FILEFORMAT

    InitWPRenaming
    if { $fmt == "Shapefile" } {
	set r [ImportShapefileFrom $file $what]
	EndWPRenaming
	return $r
    }
    if { $file == "" && [OpenImportFileFails $what $fmt] } { return 1 }

    if { $FILEFORMAT($fmt,filetype) == "unique" } {
	Import_$fmt $LChannel($what) normal
    } else { Import_$fmt $LChannel($what) $what normal }
    CloseInputFile $what
    EndWPRenaming
    return 0
}

proc ImportGREls {what fmt} {
    # load data from file in foreign format according to contents of GR(s)
    #  $fmt such that "in" is in $FILEFORMAT($fmt,mode) except GPSMan, and 
    #      $FILEFORMAT($fmt,GREls) is defined
    #  $what in $TYPES identifies menu, not being used
    # possible types used here are those in $FILEFORMAT($fmt,types) or the
    #  first element of the pair $FILEFORMAT($fmt,io_types) except
    #  TR (there is no way to identify TRs)
    global FILEFORMAT

    if { [catch {set types $FILEFORMAT($fmt,types)}] } {
	set types [lindex $FILEFORMAT($fmt,io_types) 0]
    }
    InputToGR $types TR OpenImportFileFails ImportGRElsIn CloseInputFile $fmt
    return
}

proc ImportGRElsIn {types lixs fmt} {
    # import items of $types to replace items with given indices
    #  with -1 meaning new items should be accepted
    #  $lixs is list of lists of indices (and maybe -1) aligned with $types
    #  $fmt: see proc ImportGREls
    # most formats have single-type files and $types and $lixs will
    #  have a single element and a single list, respectively
    global LChannel LFileIxs FILEFORMAT

    set file $LChannel(GR)
    foreach wh $types ixs $lixs {
	set LFileIxs($file,$wh) $ixs
    }
    if { $FILEFORMAT($fmt,filetype) == "unique" } {
	Import_$fmt $file inGR
    } else { Import_$fmt $file $types inGR }
    foreach wh $types {
	catch { unset LFileIxs($file,$wh) }
    }
    return
}

## GPStrans format

proc Import_GPStrans {file what how} {

    if { [ImportGPStransHeader $file] } {
	ImportGPStrans$what $file normal
    }
    return
}

proc ImportGPStransHeader {file} {
    # parse header when reading from file in GPStrans format
    global LFilePFrmt LFileDatum LFileEOF MESS

    set m [ReadFile $file]
    if { $LFileEOF($file) || [string first "Format:" $m]!=0 } {
	GMMessage "$MESS(noformat): $m"
	return 0
    }
    set m [string range $m 8 end]
    set LFilePFrmt($file) [FindPFormat $m]
    if { $LFilePFrmt($file) != "BAD" } {
	set m [string range $m 17 end]
	if { [scan $m "%f" off] == 1 } {
	    # don't know what to do with time offset
	    set LFileDatum($file) [string range $m 24 end]
	    if { [DatumRefId $LFileDatum($file)] == -1 } {
		GMMessage "$MESS(unkndatum): $LFileDatum($file)"
		return 0
	    }
	    return 1
	}
    }
    GMMessage "$MESS(badformat): $m"
    return 0
}

proc ImportGPStransWP {file how} {
    # load WPs from file in GPStrans format
    #  $how in {normal, inGR}: keep all data, keep data on WPs with indices
    #    in $LFileIxs($file,WP)

    LoadWPs $file 1 1 $how
    return
}

proc ImportGPStransRT {file how} {
    # load RTs from file in GPStrans format
    #  $how in {normal, inGR}: keep all data, keep data on RTs with
    #     indices in $LFileIxs($file,RT)
    global LFileLNo LFileEOF MESS

    while { 1 } {
	set m [ReadFile $file]
	if { $LFileEOF($file) } { return }
	if { ! [regexp "R\t.*" $m] } {
	    GMMessage "$MESS(badRT) $LFileLNo($file)"
	    return
	}
	if { [FindArgs RT [string range $m 2 end] $file] == "BADC" } {
	    GMMessage "$MESS(badRTargs) $LFileLNo($file)"
	    return
	}
	if { [BadRTRead $file 1 $how] } { return }
    }
}

proc ImportGPStransTR {file how} {
    # load TRs from file in GPStrans format
    #  $how in {normal, inGR}: keep all data, not used
    global LFileLNo LFileEOF LFileBuffFull MESS

    while { 1 } {
	set m [ReadFile $file]
	if { $LFileEOF($file) } { return }
	if { ! [regexp "T\t.*" $m] } {
	    GMMessage "$MESS(badTR) $LFileLNo($file)"
	    return
	}
	set LFileBuffFull($file) 1
	if { [BadTRRead $file 1 $how] } { return }
    }
}

## Fugawi export format

proc Import_Fugawi {file how} {
    # this is a translation and adaptation of the Perl program "convert" under
    #   copyright by Niki Hammler, http://www.nobaq.net
    #   that converts exported FUGAWI data to DDD GPSman data
    global LFileEOF LFileVisible LFileLNo LFileIxs MESS

    set date [Now]
    set dt "" ; set ixs "" ; set ns "" ; set chgns ""
    while { [set line [ReadFile $file]] != "" && ! $LFileEOF($file) } {
	if { [SlowOpAborted] } { return }
	set vs [split $line ","]
	if { [set l [llength $vs]] < 5 } {
	    GMMessage "$MESS(nofieldsWP): $LFileLNo($file)" ; return
	} elseif { $l > 5 } {
	    GMMessage "$MESS(excfieldsWP): $LFileLNo($file)" ; return
	}
	set name [lindex $vs 0]
	if { ! [CheckName Ignore $name] } {
	    if { [set nname [AskForName $name]] == "" } { continue }
	    set chgdname $name
	    set name $nname
	} else { set chgdname "" }
	set lat [lindex $vs 2] ; set long [lindex $vs 3]
	if { ! [CheckLat GMMessage $lat DDD] || \
		! [CheckLong GMMessage $long DDD] } { continue }
	set latd [Coord DDD $lat S] ; set longd [Coord DDD $long W]
	set posn [list $latd $longd $lat $long]
	set comment [MakeComment [lindex $vs 1]]
	lappend dt [list $name $comment DDD $posn "WGS 84" $date]
	lappend ixs [IndexNamed WP $name]
	lappend ns $name
	lappend chgns $chgdname
    }
    if { $ns == "" } { return }
    switch $how {
	normal {
	    foreach ix $ixs n $ns d $dt chgdname $chgns {
		set fd [FormData WP "Name Commt PFrmt Posn Datum Date" $d]
		if { $chgdname != "" } {
		    set fd [AddOldNameToObs WP $fd $chgdname]
		}
		StoreWP $ix $n $fd $LFileVisible($file)
	    }
	}
	inGR {
	    set grixs $LFileIxs($file,WP)
	    foreach ix $ixs n $ns d $dt {
		if { [lsearch -exact $grixs $ix] != -1 } {
		    set fd [FormData WP "Name Commt PFrmt Posn Datum Date" $d]
		    StoreWP $ix $n $fd $LFileVisible($file)
		}
	    }
	}
    }
    return
}

## file with NMEA 0183 log to be read as a track

proc Import_NMEA {file args} {
    # read file with NMEA 0183 log as a TR
    # this uses procedures defined in files garmin_nmea.tcl and garmin.tcl
    global NMEARLTM MESS LFileOther LFileVisible

    if { $NMEARLTM } {
	GMMessage $MESS(nmeainuse)
	return
    }
    OpenSerialLog
    Log "IN> Reading NMEA from file"
    GarminStartNMEA file $file
    set LFileOther($file) "" ; set errors 0
    while { ! [eof $file] } {
	if { [set line [gets $file]] != "" } {
	    if { [SlowOpAborted] } { break }
	    set buffer "" ; set xor 0 ; set error 0
	    foreach char [split $line ""] {
	        if { [binary scan $char "c" dec] != 1 } {
		    incr error
		    break
		}
		if { [set dec [expr ($dec+0x100)%0x100]] != 13 } {
		    append buffer $char
		    set xor [expr $xor ^ $dec]
		}
	    }
	    if { ! $error } {
		set error [ProcNMEALine $buffer $xor]
	    }
	    incr errors $error
	}
    }
    set NMEARLTM 0
    if { $errors } {
	GMMessage $MESS(badfile)
    }
    if { [set tps $LFileOther($file)] == "" } {
	GMMessage $MESS(voidTR)
    } else {
	set name [NewName TR]
	set fd [FormData TR "Name Datum TPoints" [list $name "WGS 84" $tps]]
	StoreTR -1 $name $fd $LFileVisible($file)
    }
    Log "IN> End of NMEA from file"
    return
}

proc ImportNMEAData {data file} {
    # use data from NMEA sentences to form a TP
    #  $data as described in proc UseRealTimeData (realtime.tcl)
    global LFileOther DateFormat

    if { [llength $data] < 5 } { return }
    foreach "date rpos fix err alt" $data { break }
    if { $rpos == "_" } { return }
    if { $date == "_" } {
	set date [ConvGarminDate 0]
    } else {
	set date [eval LocalTimeAndUTC $date UTC]
	set date [list [eval FormatDate $DateFormat $date] \
		[eval DateToSecs $date]]
    }
    set posn [eval FormatLatLong $rpos DMS]
    if { $alt == "_" } {
	set ns "latd longd latDMS longDMS date secs"
	set alt ""
    } else {
	set ns "latd longd latDMS longDMS date secs alt"
    }
    lappend LFileOther($file) [FormData TP $ns [concat $posn $date $alt]]
    return
}

#### import from file in EasyGPS and GPX formats
####  VR contribution

#### import from XML files in EasyGPS and GPX export format

# XML global variable : context shared by the XML processing procedures

proc XML_Init {} {
    global XML

    # MF change: clear XML array
    catch { unset XML }
    #--
    XML_Init_WP wpt
    foreach i {date lines rte_name trk_name searching_name tmpname tmpdesc} {
	set XML($i) ""
    }
    return	
}

proc XML_Init_WP {tag} {
    global XML DEFAULTSYMBOL

    #- MD change: using also geocache
    foreach i {alt name time cmt long lat time type geocache} {
	set XML($i) ""
    }
    if { $tag != "trkpt" } {
	set XML(desc) ""
    }
    set XML(sym) $DEFAULTSYMBOL
    return
}

# XML_ProcessItem_
#    Processing one XML item
#
#    ii : index of line being processed
#    item : string being processed
#    token : value of XML tag
#    tag : XML tag type
#    return : current index of line

proc XML_ProcessItem_KML {ii item token tag} {
    global XML

    #<Placemark>
    # <name>Col Luisas</name>
    #  <LookAt>
    #   view point informations 
    #  </LookAt>
    #  <styleUrl>#msn_ylw-pushpin01</styleUrl>
    #  <Point>
    #   <coordinates>7.069954449489316,44.7186167968765,0</coordinates>
    #  </Point>
    #</Placemark>
    set XML(end) ""
    # MF contribution: using "--"
    switch -regexp -- $tag {
	^Point  { XML_Init_WP wpt }	
	\/Point { 
		set XML(name) $XML(tmpname)
		set XML(desc) $XML(tmpdesc)
		set XML(end) wpt }
	\/.*       { }
	coordinates { 
		regexp {([-0-9\.]+),([-0-9\.]+),([-0-9\.]+).*} $token x \
		         XML(long) XML(lat) $XML(alt)}
	name 	{ set XML(tmpname) $token}
	description { set XML(tmpdesc) $token}
	.*CDATA { regexp {.*\[(.*)\]\]>} $item x XML(desc)}
	default { } 
    }
    return $ii
}

proc XML_ProcessItem_EasyGPS {ii item token tag} {
    global XML

    set XML(end) ""
    # MF contribution: using "--"
    switch -regexp -- $tag {
	loc 	{ }
	^waypoint  { XML_Init_WP wpt }	
	\/waypoint { set XML(end) wpt }
	\/.*       { }
	coord 	{ regexp {lat="([-0-9\.]+)".*lon="([-0-9\.]+)".*$} $token x \
		         XML(lat) XML(long) }
	name 	{ regexp {id="(.*)">} $token x XML(name) }
	.*CDATA { regexp {.*\[(.*)\]\]>} $item x XML(desc) }
	link 	{ regsub {.*>} $token "" XML(cmt) }
	default { } 
    }
    return $ii
}

# XML_NormalizeSymbol
#    Transforming a symbol name into the GPSMan internal name
#
#    token : current name
#    return : transformed name

array set XML_Symbol_Translation {
	waypoint 	WP_dot
	fishing		fish
	trailhead	trail_head
	white_house	house
	white_anchor	anchor
	telephone	phone
	campground	camping
	hunting 	deer
	waypoint_dot	WP_dot
}

proc XML_NormalizeSymbol {token} {
    # normalizing the symbol's name
    global DEFAULTSYMBOL XML_Symbol_Translation MESS

    regsub -all { } $token "_" symbol
    if { [BadSymbol $symbol ] } {
	set symbol [string tolower $symbol]
	if { [BadSymbol $symbol ] } {
	    regsub {_area} $symbol "" symbol
	    if { [BadSymbol $symbol ] } {	
		if { [catch {set symbol $XML_Symbol_Translation($symbol)}] } {
		    # MF change: using MESS
		    GMMessage "$MESS(badSYMBOLcode): $symbol"
		    set symbol $DEFAULTSYMBOL
		}
	    }
	}
    }
    return $symbol
}

proc XML_ProcessItem_GPX {ii item token tag} {
    global XML DateFormat MESS

    set XML(end) ""
    if { [regexp {.*\/>} $token] } {
	set XML(end) $tag	
    }
    # MF contribution: using "--"
    switch -regexp -- $tag {
	^.xml { }
	gpx - bounds - author - email - url - src - keywords { }
	wissenbach: - topografix: { }
	^wpt$ - ^rtept - ^trkpt { 
	    XML_Init_WP $tag
	    # MF change: checking for errors
	    if { ! [regexp {lat="([-0-9\.]+)".*lon="([-0-9\.eE]+)".*$} \
			$token x XML(lat) XML(long)] } {
		GMMessage $MESS(badfile)
		set XML(end) error
		return 1000
	    }
	}
	\/wpt$ { set XML(end) wpt }
	\/rtept$ { set XML(end) rtept }
	\/trkpt$ { set XML(end) trkpt }
	^rte$ { set XML(searching_name) "rte_name" ; XML_Init_WP wpt }
	\/rte$ { set XML(end) rte }
	^trk$ { set XML(searching_name) "trk_name" ; XML_Init_WP wpt }			\/trk$ { set XML(end) trk }
	^cmt { set XML(cmt) $token }
	^sym { set XML(sym) [ XML_NormalizeSymbol $token ] }
	^name {
	    # MD change: only deal with names outside geocache
	    if { $XML(geocache) == "" } {
		if { $token == "" } {
		    # the name is in a CDATA tag
		    set XML(name) [lindex $XML(lines) [expr $ii + 1]]
		    regexp {.*CDATA\[(.*)\]\]\>} $XML(name) x XML(name)
		    incr ii 1
		} else {
		    # MF change: deleted changes in the name as it will be
		    #  checked/changed in proc XML_process
		    set XML(name) $token
		}
		set XML($XML(searching_name)) $XML(name)
		set XML(searching_name) ""
	    }
	} 
	^time { set XML(time) $token 
		if { $XML(time) == "" } {
		    set XML(time) $XML(date)
		}
                set datesecs [lindex [CheckConvDate $XML(time) ] 1]
	        # MF contrib
	        if { $datesecs == "" } {
		    # undefined time
		    set datesecs 0
		}
	        #-----
		set dints [ DateIntsFromSecs $datesecs ] 
		set dints1 [ eval LocalTimeAndUTC $dints "UTC" ]
		set time1 [eval FormatDate $DateFormat $dints1] 
		set XML(time) $time1
	}
	^desc - ^type {  
	    set XML($tag) $token
	    # set XML($tag) [lindex $XML(lines) [expr $ii + 1]]
	    # GMMessage "DESC - TYPE : $item - $token - $tag - $XML($tag)"
	    # regexp {.*CDATA\[(.*)\]\]\>} $XML($tag) x XML($tag)
	    # incr ii 1
	}
	^ele { set XML(alt) $token}
	^trkseg - ^number - CDATA { }
	\/geocache$ { #- MD change: adding support for geocache
	    set XML(geocache) ""
	}
 	^geocache { #- MD change: adding support for geocache
	    set XML(geocache) "in"
	}
	\/.* { }
	default { 
	    #GMMessage "Unknown element: $item - $token" 
	}
    }
    return $ii
}

# XML_Process
#    Processing an XML file

proc DecodeXMLChars { s } {
   regsub -all {&gt;}   $s {>} s
   regsub -all {&lt;}   $s {<} s
   regsub -all {&quot;} $s {"} s
   regsub -all {&apos;} $s {'} s
   regsub -all {&amp;}  $s {\&} s
   return $s
}

proc XML_Process {fileType file what how date} {
    global MESS LFileVisible LFileIxs
    global XML

    # dt : list of WP descriptions
    set dt {}
    # wpixs : list of WP indexes
    set wpixs {} 
    # wpns : list of WP names
    set wpns {}
    # ltwps : list of lists of RT waypoints
    set ltwps {} 
    # rtns : list of RT names
    set rtns {}
    # rtdr : list of RT descriptions
    set rtdt {}
    # rtixs : list of RT indexes
    set rtixs {}
    # lwps : list of RT waypoints names
    set lwps {}
    # ldt : list of RT waypoints descriptions
    set ldt {}
    # rtld : list of list of RT waypoints descriptions
    set rtld {}
    # trns : list of TR names
    set trns {}
    # trdesc : list of TR descriptions
    set trdesc {}
    # rtld : list of lists of TR points descriptions
    set ltrks {}
    # ltrkpts : list of TR points descriptions
    set ltrkpts {}
    ## MF contribution
    # ltrksgsts : list of TR segment starters
    set ltrksgsts {}
    # trsgsts : list of segment starters in a TR
    set trsgsts {}
    # trcount : counter of points in a TR
    set trcount 0
    ##--
    # lchgdnames : list of changed names
    set lchgdnames {}

    set llines [llength $XML(lines) ]
    for {set ii 1} {$ii<[expr $llines - 1]} {incr ii 1} {
	# MF contribution
	if { [SlowOpAborted] } { break }
	#--
	set item [lindex $XML(lines) $ii]
 	regsub {( |>).*} $item "" tag
	regsub {[^ >]*[ >]} $item "" token
	set cdata ""
	set raw_cdata [lindex $XML(lines) [expr $ii + 1]]
	regexp {.*CDATA\[(.*)\]\]\>} $raw_cdata x cdata
	if { $token == "" && $cdata != "" } { 
	    # GMMessage "NULL TOKEN : $item - $tag - $raw_cdata - $cdata - "
	    set token $cdata
	    set ii [ expr $ii + 1 ]
	}
	set ii [XML_ProcessItem_$fileType $ii $item $token $tag] 
	# MF contribution: segment starters
	if { $tag == "trkseg" && $trcount > 0 && \
		 [lindex $trsgsts end] != $trcount } {
	    lappend trsgsts $trcount
	}
	#--
        switch $XML(end) {
	    wpt - rtept {
		set chgdname ""
		set XML(name) [DecodeXMLChars $XML(name)]
		if { ! [CheckName Ignore $XML(name)] } {
		    if { [set nname [AskForName $XML(name)]] == "" } {
			continue
		    }
		    # GMMessage $nname
		    set chgdname $XML(name)
		    set XML(name) $nname
		}
        	if { ! [CheckLat GMMessage $XML(lat) DDD] || \
			! [CheckLong GMMessage $XML(long) DDD] } { continue }
		set latd [Coord DDD $XML(lat) S]
		set longd [Coord DDD $XML(long) W]
        	set posn [list $latd $longd $XML(lat) $XML(long) ]
		set cmtS [ DecodeXMLChars $XML(cmt) ]
		set descS [ DecodeXMLChars $XML(desc) ]
		set d [list $XML(name) "$descS - $XML(type)" $cmtS \
			   DDD $posn "WGS 84" $XML(time) $XML(sym) $XML(alt)]
		if { $XML(end) == "wpt" } {
		    lappend wpns $XML(name)
		    lappend dt $d
		    lappend wpixs [IndexNamed WP $XML(name) ]
		    lappend lchgdnames $chgdname
	            XML_Init_WP wpt
		} elseif { $XML(end) == "rtept" } { 
		    lappend lwps $XML(name) 
		    lappend ldt $d
		}
	    }
	    trkpt {
		# MF change: using FormatLatLong instead of CreatePos
		set p [FormatLatLong $XML(lat) $XML(long) DMS]
		# MF change: dealing with undefined time stamp
		if { $XML(time) == "" || \
			 [set datesecs [CheckConvDate $XML(time) ]] == "" } {
		    set datesecs [ConvGarminDate 0]
		}
		lappend ltrkpts [FormData TP \
			        "latd longd latDMS longDMS date secs alt" \
				[concat $p $datesecs $XML(alt) ]]
		# MF contribution: segment starters
		incr trcount
		#--
	    }
	    trk {
		# MF change: using CheckString instead of CheckName
		set XML(trk_name) [DecodeXMLChars $XML(trk_name)]
		if { ! [CheckString Ignore $XML(trk_name)] } {
		    set XML(trk_name) [NewName TR]
		}
		# MF change: preventing empty tracks to be added
		if { $ltrkpts != "" } {
		    lappend trns $XML(trk_name)
		    lappend trdesc [DecodeXMLChars $XML(desc)]
		    lappend ltrks $ltrkpts
		    # MF contribution: segment starters
		    lappend ltrksgsts $trsgsts
		    #--
		    set ltrkpts {}
		}
		# MF contribution: segment starters
		set trsgsts {}
		set trcount 0
		#--
	    }
	    rte { 
		# MF change: using CheckString instead of CheckName
		set XML(rte_name) [DecodeXMLChars $XML(rte_name)]
		if { ! [CheckString Ignore $XML(rte_name)] } {
		    set XML(rte_name) [NewName RT]
		}
		set d [list [DecodeXMLChars $XML(desc)] \
			    [DecodeXMLChars $XML(cmt)]]
		# MF change: preventing empty routes to be added
		if { $lwps != "" } {
		    lappend rtns $XML(rte_name) 
		    lappend rtdt $d 
		    lappend ltwps $lwps
		    lappend rtixs [IndexNamed RT $XML(rte_name) ]
		    lappend rtld $ldt
		    set lwps {}
		}
		set ldt {}
	    }
	    default { }
      	} 
	set XML(end) 0
    }

    if { $what == "Data" } {
	set lwhat {"WP" "TR" "RT"}
    } else {
	set lwhat $what 
    }

    ## MF contribution
    if { $wpixs == {} && $what == "WP" } {
	GMMessage $MESS(shpemptyfile)
	return
    }
    if { $ltrks == {} && $what == "TR"  } {
	GMMessage $MESS(shpemptyfile)
	return
    }
    if { $ltwps == {} && $what == "RT" } {
	GMMessage $MESS(shpemptyfile)
	return
    }
    if { $wpixs == {} && $ltrks == {} && $ltwps == {} && $what == "Data" } {
	GMMessage $MESS(shpemptyfile)
	return
    }

    foreach what $lwhat {
	# MF contribution:
	if { [SlowOpAborted] } { break }

	if { $how == "inGR" } {
	    set grixs $LFileIxs($file,$what)
	    # GMMessage "Tracing Process_XML : inGR"
	}
	switch $what {
	    WP {
		foreach ix $wpixs n $wpns d $dt chgdname $lchgdnames {
		    # MF contribution:
		    if { [SlowOpAborted] } { break }

		    if { $what != "inGR" || \
			    [lsearch -exact $grixs $ix] != -1 } {
			set fd [FormData WP "Name Obs Commt PFrmt Posn Datum \
				Date Symbol Alt" $d]
			if { $chgdname != "" } {
			    set fd [AddOldNameToObs WP $fd $chgdname]
			}
			StoreWP $ix $n $fd $LFileVisible($file)
		    }
		}
	    }
	    RT {
		set wpsseen {}
		foreach ix $rtixs id $rtns d $rtdt wps $ltwps ldt $rtld {
		    # MF contribution:
		    if { [SlowOpAborted] } { break }

		    if { $what != "inGR" || \
			    [lsearch -exact $grixs $ix] != -1 } {
			# saving the WPs used in the route
			foreach n $wps d $ldt {
			    # MF contribution:
			    if { [SlowOpAborted] } { break }

			    if { [lsearch $wps $n ] != -1} {
				if { [ lsearch -exact $wpsseen $n ] == -1 } { 
				    set ix [IndexNamed WP $n]
				    set fd [FormData WP \
			  "Name Obs Commt PFrmt Posn Datum Date Symbol Alt" $d]
				    set nom [StoreWP $ix $n $fd \
					        $LFileVisible($file) ]
				    lappend wpsseen $n
				}
			    }
			}
		    }
		    set l [FormData RT "IdNumber Commt Obs WPoints" \
			       [list $id [lindex $d 0] [lindex $d 1] $wps]]
		    StoreRT [IndexNamed RT $id] $id $l $wps \
			    $LFileVisible($file)
		}
	    }
	    TR {
		# includes MF contribution: segment starters
		foreach name $trns tps $ltrks desc $trdesc trsgsts $ltrksgsts {
		    # MF contribution:
		    if { [SlowOpAborted] } { break }
		    #-
		    set fd [FormData TR "Name Obs Datum TPoints SegStarts" \
				[list $name $desc "WGS 84" $tps $trsgsts]]
		    StoreTR -1 $name $fd $LFileVisible($file)
		}
	    }
	}
    }
    return
}

proc XML_ImportFile {fileType file what how} {
    global XML SYSENC File

    XML_Init
    set XML(date) [Now]

    # pre-processing the file 

    # MF change: new way of reading the file for dealing with encoding
    #  - not using proc ReadFile as the file may need to be reopened,
    #    what is done here with no need for changing $file
    #  - it is still safe to use the globals LFile*($file) after this
    # try to find a XML declaration with an encoding tag
    foreach "lines encoding" [XMLDeclEncoding $file] {}
    if { $lines == "" } { return }
    if { $encoding == "" } { set encoding UTF-8 }
    if { [set encoding [TclEncodingName $encoding]] == "" } { return }
    if { $encoding != $SYSENC } {
	# must reopen file and configure the encoding
	set all_lines ""
	set efile [open $File($what) r]
	fconfigure $efile -encoding $encoding
	set reop 1
    } else {
	set efile $file
	set all_lines $lines
	set reop 0
    }
    # read all
    while { ! [eof $efile] } {
	gets $efile line
	if { [set line [string trim $line " \t"]] != "" } {
	    append all_lines $line
	}
    }
    if { $reop } { close $efile }
    #-----
    
    # one list item for each XML tag
    set XML(lines) [split $all_lines "<"]
    # MF contribution:
    if { [SlowOpAborted] } { return }

    XML_Process $fileType $file $what $how $XML(date) 
    return
}

proc Import_GPX {file what how} {
    XML_ImportFile GPX $file $what $how
    return
}

proc Import_KML {file what how} {
    XML_ImportFile KML $file $what $how
    return
}

proc Import_EasyGPS {file how} {
    XML_ImportFile EasyGPS $file WP $how
    return
}

### end of VR contribution

proc XMLDeclEncoding {file} {
    # try to find the XML declaration and an encoding tag in it
    # return a pair with the concatenation of non-blank lines read in
    #  and the encoding name (empty if not found)

    set lines "" ; set state start
    while 1 {
	if { [eof $file] } { return [list $lines] }
	gets $file line
	if { [set line [string trim $line " \t"]] != "" } {
	    append lines $line
	    while 1 {
		switch $state {
		    start {
			if { [regexp {<\?xml} $line] } {
			    set state enc
			    continue
			}
			if { [string first "<" $line] != -1 } {
			    return [list $lines]
			}
			break
		    }
		    enc {
			if { [regexp \
				  {encoding[\t ]*=[\t ]*"([-_a-zA-Z0-9]+)"} \
				  $line x enc] } {
			    return [list $lines $enc]
			}
			if { [regexp {encoding[\t ]*=[\t ]$} $line] } {
			    set state encname
			    break
			}
			if { [regexp {\?>} $line] } {
			    return [list $lines]
			}
			break
		    }
		    encname {
			if { [regexp {^"([-_a-zA-Z0-9]+)"} $line x enc] } {
			    return [list $lines $enc]
			}
			return [list $lines]
		    }
		}

	    }
	}
    }
    # not used
    return
}


#### Map&Guide export format

proc Import_MapGuide {file args} {
    global GFItemId GFItemCommt GFItemNB GFVersion

    ImportMapGuide $file $GFItemId $GFItemCommt $GFItemNB $GFVersion
    return
}

proc ImportMapGuide {file rtid rtcommt rtrmrk version} {
    # this is an adaptation of the script "mg2gpsman.tcl"
    #   under copyright by Heiko Thede (Heiko.Thede@gmx.de)
    #   that converts exported Map&Guide data to GPSman data
    # each file corresponds to one RT that will be split in more than one
    #  if its length exceeds $MAXWPINROUTE
    #  $rtid is the RT identifier/number given by user; it will be
    #   replaced by an automatically generated one if empty
    #  $rtcommt and $rtrmrk are the comment and remark given by the user
    #  $version is in {03/04, 2002}
    global MAXWPINROUTE TXT MESS PositionFormat LFileVisible

    # get rid of leading/trailing blanks
    foreach v "rtid rtcommt rtrmrk" {
	set $v [string trim [set $v]]
    }
    # generate RT id if none given
    if { $rtid == "" } {
	set rtid [NewName RT] ; set rtix -1
    } else {
	set rtix [IndexNamed RT $rtid]
    }
    set rts "" ; set wps ""

    # WPs: prefix for names, position type
    #  the first $rtid is used for all WPs even if the RT is split
    set prefix "$TXT(RT)$rtid"
    set nwps 0
    # positions of WPs, to avoid creating different WPs in the same place
    set coords "" ; set coordWPs ""
    # read lines
    while { ! [eof $file] } {
	gets $file line
	#select only relevant lines
	if { $line != "" } {
	    if { [SlowOpAborted] } { return }
	    set coordstart [string last "(" $line]
	    set coordend [string last ")" $line]
	    set coord [string range $line $coordstart $coordend]
	    if { [set i [lsearch -exact $coords $coord]] != -1 } {
		# use WP in this position
		set wpt [lindex $coordWPs $i]
	    } else {
		# create new WP and remember its position and name
		# generate name using $prefix if possible
		set fields [split $line "\t"]
		if { $version == 2002 } {
		    if { ! [regexp {\((-?)([0-9]+),(-?)([0-9]+)\)} $coord x \
				slong long slat lat] || \
			     [scan $long %2d%2d%d longd longm longs] != 3 || \
			     [scan $lat %2d%2d%d latd latm lats] != 3 } {
			# bad line: ignore WP
			continue
		    }
		    if { [string length $long] == 7 } {
			set longs [expr 0.1*$longs]
			set lats [expr 0.1*$lats]
			set wprmrk "[lindex $fields 0] [lrange $fields 2 3]"
		    } else {
			set wprmrk "[lindex $fields 0] [lrange $fields 2 4]"
		    }
		} elseif { ! [regexp {\((-?)([0-9]+),(-?)([0-9]+)\)} $coord x \
				  slong long slat lat] || \
			   ! ( [set x [string length $long]] == 7 && \
			     [scan $long %2d%2d%3d longd longm longs] == 3 || \
			     $x == 6 && \
			   [scan $long %1d%2d%3d longd longm longs] == 3 ) || \
		           ! ( [set x [string length $lat]] == 7 && \
			     [scan $lat %2d%2d%3d latd latm lats] == 3 || \
			     $x == 6 && \
			   [scan $lat %1d%2d%3d latd latm lats] == 3 ) } {
		    # bad line: ignore WP
		    continue
		} else {
		    set longs [expr 0.1*$longs]
		    set lats [expr 0.1*$lats]
		    set wprmrk "[lindex $fields 0] [lrange $fields 2 3]"
		}
		set lat [expr $latd+($latm+$lats/60.0)/60.0]
		if { $slat == "-" } {
		    set lat [expr -$lat]
		}
		set long [expr $longd+($longm+$longs/60.0)/60.0]
		if { $slong == "-" } {
		    set long [expr -$long]
		}
		foreach "posn pfmt datum" \
		    [FormatPosition $lat $long "WGS 84" \
			 $PositionFormat "" DDD] { break }
		set wpt [NewName WP $prefix]
		lappend coords $coord ; lappend coordWPs $wpt
		set data [FormData WP "Name Obs PFrmt Posn Datum" \
			[list $wpt $wprmrk $pfmt $posn $datum]]
		# displaying will be done along with RT if needs be
		StoreWP -1 $wpt $data 0
	    }
	    if { $nwps == $MAXWPINROUTE } {
		# save previous route and start new one
		lappend rts $rtid $wps
		# cannot use proc NewName here as previous RTs were not
		#  stored yet
		set rtid ""
		set nwps 0 ; set wps ""
	    }
	    lappend wps $wpt
	    incr nwps
	}
    }
    if { $wps == "" && $rts == "" } {
	GMMessage $MESS(voidRT)
	return
    }
    if { $nwps > 0 } {
	# save last route
	lappend rts $rtid $wps
    }
    # prepare comment and remark fields if any
    if { $rtcommt != "" && [CheckComment Ignore $rtcommt] } {
	set fields Commt
	set fvals [list $rtcommt]
	set rmrkix 1
    } else {
	set fields "" ; set fvals ""
	set rmrkix 0
    }
    if { $rtrmrk != "" } {
	lappend fields Obs
	lappend fvals $rtrmrk
	# prepare for adding new info to remark
	set norem 0
	set rtrmrk "${rtrmrk}\n"
    } else { set norem 1 }
    lappend fields IdNumber WPoints
    # store all routes
    foreach "rtid wps" $rts {
	if { [SlowOpAborted] } { return }
	if { $rtid == "" } {
	    set rtid [NewName RT] ; set rtix -1
	}
	set fd [FormData RT $fields [linsert $fvals end $rtid $wps]]
	StoreRT $rtix $rtid $fd $wps $LFileVisible($file)

	# add "Insert after $rtid" to remark for use in next route
	set rmk "${rtrmrk}$TXT(insa): $rtid"
	if { $norem } {
	    # add remark field
	    set fields [linsert $fields $rmrkix Obs]
	    set fvals [linsert $fvals $rmrkix $rmk]
	    set norem 0
	} else {
	    # replace remark
	    set fvals [lreplace $fvals $rmrkix $rmrkix $rmk]
	}
    }
    return
}

## OziExplorer WP file format
# from the description in the implementation of exportation of the same
#  format by Alessandro Palmas

proc DateFromDelphiTime {dtime} {
    # build date from a Delphi time representation (days since 1899-12-30 0:0:0
    #  as a float)
    # return empty on error (no message given)
    global YEAR0

    # number of days from 1899-12-30 0:0:0 to 1988-01-01 0:0:0 = 32143
    if { [catch {set s88 [expr round(($dtime-32143)*86400)]}] || $s88 < 0 } {
	return ""
    }
    set y $YEAR0 ; set YEAR0 1988
    set d [DateFromSecs $s88]
    set YEAR0 $y
    return $d
}

proc Import_Ozi {file what how} {
    # import data from $file in OziExplorer format
    #  $how in {normal, inGR}
    #  $what == WP
    # support is only for WP files and only the following fields are kept
    # Field  2: Name (short)
    # Field  3: lat DDD
    # Field  4: long DDD
    # Field  5: date, in Delphi format
    # Field 11: Comment
    # Field 15: Altitude, in feet. -777 is non valid
    global LFileVisible LFileLNo LFileIxs ALSCALEFOR MESS

    # header
    #   OziExplorer Waypoint File Version 1.1
    #   DATUM
    #   Reserved*
    #   anything not starting by number
    if { ! [regexp {^OziExplorer Waypoint File} [ReadFile $file]] } {
	GMMessage $MESS(noheader)
	return
    }
    set datum [ReadFile $file]
    if { ! [catch {set d $OziDatum($datum)}] } {
	set datum $d
    } elseif { [DatumRefId $datum] == -1 } {
	GMMessage "$MESS(unkndatum): $datum"
	return
    }
    while { [set line [ReadFile $file]] != "" } {
	if { [regexp {^ *[0-9]+,} $line] } { break }
    }
    set dt "" ; set ixs "" ; set ns "" ; set chgns ""
    while 1 {
	if { [SlowOpAborted] } { return }
	if { [llength [set line [split $line ","]]] < 15 } {
	    GMMessage "$MESS(nofieldsWP): $LFileLNo($file)" ; return
	}
	set name [lindex $line 1]
	if { ! [CheckName Ignore $name] } {
	    if { [set nname [AskForName $name]] == "" } { continue }
	    set chgdname $name
	    set name $nname
	} else { set chgdname "" }
	set lat [lindex $line 2] ; set long [lindex $line 3]
	if { ! [CheckLat GMMessage $lat DDD] || \
		! [CheckLong GMMessage $long DDD] } { continue }
	set latd [Coord DDD $lat S] ; set longd [Coord DDD $long W]
	set posn [list $latd $longd $lat $long]
	set alt [lindex $line 14]
	if { $alt == -777 || \
		 [catch {set alt [expr $alt*$ALSCALEFOR(FT)]}] || \
		 [set alt [AltitudeList $alt]] == "nil" } {
	    set alt ""
	}
	set comment [MakeComment [lindex $line 10]]
	set date [DateFromDelphiTime [lindex $line 4]]
	lappend dt [list $name $comment DDD $posn $datum $alt $date]
	lappend ixs [IndexNamed WP $name]
	lappend ns $name
	lappend chgns $chgdname
	if { [set line [ReadFile $file]] == "" } { break }
    }
    if { $ns == "" } { return }
    switch $how {
	normal {
	    foreach ix $ixs n $ns d $dt chgdname $chgns {
		set fd [FormData WP "Name Commt PFrmt Posn Datum Alt Date" $d]
		if { $chgdname != "" } {
		    set fd [AddOldNameToObs WP $fd $chgdname]
		}
		StoreWP $ix $n $fd $LFileVisible($file)
	    }
	}
	inGR {
	    set grixs $LFileIxs($file,WP)
	    foreach ix $ixs n $ns d $dt {
		if { [lsearch -exact $grixs $ix] != -1 } {
		    set fd [FormData WP \
				"Name Commt PFrmt Posn Datum Alt Date" $d]
		    StoreWP $ix $n $fd $LFileVisible($file)
		}
	    }
	}
    }
    return
}

## gd2 format

proc Import_GD2 {file what how} {
    # import data from $file in gd2 format
    #  $how in {normal, inGR}
    #  $what in {WP, RT, TR}, but not TR if $how==inGR
    # gd2.c is a program by Randolph Bentson (bentson@grieg.seaslug.org)
    #  distributed under GPL
    global LFileEOF LFileVisible LFileBuffFull LFileLNo LFileIxs MESS

    set date [Now]
    set dt "" ; set ixs "" ; set ns "" ; set error 0
    switch $what {
	WP { set ns "" ; set chgns "" }
	RT {
	    set rtid "" ; set dtwps "" ; set lwps "" ; set lchgwps ""
	    set ldwps "" ; set rtwps start
	}
	TR { set tps "" }
    }
    while { [set line [ReadFile $file]] != "" && ! $LFileEOF($file) } {
	if { [SlowOpAborted] } { return }
	switch $what {
	    WP {
		if { ! [regexp \
		{^WPT  ? (......) ([-0-9\.]+) ([-0-9\.]+) ([-0-9/:]+) (.*)$} \
			$line x name lat long date commt] || \
		    [set dwp [ImportGD2WP $name $lat $long $date $commt]] == \
		    -1 } {
		    set error 1
		} else {
		    foreach "name chgdn d ix" $dwp {}
		    lappend dt $d ; lappend ixs $ix ; lappend ns $name
		    lappend chgns $chgdn
		}
	    }
	    RT {
		if { $rtwps == "" || \
			! [regexp {^RTE ([0-9]+) (.*)$} line rtid rtcommt] } {
		    set error 1
		} else {
		    set rtcommt [MakeComment [string trim $rtcommt]]
		    set what RTWP
		    set rtwps "" ; set dtwps "" ; set rtchgwps ""
		}
	    }
	    RTWP {
		if { ! [regexp \
		       {^ (......) ([-0-9\.]+) ([-0-9\.]+) ([-0-9:/]+) (.*)$} \
			$line x name latd longd date commt] } {
		    set what RT ; set LFileBuffFull($file) 1
		    lappend dt [list $rtid $rtcommt $rtwps]
		    lappend ixs [IndexNamed RT $rtid]
		    lappend ns $rtid
		    lappend lwps $rtwps ; lappend lchgwps $rtchgwps
		    lappend ldwps $dtwps
		    set rtid ""
		} elseif { [set dwp \
			[ImportGD2WP $name $lat $long $date $commt]] == -1 } {
		    set error 1
		} else {
		    foreach "name chgdn d ix" $dwp {}
		    lappend dtwps $d ; lappend rtwps $name
		    lappend rtchgwps $chgdn
		}
	    }
	    TR {
		if { [regexp \
    {^TRK ((S|N)[0-9]+ [0-9\.]+) ((W|E)[0-9]+ [0-9\.]+) ([-0-9:/]+) (0|1)$} \
			$line x latdmm x longdmm x date new] } {
		    set lat [Coord DMM $latdmm S]
		    set long [Coord DMM $longdmm W]
		} elseif { ! [regexp \
			{^TRK ([-0-9\.]+) ([-0-9\.]+) ([-0-9:/]+) (0|1)$} \
			lat long date new] } {
		    set error 1
		}
		if { ! $error } {
		    if { ! [CheckLat GMMessage $lat DDD] || \
			    ! [CheckLong GMMessage $long DDD] || \
			    [set datesecs [CheckConvDate $date]] == "" } {
			set error 1
		    } else {
			if { $new && $tps != "" } {
			    lappend dt $tps
			    set tps ""
			}
			set p [FormatLatLong $lat $long DMS]
			lappend tps [FormData TP \
				       "latd longd latDMS longDMS date secs" \
				       [concat $p $datesecs]]
		    }
		}
	    }
	}
	if { $error } {
	    GMMessage "$MESS(loaderr) $LFileLNo($file)"
	    return
	}
    }
    # terminate pending RT or TR if any
    switch $what {
	RT {
	    if { $rtid != "" } {
		if { $rtwps == "" } {
		    GMMessage "$MESS(loaderr) $LFileLNo($file)"
		    return
		}
		lappend dt [list $rtid $rtcommt $rtwps]
		lappend ixs [IndexNamed RT $rtid]
		lappend ns $rtid
		lappend lwps $rtwps ; lappend lchgwps $rtchgwps
		lappend ldwps $dtwps
	    }
	}
	TR {
	    if { $tps != "" } {
		lappend dt $tps
	    }
	}
    }
    switch $how {
	normal {
	    switch $what {
		WP {
		    foreach ix $ixs n $ns d $dt chgdname $chgns {
			if { [SlowOpAborted] } { return }
			set fd [FormData WP \
				"Name Commt PFrmt Posn Datum Date" $d]
			if { $chgdname != "" } {
			    set fd [AddOldNameToObs WP $fd $chgdname]
			}
			StoreWP $ix $n $fd $LFileVisible($file)
		    }
		}
		RT {
		    set wpsseen "" ; set wpsseenn ""
		    foreach ix $ixs id $ns d $dt wps $ltwps dwps $ldwps \
			    chgns $lchgwps {
			set wpsn ""
			foreach nwp $wps dwp $dwps chgdname $chgns {
			    if { [SlowOpAborted] } { return }
			    if { [set k [lsearch -exact $wpsseen $nwp]] \
				    == -1 } {
				set ix [IndexNamed WP $nwp]
				set fd [FormData WP \
				       "Name Commt PFrmt Posn Datum Date" $dwp]
				if { $chgdname != "" } {
				    set fd [AddOldNameToObs RT $fd $chgdname]
				}
				set nnwp [StoreWP $ix $nwp $fd 0]
				lappend wpsseen $nwp
				lappend wpsseenn $nnwp
				lappend wpsn $nnwp
			    } else {
				lappend wpsn [lindex $wpsseenn $k]
			    }
			}
			StoreRT $ix $id $d $wpsn $LFileVisible($file)
		    }
		}
		TR {
		    foreach tps $dt {
			if { [SlowOpAborted] } { return }
			set name [NewName TR]
			set fd [FormData TR "Name Datum TPoints" \
				[list $name "WGS 84" $tps]]
			StoreTR -1 $name $fd $LFileVisible($file)
		    }
		}
	    }
	}
	inGR {
	    set grixs $LFileIxs($file,$what)
	    switch $what {
		WP {
		    foreach ix $ixs n $ns d $dt chgdname $chgns {
			if { [SlowOpAborted] } { return }
			if { [lsearch -exact $grixs $ix] != -1 } {
			    set fd [FormData WP \
				    "Name Commt PFrmt Posn Datum Date" $d]
			    if { $chgdname != "" } {
				set fd [AddOldNameToObs WP $fd $chgdname]
			    }
			    StoreWP $ix $n $fd $LFileVisible($file)
			}
		    }
		}
		RT {
		    set wpsseen "" ; set wpsseenn ""
		    foreach ix $ixs id $ns d $dt wps $ltwps dwps $ldwps \
			    chgns $lchgwps {
			if { [lsearch -exact $grixs $ix] != -1 } {
			    set wpsn ""
			    foreach nwp $wps dwp $dwps chgdname $chgns {
				if { [SlowOpAborted] } { return }
				if { [set k [lsearch -exact $wpsseen $nwp] \
					== -1 } {
				    set ix [IndexNamed WP $nwp]
				    set fd [FormData WP \
					   "Name Commt PFrmt Posn Datum Date" \
					   $dwp]
				    if { $chgdname != "" } {
					set fd \
					    [AddOldNameToObs RT $fd $chgdname]
				    }
				    set nnwp [StoreWP $ix $nwp $fd 0]
				    lappend wpsseen $nwp
				    lappend wpsseenn $nnwp
				    lappend wpsn $nnwp
				} else {
				    lappend wpsn [lindex $wpsseenn $k]
				}
			    }
			    StoreRT $ix $id $d $wpsn $LFileVisible($file)
			}
		    }
		}
	    }
	}
    }
    return
}

proc ImportGD2WP {name lat long date commt} {
    # prepare WP data when importing from file in gd2 format
    # return 0 on error, otherwise list with name, old name (or ""),
    #  list with WP data (see below) and index

    set name [string trim $name]
    if { ! [CheckLat GMMessage $lat DDD] || \
	    ! [CheckLong GMMessage $long DDD] } { return -1 }
    if { ! [CheckName Ignore $name] } {
	if { [set nname [AskForName $name]] == "" } { return -1 }
	set chgdname $name
	set name $nname
    } else { set chgdname "" }
    set posn [FormatLatLong $lat $long DDD]
    set commt [MakeComment [string trim $commt]]
    return [list $name $chgdname [list $name $commt DDD $posn "WGS 84" $date] \
	    [IndexNamed WP $name]]
}

## FAI IGC data file format

    # number 999 for undefined taken to be "WGS 84"
array set IGCDatum {
    0   0       1   1       2   2       3   3       4   4       5   5
    6   6       7   8       8   11      9   12      10  9       11  10
    12  7       13  13      14  14      15  15      16  16      17  17
    18  19      19  18      20  20      21  22      22  23      23  24
    24  25      25  26      26  27      27  28      28  29      29  30
    30  31      31  32      32  75      33  33      34  34      35  35
    36  36      37  37      38  38      39  39      40  40      41  41
    42  42      43  43      44  44      45  45      46  46      47  47
    48  48      49  49      50  50      51  51      52  52      53  53
    54  54      55  55      56  56      57  60      58  58      59  57
    60  59      61  61      62  62      63  63      64  64      65  65
    66  66      67  70      68  67      69  68      70  69      71  71
    72  72      73  73      74  74      75  76      76  77      77  80
    78  81      80  83      81  84      82  85      83  78      85  91
    86  79      87  86      88  87      89  88      90  89      91  92
    92  93      93  94      94  95      95  96      97  97      98  98
    99  99      100 100     101 101     102 21      103 gm145  999 100
}

array set IGCTrans {
    A   A
    B   {I J recdata}
    C   {I J recdata}
    D   {I J recdata}
    E   {I J recdata}
    F   {I J recdata}
    G   recdata
    H   {A H}
    I   H
    J   I
    K   {I J recdata}
    L   {I J recdata}
}

array set IGCRE {
B {^B([0-9]{6})([0-9]{7})([NS])([0-9]{8})([EW])([AV])([-0-9]{5})([-0-9]{5})(.*)$}
    E   {^E([0-9]{6})([A-Z0-9]{3})(.*)$}
    H   {^H[FOP]([A-Z0-9]{3})([^:]*)(.*)$}
}

proc Import_IGC {file args} {
    # import data from $file in FAI IGC format and return one TR
    global IGCTrans IGCRE IGCDatum TimeOffset DateFormat LFileEOF \
	LFileVisible LFileLNo MESS YEAR0 GFOption File TXT

    set name [file rootname [file tail $File(TR)]]
    if { $GFOption == "gps" } {
	set baroalt 0
	set rmrk ""
    } else {
	set baroalt 1
	set rmrk "$TXT(alt): baro"
    }
    set toffsetold $TimeOffset
    set TimeOffset 0
    set ymd [list $YEAR0 1 day 1]
    set datum "WGS 84"
    set oldpos "" ; set oldkeep 1
    set tps "" ; set sgsts "" ; set ntp 0
    set error 0
    set state A
    while { [set line [ReadFile $file]] != "" && ! $LFileEOF($file) } {
	if { [SlowOpAborted] } { return }
	set rtype [string index $line 0]
	if { [catch {set sts $IGCTrans($rtype)}] || \
		 [lsearch -exact $sts $state] == -1 } {
	    incr error ; break
	}
	switch $rtype {
	    A -  L {
	    }
	    H {
		set state H
		if { ! [regexp $IGCRE(H) $line x stype s1 s2] } {
		    incr error ; break
		}
		switch -- $stype {
		    DTE {
			# date  DDMMYY
			# proc ScanDate could be used here...
			#  YY taken as 2000+YY if YY<70, and 1900+YY otherwise
			if { $s2 != "" || \
			       [scan $s1 %02d%02d%02d d m y] != 3 } {
			    incr error ; break
			}
			if { $y < 70 } {
			    incr y 2000
			} else { incr y 1900 }
			set ymd [list $y $m $d]
			set prevtime -1
		    }
		    TZN {
			# time zone offset in hours from UTC
			if { $s2 == "" } {
			    set s $s1
			} else { set s [string range $s2 1 end] }
			if { [scan $s %0d t] != 1 } {
			    incr error ; break
			}
			set TimeOffset $t
		    }
		    DTM {
			# datum
			if { [scan $s1 %03d n] != 1 || \
				 [catch {set gmno $IGCDatum($n)}] } {
			    incr error ; break
			}
			set datum [DatumWithRefId $gmno]
		    }
		}
	    }
	    I {
		set state I
	    }
	    J {
		set state J
	    }
	    C -  D {
		set state recdata
	    }
	    F {
		set state recdata
	    }
	    B {
		set state recdata
		if { ! [regexp $IGCRE(B) $line x time lat hlat long hlong \
			    tfix palt alt exts] } {
		    incr error ; break
		}
		# time stamp
		scan $time %02d%02d%02d hh mm ss
		if { $prevtime > $time } {
		    set ymd [eval NextDay $ymd]		    
		}
		set prevtime $time
		set date [eval LocalTimeAndUTC $ymd $hh $mm $ss UTC]
		set date [list [eval FormatDate $DateFormat $date] \
			      [eval DateToSecs $date]]
		# position
		scan $lat %02d%0d latd m
		set latd [expr $latd+$m/60000.0]
		if { $hlat == "S" } { set latd [expr -$latd] }
		scan $long %03d%0d longd m
		set longd [expr $longd+$m/60000.0]
		if { $hlong == "W" } { set longd [expr -$longd] }
		# with bad quality fixes test whether position is
		#  acceptable: not 0 0 as first value, and no
		#  differences in lat and in long greater than 5 degrees
		set keep 1
		if { $tfix == "V" } {
		    if { $oldpos == "" } {
			if { $latd == 0 && $longd == 0 } { set keep 0 }
		    } elseif { abs([lindex $oldpos 0]-$latd) > 5 || \
				   abs([lindex $oldpos 1]-$longd) > 5 } {
			set keep 0
		    }
		}
		if { $keep } {
		    if { $oldkeep == 0 && $ntp != 0 } {
			lappend sgsts $ntp
		    }
		    set oldpos [list $latd $longd]
		    set posn [FormatLatLong $latd $longd DMS]
		    # altitude
		    if { $baroalt } {
			set alt $palt ; set tfix "A"
		    }
		    scan $alt %0d alt
		    if { $tfix == "V" || $alt == 0 } {
			set ns "latd longd latDMS longDMS date secs"
			set alt ""
		    } else {
			set ns "latd longd latDMS longDMS date secs alt"
		    }
		    lappend tps [FormData TP $ns [concat $posn $date $alt]]
		    incr ntp
		}
		set oldkeep $keep
	    }
	    E {
		set state recdata
		if { ! [regexp $IGCRE(E) $line x time stype s] } {
		    incr error ; break
		}
		switch -- $stype {
		    CGD {
			# change datum
			if { [scan $s %03d n] != 1 || \
				 [catch {set gmno $IGCDatum($n)}] } {
			    incr error ; break
			}
			set datum [DatumWithRefId $gmno]
		    }
		}
	    }
	    K {
		set state recdata
	    }
	    G {
		break
	    }
	}
    }
    set TimeOffset $toffsetold
    if { $error } {
	GMMessage "$MESS(loaderr) $LFileLNo($file)"
	return
    }
    if { $tps == "" } {
	GMMessage $MESS(voidTR)
	return
    }
    if { [IndexNamed TR $name] != -1 } {
	set rmrk [AddToNB $rmrk "$TXT(file): $name"]
	set name [NewName TR]
    }
    set fd [FormData TR "Name Obs Datum TPoints SegStarts" \
		[list $name $rmrk $datum $tps $sgsts]]
    StoreTR -1 $name $fd $LFileVisible($file)
    return
}

## Kismet .network files with location information
#   http://www.kismetwireless.net

proc Import_Kismet {file how} {
    # import WPs from Kismet .network files with location information
    #  $how in {normal, inGR}: keep all data, not used
    # some options can be changed by editing the file config.tcl and
    #  redefining the KISMETOPT array elements
    # there is a single parameter for controlling the creation of a group
    #  with the imported WPs for each type of network
    global KISMETOPT GFOption PositionFormat LFileVisible MESS TXT

    set crgroups $GFOption
    foreach v "prename namenumber types defsymbol esymbols" {
	set $v $KISMETOPT($v)
    }
    set netno 0
    while { ! [eof $file] } {
	if { [SlowOpAborted] } { return }
	gets $file line ; set line [string trim $line]
	if { $line == "" || ! [regexp {^Network} $line] } { continue }
	# network
	incr netno
	set obs "" ; set name ""
	if { ! [regexp {: "(.+)" B?SSID} $line x name] || \
		 ! [CheckName Ignore $name] || [IndexNamed WP $name] != -1 } {
	    if { $name != "" } { set obs "ssid = $name" }
	    set k 0
	    while { $k < 100 } {
		set name "$prename$namenumber"
		incr namenumber
		if { [CheckName Ignore $name] && \
			 [IndexNamed WP $name] == -1 } {
		    break
		}
		incr k
	    }
	    if { $k >= 100 } { set name [NewName WP] }
	}
	if { [set line [NextLine 1 $file]] == "" } { return }
	# type?
	if { ! [regexp {^Type.*: ([-a-zA-Z0-9]+)$} $line x type] } {
	    # "Bad Type line after $netno Network"
	    GMMessage $MESS(badfile)
	    continue
	}
	if { [lsearch -exact $types $type] == -1 } { continue }
	if { [set line [NextLine 3 $file]] == "" } { return }
	# channel?
	if { ! [regexp {^Channel.*:} $line] } {
	    # "No Channel line found after $netno Network"
	    GMMessage $MESS(badfile)
	    continue
	}
	set obs [AddToNB $obs $line]
	if { [set line [NextLine 1 $file]] == "" } { return }
	# encryption?
	if { ! [regexp {^Encryption.*: "(.+)"$} $line x encr] } {
	    # "No Encryption line found after $netno Network"
	    GMMessage $MESS(badfile)
	    continue
	}
	set symbol $defsymbol
	foreach p $esymbols {
	    if { [lindex $p 0] == $type } {
		foreach "epatt s" [lindex $p 1] {
		    if { [string match -nocase $epatt $encr] } {
			set symbol $s
			break
		    }
		}
		break
	    }
	}
	if { [set line [NextLine 10 $file]] == "" } { return }
	# min loc
	if { ! [regexp {^Min Loc.*: Lat ([-.0-9]+) Lon ([-.0-9]+) } $line \
		    x lat1 long1] } {
	    # "No/bad Min Loc line after $netno Network"
	    GMMessage $MESS(badfile)
	    continue
	}
	if { [set line [NextLine 1 $file]] == "" } { return }
	# max loc
	if { ! [regexp {^Max Loc.*: Lat ([-.0-9]+) Lon ([-.0-9]+) } $line \
		    x lat2 long2] } {
	    # "No/bad Max Loc line after $netno Network"
	    GMMessage $MESS(badfile)
	    continue
	}
	if { [catch {set lat [expr ($lat1+$lat2)*0.5]}] || \
		 [catch {set long [expr ($long1+$long2)*0.5]}] } {
	    # "Error averaging coordinates after $netno Network"
	    GMMessage $MESS(badfile)
	    continue
	}
	foreach "posn pfmt datum" \
	        [FormatPosition $lat $long "WGS 84" $PositionFormat "" DDD] {
	    break
	}
	set data [FormData WP "Name Obs PFrmt Posn Datum Symbol" \
		      [list $name $obs $pfmt $posn $datum $symbol]]
	StoreWP -1 $name $data $LFileVisible($file)
	lappend names $name
	lappend namesof($type) $name
    }
    if { $crgroups } {
	# create a group with WPs for each type of network
	foreach t [array names namesof] {
	    set name ${prename}_$t
	    if { [IndexNamed GR $name] != -1 } { set name [NewName GR] }
	    set obs "Kismet type: $t"
	    set els [list [list WP [lsort -dictionary $namesof($t)]]]
	    set data [FormData GR "Name Obs Conts" [list $name $obs $els]]
	    # displaying has already been taken care of
	    StoreGR -1 $name $data 0
	}
    }
    return
}

## BGA format: British Glider Association DOS turnpoints files available at
#    http://www.spsys.demon.co.uk/turningpoints.htm
#
# PS contribution: most of the BGA support is
# based on code from the  dos2gpsman  script by
#  Paul Scorer (p.scorer@leedsmet.ac.uk), distributed under GPL

proc Import_BGA {file how} {
    # import WPs from file in BGA (DOS) format
    #  $how in {normal, inGR}: keep all data, not used
    global BGAfeature BGAfindblty BGAairact PositionFormat LFileVisible \
	ALSCALEFOR MESS TXT

    set nwps 0 ; set ascale $ALSCALEFOR(FT)
    set date [Now]
    # Process each record
    while 1 {
	if { [SlowOpAborted] } { return }
	# get one record
	if {[gets $file fullname] > 0 && \
		[gets $file trigraph] > 0 && \
		[gets $file bganum] > 0 && \
		[gets $file findability] > 0 && \
		[gets $file exactpoint] > 0 && \
		[gets $file description] > 0 && \
		[gets $file distance] > 0 && \
		[gets $file direction] > 0 && \
		[gets $file feature] > 0 && \
		[gets $file OSMap] > 0 && \
		[gets $file Easting_Northing] > 0 && \
		[gets $file Lat_Long] > 0 && \
		[gets $file altitude] > 0 && \
		[gets $file trigraph1] > 0 && \
		[gets $file junk] == 0} {
	    # Blank line - Record Separator
	    if {[string equal $trigraph $trigraph1] } {
		# Simple consistency check:
		# Trigraph at last line should match that at second line
		scan  $Lat_Long "%d%lf%s%d%lf%s" \
		    degLat minLat NS degLong minLong EW

		# Matches "Findability"?
		if { $BGAfindblty != "" } {
		    if { [BGACheckFind $BGAfindblty $findability] == 0 } {
			continue
		    }
		}

		# Matches "Feature"?
		if { $BGAfeature != "" } {
		    if { [BGACheckPlace $BGAfeature $feature] == 0 } {
			continue
		    }
		}
	
		# Matches "Air Activity"?
		if { $BGAairact != "" } {
		    if { [BGACheckActivity $BGAairact $findability] == 0 } {
			continue
		    }
		}

		set latd [expr $degLat+$minLat/60.0]
		if { $NS == "S" || $NS == "s" } { set latd [expr -$latd] }
		set longd [expr $degLong+$minLong/60.0]
		if { $EW == "W" || $NS == "w" } { set longd [expr -$longd] }
		foreach "posn pfmt datum" \
		    [FormatPosition $latd $longd {WGS 84} \
			 $PositionFormat "" DDD] { break }
		set altitude [expr $ascale*$altitude]
		set obs \
		 "$fullname\n$exactpoint\n$description\n$feature\n$findability"
		if { ! [CheckName Ignore $trigraph] } {
		    if { [set nname [AskForName $trigraph]] == "" } {
			continue
		    }
		    set obs "$obs\n$TXT(oname): $trigraph"
		    set trigraph $nname
		}
		set data [FormData WP "Name Obs PFrmt Posn Datum Alt Date" \
			      [list $trigraph $obs $pfmt $posn \
				   $datum $altitude $date]]
		StoreWP [IndexNamed WP $trigraph] $trigraph $data \
		    $LFileVisible($file)
		incr nwps
	    } else {
		GMMessage $MESS(badfile)
		return
	    }
	} else {
	    break
	}
    }
    if { $nwps == 0 } {
	GMMessage $MESS(nosuchitems)
    }
    return
}

proc BGACheckPlace {placeList feature} {
    # check if current record is associated with $feature

    foreach place $placeList {
	if { [string equal -nocase $feature $place] } {
	    return 1
	}
    }
    return 0
}

proc BGACheckFind {findList findability} {
    # check if "findability" (A B C D or G) matches that given as parameter

    foreach category $findList {
	if { [string equal -nocase -length 1 $category $findability] } {
	    return 1
	}
    }
    return 0
}

proc BGACheckActivity {activityList findability} {
    # check if category of "Air Activity" matches that given as parameter

    foreach category $activityList {
	set len [string length $category]
	incr len 
	if { [string length $findability] == $len } {
	    return 1
	}
    }
    return 0
}

## SimpleText format
# based on the Garmin Simple Text Output protocol and the following rules
# 	- discard lines with position status different from g or G
# 	- a single discarded line (not at the beginning of the file) is
# 	taken as a segment start marker
# 	- two or more discarded lines in sequence (not at the beginning of
# 	the file) are taken as starting a new TR

proc Import_SimpleText {file how} {
    # load TRs from file in SimpleText format
    #  $how in {normal, inGR}: keep all data, not used
    global LFileLNo LFileEOF LFileVisible MESS DateFormat

    set tps "" ; set tpn 0 ; set es 0 ; set sgsts ""
    while { 1 } {
	set m [ReadFile $file]
	if { $LFileEOF($file) } { break }
	if { [string first @ $m] != 0 || \
		 [string length $m] != 55 } {
	    GMMessage "$MESS(badTR) $LFileLNo($file)"
	    return
	}
	set bad 0
	foreach fd "x yr mon day hr min sec hlt ltd ltm hlg lgd lgm st x alt" \
                wd "1 2 2 2 2 2 2 1 2 5 1 3 5 1 3 6" \
	        type "x d d d d d d c d d c d d c x pd" {
	    if { $type == "pd" } {
		if { [set u [string first _ $m]] != -1 && $u < $wd } {
		    set $fd "" ; set type x
		} else { set type d }
	    }
	    switch $type {
		d {
		    set type "0${wd}d"
		    if { [scan $m "%$type" $fd] == 0 } {
			incr bad
			break
		    }
		}
		c { set $fd [string range $m 0 [expr $wd-1]] }
	    }
	    set m [string replace $m 0 [expr $wd-1]]
	}
	if { $bad || ( $st != "G" && $st != "g" ) || $ltm < 0 || $lgm < 0 } {
	    incr es
	    continue
	}
	# date
	if { $yr > 86 } {
	    # possible year 3000 bug!
	    incr yr 1900
	} else { incr yr 2000 }
	if { ! [CheckDateEls $yr $mon $day $hr $min $sec] } {
	    GMMessage "$MESS(baddate) $LFileLNo($file)"
	    incr es
	    continue
	}
	set l [list $yr $mon $day $hr $min $sec]
	set dtscs [list [eval FormatDate $DateFormat $l] [eval DateToSecs $l]]
	# position
	set lat "$hlt$ltd [expr $ltm/1000.0]"
	set long "$hlg$lgd [expr $lgm/1000.0]"
	if { ! [CheckLat GMMessage $lat DMM] || \
		 ! [CheckLong GMMessage $long DMM] } {
	    incr es
	    continue
	}
	set lat [Coord DMM $lat S]
	set long [Coord DMM $long W]
	set p [FormatLatLong $lat $long DMS]
	# altitude
	if { $st != "G" } { set alt "" }

	if { $es > 1 } {
	    if { $tps != "" } {
		set name [NewName TR]
		set fd [FormData TR "Name Datum TPoints SegStarts" \
			    [list $name "WGS 84" $tps $sgsts]]
		StoreTR -1 $name $fd $LFileVisible($file)
	    }
	    set tps "" ; set tpn 0 ; set sgsts ""
	} elseif { $es && $tps != "" } {
	    # segment starter
	    lappend sgsts $tpn
	}
	set es 0
	lappend tps [FormData TP "latd longd latDMS longDMS date secs alt" \
			 [concat $p $dtscs $alt]]
	incr tpn
    }
    if { $tps != "" } {
	set name [NewName TR]
	set fd [FormData TR "Name Datum TPoints SegStarts" \
		    [list $name "WGS 84" $tps $sgsts]]
	StoreTR -1 $name $fd $LFileVisible($file)
    }
    return
}

## GTrackMaker format

  # datums having the same definition in GTM and GPSMan (possibly under
  #  different names
array set GTMEquivDatum {
    1   "Adindan; B Faso"        2   "Adindan; Cameroon"
    3   "Adindan; Ethiopia"      4   "Adindan; Mali"
    5	"Adindan; Ethiopia+Sudan"    6    "Adindan; Senegal"
    7   "Adindan; Sudan"         8   "Afgooye"
    9   "Ain el Abd 1970; Bahrain"    10  "Ain el Abd 1970; Saudi Arabia"
    11  "American Samoa 1962"    13  "Antigua Island Astro 1943"
    14  "Arc 1950; Botswana"     15  "Arc 1950; Burundi"
    16  "Arc 1950; Lesotho"      17  "Arc 1950; Malawi"
    18  "Arc 1950"               19  "Arc 1950; Swaziland"
    20  "Arc 1950; Zaire"        21  "Arc 1950; Zambia"
    22  "Arc 1950; Zimbabwe"     23  "Arc 1960; Kenya+Tanzania"
    24  "Arc 1960; Kenya"        25  "Arc 1960; Tanzania"
    26  "Ascension Island `58"   27  "Astro Beacon \"E\""
    28  "Astro DOS 71/4"         29  "Astro Tern Island (FRIG)"
    30  "Astronomic Stn `52"     34  "Bellevue (IGN)"
    35  "Bermuda 1957"           36  "Bissau"
    37  "Bogota Observatory"     38  "Bukit Rimpah"
    39  "Camp Area Astro"        40  "Campo Inchauspe"
    41  "Canton Astro 1966"      42  "Cape"
    43  "Cape Canaveral"         44  "Carthage"
    45  "Chatham 1971"           46  "Chua Astro"
    47  "Corrego Alegre"         48  "Dabola"
    49  "Deception Island"       50  "Djakarta (Batavia)"
    51  "DOS 1968"               52  "Easter Island 1967"
    53  "Estonia Coord System 1937"    54  "European 1950; Cyprus"
    55  "European 1950; Egypt"   56  "European 1950; England Channel"
    57  "European 1950; England Channel"
    58  "European 1950; Finland+Norway"
    59  "European 1950; Greece"  60  "European 1950; Iran"
    61  "European 1950; Italy (Sardinia)"
    62  "European 1950; Italy (Sicily)"
    63  "European 1950; Malta"   64  "European 1950"
    65  "European 1950; NW Europe"
    66  "European 1950; Middle East"
    67  "European 1950; Portugal+Spain"
    68  "European 1950; Tunisia"
    69  "European 1979"          70  "Fort Thomas 1955"
    71  "Gan 1970"               72  "Geodetic Datum `49"
    73  "Graciosa Base SW 1948"  74  "Guam 1963"
    75  "Gunung Segara"          76  "GUX 1 Astro"
    77  "Herat North"            78  "Hermannskogel"
    79  "Hjorsey 1955"           80  "Hong Kong 1963"
    81  "Hu-Tzu-Shan"            82  "Indian (Bangladesh)"
    83  "Indian (India, Nepal)"  84  "Indian (Pakistan)"
    85  "Indian 1954"            86  "Indian 1960; Vietnam (Con Son)"
    87  "Indian 1960; Vietnam (N16)"
    88  "Indian 1975"            89  "Indonesian 1974"
    90  "Ireland 1965"           91  "ISTS 061 Astro 1968"
    92  "ISTS 073 Astro `69"     93  "Johnston Island 1961"
    94  "Kandawala"              95  "Kerguelen Island"
    96  "Kertau 1948"            97  "Kusaie Astro 1951"
    98  "NAD83; Canada"          99  "L.C. 5 Astro"
    100 "Leigon"                 101 "Liberia 1964"
    102 "Luzon Philippines"      103 "Luzon Mindanao"
    104 "M'Poraloko"             105 "Mahe 1971"
    106 "Massawa"                107 "Merchich"
    108 "Midway Astro 1961"      109 "Minna; Cameroon"
    110 "Minna"                  111 "Montserrat Island Astro 1958"
    112 "Nahrwn Masirah Ilnd"    113 "Nahrwn Saudi Arbia"
    114 "Nahrwn United Arab"     115 "Naparima BWI"
    116 "NAD27 Alaska"           117 "NAD27 Alaska; Aleutian East"
    118 "NAD27 Alaska; Aleutian West"
    119 "NAD27 Bahamas"          120 "NAD27 San Salvador"
    121 "NAD27 Canada West"      122 "NAD27 Canada Middle"
    123 "NAD27 Canada East"      124 "NAD27 Canada North"
    125 "NAD27 Canada Yukon"     126 "NAD27 Canal Zone"
    127 "NAD27 Cuba"             128 "NAD27 Greenland"
    129 "NAD27 Caribbean"        130 "NAD27 Central"
    131 "NAD27 Canada"           132 "NAD27 CONUS"
    133 "NAD27 CONUS East"       134 "NAD27 CONUS West"
    135 "NAD27 Mexico"           136 "NAD83; Canada"
    137 "NAD83; Aleutian Ids"    138 "NAD83; Canada"
    139 "NAD83; Canada"          140 "NAD83; Hawaii"
    141 "NAD83; Canada"          142 "North Sahara 1959"
    143 "Observatorio 1966"      144 "Old Egyptian"
    145 "Old Hawaiian; Hawaii"   146 "Old Hawaiian; Kauai"
    147 "Old Hawaiian; Maui"     148 "Old Hawaiian"
    149 "Old Hawaiian; Oahu"     150 "Oman"
    151 "Ord Srvy Grt Britn; England"
    152 "Ord Srvy Grt Britn; England+Wales"
    153 "Ord Srvy Grt Britn"     154 "Ord Srvy Grt Britn; Scotland+Shetlands"
    155 "Ord Srvy Grt Britn; Wales"
    156 "Pico De Las Nieves"     157 "Pitcairn Astro 1967"
    158 "Point 58"               159 "Pointe Noire 1948"
    160 "Porto Santo 1936"       161 "Prov So Amrican 56; Bolivia"
    162 "Prov So Amrican 56; Chile North"
    163 "Prov So Amrican 56; Chile South"
    164 "Prov So Amrican 56; Colombia"
    165 "Prov So Amrican 56; Ecuador"
    166 "Prov So Amrican 56; Guyana"
    167 "Prov So Amrican `56"    168 "Prov So Amrican 56; Peru"
    169 "Prov So Amrican 56; Venezuela"
    170 "Prov So Chilean `63"    171 "Puerto Rico"
    172 "Pulkovo 1942"           173 "Qatar National"
    174 "Qornoq"                 175 "Reunion"
    176 "Rome 1940"              177 "S-42 (Pulkovo 1942); Hungary"
    178 "S-42 (Pulkovo 1942); Poland"
    179 "S-42 (Pulkovo 1942); Czechoslavakia"
    180 "S-42 (Pulkovo 1942); Latvia"
    181 "S-42 (Pulkovo 1942); Kazakhstan"
    182 "S-42 (Pulkovo 1942); Albania"
    183 "S-42 (Pulkovo 1942); Hungary"
    184 "S-JTSK"                 185 "Santo (DOS)"
    186 "Sao Braz"               187 "Sapper Hill 1943"
    188 "Schwarzeck"             189 "Selvagem Grande 1938"
    190 "Sierra Leone"           191 "South American 69; Argentina"
    192 "South American 69; Bolivia"
    193 "South American 69; Brazil"
    194 "South American 69; Chile"
    195 "South American 69; Colombia"
    196 "South American 69; Ecuador"
    197 "South American 69; Baltra"
    198 "South American 69; Guyana"
    199 "South American `69"      200 "South American 69; Paraguay"
    201 "South American 69; Peru"
    202 "South American 69; Trinidad+Tobago"
    203 "South American 69; Venezuela"
    204 "South Asia"              205 "Tananarive Observatory 1925"
    206 "Timbalai 1948"           207 "Tokyo"
    208 "Tokyo"                   209 "Tokyo; Okinawa"
    210 "Tokyo; South Korea"      211 "Tristan Astro 1968"
    212 "Viti Levu 1916"          213 "Voirol 1960"
    214 "Wake Island Astro 195"   217 "WGS 84"
    218 "Yacare"                  219 "Zanderij"
    220 "Rijks Driehoeksmeting"   221 "NTF (NTF ellipsoid)"
    224 "CH-1903"                 226 "European 1950; Belgium"
    227 "Israeli"                 228 "Rome 1940; Luxembourg"
    229 "Finland Hayford"         230 "Dionisos"
    231 "South American 69; Brazil/IBGE"
    232 "Potsdam"                 233 "Datum 73"
    234 "WGS 72"
}

  # datums having a different definition in GTM; GPSMan definition will be used
array set GTMEquivDatum {
    12  "Anna 1 Astro 1965"       31  "Australian Geod `66"
    32  "Australian Geod `84"     33  "Ayabelle Lighthouse"
    215 "Wake-Eniwetok 1960"      216 "WGS 1972"
    222 "Potsdam"                 223 "RT 90"
    225 "Austrian (MGI)"
}

set GTMVersions 211

array set GTMTypes {
    header {int charray=10 byte unused=1 byte unused=1 unused=1 byte unused=1
            long long long long long long long float float float float long
            long unused=4 unused=4 bool bool unused=2 unused=2 unused=2
            unused=2 unused=2 unused=2 bool unused=2 varstring varstring
            varstring varstring}
    datum {unused=2 unused=8 unused=8 unused=8 unused=8 int double double int
	   int int}
    image {varstring varstring float float float float long float float byte
           byte}
    wp {double double charray=10 varstring int byte long int float unused=2}
    wpstyle {long varstring byte long long float byte bool long byte byte byte
             byte}
    tr {double double long byte float}
    trstyle {varstring byte long float byte int}
    rt {double double charray=10 varstring varstring int byte byte long int
        float int}
    icon {varstring byte long}
    layer {int varstring long byte byte byte int}
}

array set GTMDescr {
    header {version code maplinewidth unused fontsize unused unused iconcolour
            unused gridcolour bgcolour nwpstyles usercolour nwps ntrs nrts
            maxlong minlong maxlat minlat nimgs ntrnames unused unused
            rectcoords truegrid unused unused unused unused unused unused
            hasmap unused gridfontname unused unused unused}
    datum {unused unused unused unused unused ndatum a f dx dy dz}
    image {NOTUSED}
    wp {lat long name commt symbol style secs txtangle alt unused}
    wpstyle {NOTUSED}
    tr {lat long secs new alt}
    trstyle {NOTUSED}
    rt {lat long wpname wpcommt rtname wpsymbol wpstyle new secs unused unused
        unused}
    icon {NOTUSED}
    layer {NOTUSED}
}

array set GTMConstr {
    header {
	{ if { [lsearch $GTMVersions $version] == -1 } { set m badGTMvers } }
	{ if { $code != "TrackMaker" } { set m badGTMfile } }
	{ if { $nwpstyles < 0 || $nwps < 0 || $ntrs < 0 || $nrts < 0 || $nimgs < 0 || $ntrnames < 0 } {
	    set m badGTMcounts } }
        { if { $nwps == 0 } { set nwpstyles 0 } }
	{ if { $ntrs == 0 } { set ntrnames 0 } }
    }
    datum {
	{ if { [catch {set eqdatum $GTMEquivDatum($ndatum)}] } {
	    set m badGTMdatum } }
    }
    wp {
	{ if { $lat < -90 || $lat > 90 } { set m badGTMlat } }
	{ if { $long < -180 || $long > 180 } { set m badGTMlong } }
    }
    tr {
	{ if { $lat < -90 || $lat > 90 } { set m badGTMlat } }
	{ if { $long < -180 || $long > 180 } { set m badGTMlong } }
    }
    rt {
	{ if { $lat < -90 || $lat > 90 } { set m badGTMlat } }
	{ if { $long < -180 || $long > 180 } { set m badGTMlong } }
    }
}

#
# file structure:
#   header, datum, image info, wps, wpstyles, trs, trstyles, rts, layers,
#   symbols, symbol images, map images
#
# header has counts of
#   wpstyles, wps, tps, rtwps, imgs, trnames

proc Import_GTrackMaker {file args} {
    # names in GTMDescr lists are implicitly used here as local variables!
    global GTMVersions GTMTypes GTMDescr GTMConstr GTMEquivDatum ReadBinError \
	    PositionFormat DateFormat LFileVisible YEAR0 MESS

    fconfigure $file -translation binary
    set ReadBinError 0
    set m "" ; set one 1
    set wpl "" ; set trl "" ; set trtps "" ; set rtl "" ; set rtwps ""
    foreach b "header datum image wp wpstyle tr trstyle rt" \
	    c "one one nimgs nwps nwpstyles ntrs ntrnames nrts" {
	for { set i 0 } { $i < [set $c] } { incr i } {
	    if { [SlowOpAborted] } { return }
	    set vals [ReadBinData $file $GTMTypes($b)]
	    if { $ReadBinError } {
		SlowOpFinish $MESS(errorGTMread)
		return
	    }
	    if { $GTMDescr($b) != "NOTUSED" } {
		# assign values to vars
		foreach $GTMDescr($b) $vals {}
		# check constraints that may assign values to other variables
		foreach ct $GTMConstr($b) {
		    eval $ct
		    if { $m != "" } {
			SlowOpFinish $MESS($m)
			return
		    }
		}
		# use values in variables corresponding to fields and in
		#  variables assigned during evaluation of constraints
		switch $b {
		    wp {
			# unused: symbol, style, txtangle
			lappend wpl \
				[list $lat $long $name $commt $secs $alt]
		    }
		    tr {
			if { $new } {
			    if { $trtps != "" } { lappend trl $trtps }
			    set trtps ""
			}
			lappend trtps [list $lat $long $secs $alt]
		    }
		    rt {
			# unused: wpsymbol, wpstyle
			# assume route name only defined when $new
			if { $new } {
			    if { $rtwps != "" } {
				lappend rtl [list $oldroute $rtwps]
			    }
			    set rtwps ""
			    set oldroute $rtname
			}
			lappend rtwps \
				[list $lat $long $wpname $wpcommt $secs]
		    }
		}
	    }
	}
    }
    if { $trtps != "" } { lappend trl $trtps }
    if { $rtwps != "" } {
	lappend rtl [list $oldroute $rtwps]
    }
    set oldYEAR0 $YEAR0 ; set YEAR0 1990
    foreach wpd $wpl {
	if { [SlowOpAborted] } { return }
	foreach "lat long name commt secs alt" $wpd {}
	# GTM comment saved in remark, along with name if already in use
	if { [set ix [IndexNamed WP $name]] != -1 } {
	    set commt "$name\n$commt"
	}
	foreach "posn pfmt datum" \
	    [FormatPosition $lat $long $eqdatum $PositionFormat "" DDD] {
		break
	}
	set date [DateFromSecs $secs]
	set data [FormData WP "Name Obs PFrmt Posn Datum Date Alt" \
		[list $name $commt $pfmt $posn $datum $date $alt]]
	StoreWP $ix $name $data $LFileVisible($file)
    }
    foreach trtps $trl {
	if { $trtps != "" } {
	    set tps ""
	    foreach tpd $trtps {
		if { [SlowOpAborted] } { return }
		foreach "lat long secs alt" $tpd {}
		set dints [DateIntsFromSecs $secs]
		set date [eval FormatDate $DateFormat $dints]
		set secs [eval DateToSecsFrom $dints $oldYEAR0]
		set d [FormatLatLong $lat $long DMS]
		lappend d $date $secs $alt
		lappend tps \
		    [FormData TP "latd longd latDMS longDMS date secs alt" $d]
	    }
	    set name [NewName TR]
	    set data [FormData TR "Name Datum TPoints" \
		                  [list $name $eqdatum $tps]]
	    StoreTR -1 $name $data $LFileVisible($file)
	}
    }
    foreach rtd $rtl {
	foreach "rtname rtwps" $rtd {}
	if { $rtwps != "" } {
	    set wpns ""
	    foreach wpd $rtwps {
		if { [SlowOpAborted] } { return }
		# GTM wp comment saved in remark, along with name if in use
		foreach "lat long wpname commt secs" $wpd {}
		if { [set ix [IndexNamed WP $wpname]] != -1 } {
		    set commt "$wpname\n$commt"
		}
		foreach "posn pfmt datum" \
		    [FormatPosition $lat $long $eqdatum \
			 $PositionFormat "" DDD] { break }
		set date [DateFromSecs $secs]
		set data [FormData WP "Name Obs PFrmt Posn Datum Date" \
			[list $name $commt $pfmt $posn $datum $date]]
		set wpname [StoreWP $ix $wpname $data 0]
		lappend wpns $wpname
	    }
	    # GTM route name will be saved in remark
	    set id [NewName RT]
	    set data [FormData RT "IdNumber Obs WPoints" \
		                  [list $id $commt $wps]]
	    StoreRT -1 $id $data $wps $LFileVisible($file)
	}
    }
    set YEAR0 $oldYEAR0
    return
}

## Shapefile format

proc ImportShapefileFrom {fname what} {
    # import from Shapefile format items of type $what
    # if $fname=="" ask user to give a file name
    #  $what in {WP, RT, TR, LN}
    # return 1 on error, 0 on success
    global SHPPFormt SHPZone SHPDatum SHPAUnit SHPDUnit SHPDim GFPFormt \
	    GFDUnit GFAUnit GFVisible NNUMPFORMATS MESS TXT INVTXT \
	    MAXWPINROUTE ZGRID POSTYPE ALSCALEFOR SHOWFILEITEMS

    set GFVisible $SHOWFILEITEMS
    if { $fname == "" } {
	set ok 0
	set GFPFormt $TXT($SHPPFormt)
	set GFDUnit $TXT($SHPDUnit) ; set GFAUnit $TXT($SHPAUnit)
	set vs "SHPDim GFPFormt SHPZone SHPDatum GFDUnit GFAUnit GFVisible"
	set pfas [list $NNUMPFORMATS =GFPFormt TXT]
	set us [list $TXT(M) $TXT(FT)]
	set ds [list +$TXT(dimens)/[list 3 2] \
		    !$TXT(optPositionFormat)=FillPFormtMenu/$pfas \
		    =$TXT(zone) !$TXT(datum)=FillDatumMenu/ \
		    +$TXT(distunit)/$us +$TXT(altunit)/$us @$TXT(mapitems)]
	while { [set fn [GMGetFileName $TXT(importfrm) $what r $vs $ds]] \
		!= ".." } {
	    set SHPPFormt $INVTXT($GFPFormt)
	    if { [BadDatumFor $SHPPFormt $SHPDatum GMMessage] != 0 } {
		continue
	    }
	    set SHPDUnit $INVTXT($GFDUnit)
	    set SHPAUnit $INVTXT($GFAUnit)
	    if { $ZGRID($SHPPFormt) } {
		set SHPZone [string trim $SHPZone]
		while { ! [CheckZone GMMessage $SHPZone $SHPPFormt] } {
		    set SHPZone ""
		    if { ! [GMChooseParams $TXT(zone) SHPZone \
			    [list =$TXT(zone)]] } {
			set SHPZone ""
			return 1
		    }
		    set SHPZone [string trim $SHPZone]
		}
	    } else { set SHPZone "" }
	    set basename [file rootname $fn]
	    switch -- [set ext [file extension $fn]] {
		.shp -  .shx -  .dbf -  "" {
		    set ok 1 ; break
		}
		default {
		    if { [GMConfirm [format $MESS(shpext) $ext]] } {
			set ok 1 ; break
		    }
		}
	    }
	}
	if { ! $ok } { return 1 }
    } else {
	# SHPDim SHPPFormt, SHPZone, SHPDatum, SHPDUnit and SHPAUnit assumed to
	#  have been defined
	set basename [file rootname $fname]
    }
    if { [set fsid [GSHPOpenInputFiles $basename]] < 1 } {
	switch -- $fsid {
	    0 { set m shpcntopen }
	    -1 { set m shpemptyfile }
	    -2 { set m shpwrongfile }
	    -3 { set m shpoutmem }
	}
	GMMessage $MESS($m)
	GSHPCloseFiles $fsid
	return 1
    }
    if { [set info [GSHPInfoFrom $fsid]] == 0 } { BUG bad channel ; return 1 }
    foreach "fwh fno fdim fix dbfn dbfnps" $info {}
    if { $fno < 1 } {
	GMMessage $MESS(shpemptyfile) ; GSHPCloseFiles $fsid
	return 1
    }
    if { $fwh == "UNKNOWN" } {
	if { $what == "WP" } {
	    GMMessage $MESS(shpwrongfile) ; GSHPCloseFiles $fsid
	    return 1
	}
    } elseif { $fwh != $what && \
	    ( $what != "LN" || $fwh != "TR" ) } {
	GMMessage $MESS(shpwrongfile) ; GSHPCloseFiles $fsid
	return 1
    }
    if { $fwh == "UNKNOWN" || $fwh == "WP" } {
	set dbfs ""
	foreach "n x" $dbfnps {
	    lappend dbfs $n
	}
    }
    if { $fdim < $SHPDim && ! [GMConfirm $MESS(shplessdim)] } {
	GSHPCloseFiles $fsid
	return 1
    }
    if { $SHPPFormt == "UTM/UPS" } {
	regexp {^([0-5]?[0-9]|60)([A-HJ-NP-Z])$} $SHPZone x ze zn
	set zone [list $ze $zn]
    } else { set zone $SHPZone }
    if { $POSTYPE($SHPPFormt) == "latlong" } {
	set iscdist 1
    } else { set iscdist [expr 1.0/$ALSCALEFOR($SHPDUnit)] }
    set iscalt [expr 1.0/$ALSCALEFOR($SHPAUnit)]
    set zz [expr $fdim+$SHPDim == 6] ; set alt ""
    set tpfs "latd longd latDMS longDMS"
    set lpfs "posn"
    SlowOpWindow $TXT(import)
    switch $fwh {
	WP {
	    set wpfs "Name Commt Obs PFrmt Posn Datum Date"
	    if { $zz } {
		set ixdbfs 6
	    } else { set ixdbfs 5 }
	    while { $fno } {
		if { [SlowOpAborted] } { break }
		incr fno -1
		if { [set fd [GSHPGetObj $fsid $fno]] != "" } {
		    if { $fd == 0 || $fd == -1 } {
			BUG bad GSHPGetObj ; return
		    }
		    if { [set name [lindex $fd 0]] == "" } {
			set name [NewName WP]
			set chgdname ""
		    } elseif { ! [CheckName Ignore $name] } {
			if { [set nname [AskForName $name]] == "" } {
			    continue
			}
			set chgdname $name ; set name $nname
		    } else { set chgdname "" }
		    set commt [MakeComment [lindex $fd 1]]
		    if { ! [CheckDate Ignore [set date [lindex $fd 2]]] } {
			set date ""
		    }
		    set x [expr $iscdist*[lindex $fd 3]]
		    set y [expr $iscdist*[lindex $fd 4]]
		    if { [set posn [ConvertPos $SHPPFormt $zone $x $y \
					$SHPDatum $SHPPFormt]] == -1 } {
			continue
		    }
		    if { $chgdname != "" } {
			set obs "$TXT(oname): $chgdname"
			set sep "\n"
		    } else { set obs "" ; set sep "" }
		    foreach n $dbfs v [lindex $fd $ixdbfs] {
			if { $v != "" } {
			    set obs "${obs}${sep}$n: $v"
			    set sep "\n"
			}
		    }
		    set pd [list $name $commt $obs $SHPPFormt $posn $SHPDatum \
				$date]
		    if { $zz && ! ([BadAltitude [set alt [lindex $fd 5]]] || \
			    $alt < -1e35) } {
			set alt [expr $iscalt*$alt]
			set fs [linsert $wpfs end Alt]
			lappend pd $alt
		    } else { set fs $wpfs }
		    set pd [FormData WP $fs $pd]
		    StoreWP [IndexNamed WP $name] $name $pd $GFVisible
		}
	    }
	}
	RT {
	    set wpfs "Name PFrmt Posn Datum"
	    while { $fno } {
		if { [SlowOpAborted] } { break }
		incr fno -1
		if { [set fd [GSHPGetObj $fsid $fno]] != "" } {
		    if { $fd == 0 || $fd == -1 } {
			BUG bad GSHPGetObj ; return 1
		    }
		    if { [set name [lindex $fd 0]] == "" || \
			    ! [CheckNumber Ignore $name] } {
			set name [NewName RT]
		    }
		    set commt [MakeComment [lindex $fd 1]]
		    if { [set np [lindex $fd 2]] < 1 } {
			GMMessage $MESS(voidRT) ; continue
		    }
		    set wpns ""
		    while { $np } {
			if { [SlowOpAborted] } { break }
			incr np -1
			if { [set pd [GSHPReadNextPoint $fsid]] == -2 } {
			    break
			}
			if { $pd == 0 || $pd == -1 } {
			    BUG bad GSHPReadNextPoint ; return 1
			}
			set x [expr $iscdist*[lindex $pd 0]]
			set y [expr $iscdist*[lindex $pd 1]]
			if { [set posn [ConvertPos $SHPPFormt $zone $x $y \
					    $SHPDatum $SHPPFormt]] == -1 } {
			    continue
			}
			lappend wpns [set wpn [NewName WP]]
			set wdt [list $wpn $SHPPFormt $posn $SHPDatum]
			if { $zz && ! ([BadAltitude [set alt [lindex $pd 2]]] \
				|| $alt < -1e35) } {
			    set alt [expr $iscalt*$alt]
			    set fs [linsert $wpfs end Alt]
			    lappend wdt $alt
			} else { set fs $wpfs }
			StoreWP -1 $wpn [FormData WP $fs $wdt] 0
		    }
		    if { [set np [llength $wpns]] == 0 } {
			GMMessage $MESS(voidRT) ; continue
		    }
		    if { $np > $MAXWPINROUTE } {
			GMMessage [format $MESS(toomuchWPs) $MAXWPINROUTE]
		    }
		    set fd [FormData RT "IdNumber Commt WPoints" \
			    [list $name $commt $wpns]]
		    StoreRT [IndexNamed RT $name] $name $fd $wpns $GFVisible
		}
	    }
	}
	TR {
	    while { $fno } {
		if { [SlowOpAborted] } { break }
		incr fno -1
		if { [set fd [GSHPGetObj $fsid $fno]] != "" } {
		    switch -- $fd {
			-1 -  -2 { BUG bad GSHPGetObj ; return 1 }
			-3 { SlowOpFinish $MESS(shpoutmem) ; return 1 }
		    }
		    if { ! [CheckString Ignore [set name [lindex $fd 0]]] } {
			set name [NewName $what]
		    }
		    set commt [MakeComment [lindex $fd 1]]
		    if { [set np [lindex $fd 2]] < 1 } {
			GMMessage $MESS(voidTR) ; continue
		    }
		    set ssts [lindex $fd 3]
		    set pts ""
		    if { $what == "TR" } {
			while { $np } {
			    if { [SlowOpAborted] } { break }
			    incr np -1
			    if { [set pd [GSHPReadNextPoint $fsid]] == -2 } {
				break
			    }
			    if { $pd == 0 || $pd == -1 } {
				BUG bad GSHPReadNextPoint ; return 1
			    }
			    set x [expr $iscdist*[lindex $pd 0]]
			    set y [expr $iscdist*[lindex $pd 1]]
			    set tpd [ConvertPos $SHPPFormt $zone \
					 $x $y $SHPDatum DMS]
			    if { $zz && \
				    ! ([BadAltitude [set alt [lindex $pd 2]]] \
				    || $alt < -1e35) } {
				set alt [expr $iscalt*$alt]
				set fs [linsert $tpfs end alt]
				lappend tpd $alt
			    } else { set fs $tpfs }
			    lappend pts [FormData TP $fs $tpd]
			}
			if { [set np [llength $pts]] == 0 } {
			    GMMessage $MESS(voidTR) ; continue
			}
			set fd [FormData TR \
				    "Name Obs TPoints Datum SegStarts" \
				    [list $name $commt $pts $SHPDatum $ssts]]
			StoreTR [IndexNamed TR $name] $name $fd $GFVisible
		    } else {
			# LN
			while { $np } {
			    if { [SlowOpAborted] } { break }
			    incr np -1
			    if { [set pd [GSHPReadNextPoint $fsid]] == -2 } {
				break
			    }
			    if { $pd == 0 || $pd == -1 } {
				BUG bad GSHPReadNextPoint ; return 1
			    }
			    set x [expr $iscdist*[lindex $pd 0]]
			    set y [expr $iscdist*[lindex $pd 1]]
			    if { [set posn [ConvertPos $SHPPFormt $zone \
					 $x $y $SHPDatum $SHPPFormt]] == -1 } {
				continue
			    }
			    set dt [list $posn]
			    if { $zz && \
				    ! ([BadAltitude [set alt [lindex $pd 2]]] \
				    || $alt < -1e35) } {
				set alt [expr $iscalt*$alt]
				set fs [linsert $lpfs end alt]
				lappend dt $alt
			    } else { set fs $lpfs }
			    lappend pts [FormData LP $fs $dt]
			}
			if { [set np [llength $pts]] == 0 } {
			    GMMessage $MESS(voidLN) ; continue
			}
			set fd [FormData LN \
				    "Name Obs LPoints Datum PFrmt SegStarts" \
			   [list $name $commt $pts $SHPDatum $SHPPFormt $ssts]]
			StoreLN [IndexNamed LN $name] $name $fd $GFVisible
		    }
		}
	    }
	}
	UNKNOWN {
	    while { $fno } {
		if { [SlowOpAborted] } { break }
		incr fno -1
		if { [set fd [GSHPGetObj $fsid $fno]] != "" } {
		    switch -- $fd {
			-1 -  -2 { BUG bad GSHPGetObj ; return 1 }
			-3 { SlowOpFinish $MESS(shpoutmem) ; return 1 }
		    }
		    foreach "npts ssts dbfvs" $fd {}
		    if { $what == "TR" } {
			set pfrmt DMS
		    } else { set pfrmt $SHPPFormt }
		    set pts ""
		    while { $npts } {
			if { [SlowOpAborted] } { break }
			incr npts -1
			if { [set pd [GSHPReadNextPoint $fsid]] == -2 } {
			    break
			}
			if { $pd == 0 || $pd == -1 } {
			    BUG bad GSHPReadNextPoint ; return 1
			}
			set x [expr $iscdist*[lindex $pd 0]]
			set y [expr $iscdist*[lindex $pd 1]]
			if { [set posn [ConvertPos $SHPPFormt $zone \
					  $x $y $SHPDatum $pfrmt]] == -1 } {
			    continue
			}
			if { ! $zz || [BadAltitude [set alt [lindex $pd 2]]] \
				|| $alt < -1e35 } {
			    set alt ""
			} else { set alt [expr $iscalt*$alt] }
			lappend pts [list $posn $alt]
		    }
		    if { $pts == "" } {
			GMMessage $MESS(void$what) ; continue
		    }
		    set name [NewName $what]
	       	    set obs "" ; set sep ""
		    foreach n $dbfs v $dbfvs {
			if { $v != "" } {
			    set obs "${obs}${sep}$n: $v"
			    set sep "\n"
			}
		    }
		    switch $what {
			RT {
			    set wpfs "Name PFrmt Posn Datum"
			    set wpns ""
			    foreach pt $pts {
				if { [SlowOpAborted] } { break }
				lappend wpns [set wpn [NewName WP]]
				foreach "posn alt" $pt {}
				set wdt [list $wpn $pfrmt $posn $SHPDatum]
				if { $zz && $alt != "" } {
				    set fs [linsert $wpfs end Alt]
				    lappend wdt $alt
				} else { set fs $wpfs }
				StoreWP -1 $wpn [FormData WP $fs $wdt] 0
			    }
			    if { [llength $wpns] > $MAXWPINROUTE } {
				GMMessage [format $MESS(toomuchWPs) \
					          $MAXWPINROUTE]
			    }
			    set fd [FormData RT "IdNumber Obs WPoints" \
				    [list $name $obs $wpns]]
			    StoreRT -1 $name $fd $wpns $GFVisible
			}
			TR {
			    set tps ""
			    foreach pt $pts {
				if { [SlowOpAborted] } { break }
				foreach "tpd alt" $pt {}
				if { $alt != "" } {
				    set fs [linsert $tpfs end alt]
				    lappend tpd $alt
				} else { set fs $tpfs }
				lappend tps [FormData TP $fs $tpd]
			    }
			    set fd [FormData TR \
					"Name Obs TPoints Datum SegStarts" \
					[list $name $obs $tps $SHPDatum $ssts]]
			    StoreTR -1 $name $fd $GFVisible
			}
			LN {
			    set lps ""
			    foreach pt $pts {
				if { [SlowOpAborted] } { break }
				foreach "posn alt" $pt {}
				set dt [list $posn]
				if { $alt != "" } {
				    set fs [linsert $lpfs end alt]
				    lappend dt $alt
				} else { set fs $lpfs }
				lappend lps [FormData LP $fs $dt]
			    }
			    set fd [FormData LN \
				 "Name Obs LPoints Datum PFrmt SegStarts" \
				 [list $name $obs $lps $SHPDatum $pfrmt $ssts]]
			    StoreLN -1 $name $fd $GFVisible
			}
		    }
		}
	    }
	}
    }
    GSHPCloseFiles $fsid
    SlowOpFinish ""
    return 0
}

####
# MGM contribution
#### import/export from/to file in MapSend and Meridian formats

proc Export_MapSend {file what ixs} {
    # MF contribution: code by MGM moved out from general export procs

    fconfigure $file -translation {binary binary}
    ExportMapSend$what $file $ixs
    return
}

proc Export_Meridian {file what ixs} {
    # MF contribution: code by MGM moved out from general export procs

    ExportMeridian$what $file $ixs
    return
}

proc ReadMapSendHeader {file} {
    global ms_content ms_ver

    set hlen 0
    binary scan [read $file 1] "c" hlen

    set tmp [read $file $hlen]

    set ms_ver 1
    set teststr ""
    scan $tmp "%*s%*2s%d" ms_ver

    set ms_content 0
    set tmp [ReadBinData $file [list long]]
    set ms_content [lindex $tmp 0]

    return $ms_content
}

proc ReadString {file} {
    set slen 0
    binary scan [read $file 1] c slen
    set slen [expr $slen & 0xFF]
    set tmpstr [read $file $slen]
    return $tmpstr
}

proc WriteString {file str} {
    puts -nonewline $file [binary format c [string length $str]]
    puts -nonewline $file $str
    return
}

proc ReadMapSendTP {file} {
    global ms_ver
    set longd 0
    set latd 0
    set alt 0
    set tm 0
    set valid 0
    set cs 0
    # Endianess,word len issues here !!!
    set tmp [ReadBinData $file [list double double long long long]]
    set longd [lindex $tmp 0]
    set latd [lindex $tmp 1]
    set alt [lindex $tmp 2]
    set tm [lindex $tmp 3]
    set valid [lindex $tmp 4]

    # centiseconds
    if { $ms_ver >= 30 } {
	binary scan [read $file 1] "c" cs
    }

    set ns "latd longd latDMS longDMS alt date secs"
    set latd [expr $latd * -1]
    # MF change: using FormatLatLong instead of CreatePos
    set p [FormatLatLong $latd $longd DMS]
    set dt [DateFromSecs [expr $tm - 567648000]]
    set tp ""

    if {$valid} {
	set tp [FormData TP $ns [concat $p $alt [list $dt] [expr $tm%86400]]]
    }

    return $tp
}

proc WriteMapSendTP {file tp} {
    global DataIndex TPlatDMS TPlongDMS TPdate

    set lat [expr [lindex $tp $DataIndex(TPlatd) ] * -1]
    set long [lindex $tp $DataIndex(TPlongd) ]
    set dt [lindex $tp $DataIndex(TPdate)]
    set alt [lindex $tp $DataIndex(TPalt)]
    set secs [lindex $tp $DataIndex(TPsecs)]
    
    WriteBinData $file [list double double long long long byte] \
	    [list $long $lat [expr int($alt)] [expr $secs + 567648000] 1 0]

    return
}

proc ReadMapSendWP {file} {
    global ms_ver LFileVisible SYMBOLS MAG_SYMTAB

    set wpnam [ReadString $file]
    set wpcom [ReadString $file]

    set num 0
    binary scan [read $file 4] i num

    set longd 0
    set latd 0
    set alt 0
    set icon 0
    set stat 0

    binary scan [read $file 1] c icon

    binary scan [read $file 1] c stat

    set tmp [ReadBinData $file [list double double double]]
    set alt [lindex $tmp 0]
    set longd [lindex $tmp 1]
    set latd [lindex $tmp 2]
    set latd [expr $latd * -1]
    # MF change: using FormatLatLong instead of CreatePos
    set p [FormatLatLong $latd $longd DMS]

    set ns "Name Commt Posn Symbol Alt"
    set sym [lindex $MAG_SYMTAB $icon]
    # MF contribution: setting undefined symbol to default
    if { $sym == "" } {
	global DEFAULTSYMBOL
	set sym $DEFAULTSYMBOL
    }

    set wp [FormData WP $ns [list $wpnam $wpcom $p $sym $alt]]
    set ix [IndexNamed WP $wpnam]
    StoreWP $ix $wpnam $wp $LFileVisible($file)
    return
}

proc WriteMapSendWP {file widx cnt} {
    global WPPosn WPAlt WPName WPCommt WPSymbol MAG_SYMTAB

    WriteString $file $WPName($widx)
    WriteString $file $WPCommt($widx)

    set latd [lindex $WPPosn($widx) 0]
    set longd [lindex $WPPosn($widx) 1]
    set snum [lsearch -exact $MAG_SYMTAB $WPSymbol($widx)]

    #altitude
    # MF contribution: access to altitude array
    set wpa [lindex $WPAlt($widx) 0]
    if {$wpa == "" } {set wpa 0}

    #write index,symbol,status,alt,pos
    WriteBinData $file [list long byte byte double double double]\
	[list $cnt $snum 2 $wpa $longd [expr $latd * -1]]
    return
}

proc ReadMapSendRT {file} {
    global ms_ver LFileVisible
    # Read the route header, blocks

    set rtnam [ReadString $file]

    set tmp [ReadBinData $file [list long long]]
    set num [lindex $tmp 0]
    set block_cnt [lindex $tmp 1]

    set i 0
    set wps ""
    while { $i < $block_cnt } {
	lappend wps [ReadMapSendRTBlock $file]
	# need to accomodate errors here !!!
	incr i
    }

    set stages ""
    set id 1
    set obs ""
    set ix [IndexNamed RT $id]
    set l [FormData RT "IdNumber Commt Obs WPoints Stages" \
	    [list $id "" $obs $wps $stages]]
    StoreRT $ix $id $l $wps $LFileVisible($file)
    return
}

proc ReadMapSendRTBlock {file} {
    # Read  a route block
    global ms_ver LFileVisible SYMBOLS

    set longd 0
    set latd 0
    set icon 0
    set stat 0

    set rtwpnam [ReadString $file]

    set tmp [ReadBinData $file [list long double double]]
    set num [lindex $tmp 0]
    set longd [lindex $tmp 1]
    set latd [expr [lindex $tmp 2] * -1]

    binary scan [read $file 1] c icon

    return $rtwpnam
}

proc Import_Meridian {file what how} {
    #   Matt.Martin@ieee.org
    # MF contribution: added $what argument, not used
    global LFileEOF LFileVisible LFileLNo MESS ms_ver PDTYPE

    # track points
    set tps ""

    set date [Now]
    set dt "" ; set ixs "" ; set ns ""
    set line [gets $file]
    set ftype [string range $line 5 7]

    switch $ftype {
	
	RTE -
	WPL {
	    set block_cnt 0
	    while { [string range $line 5 7] == "WPL" } {
		# Strip checksum and LF/CR
		#regsub {\*..\r?\n?$} [gets $file] "X" line
		regsub "\\*...\*\$" $line "" line
		set thedat [lrange [UnPackData [split $line ""] \
			[concat [list string] $PDTYPE(WPIN)]] 1 7]
		AddMagWPT [lindex [ConvWPData [list $thedat]] 0]
		incr block_cnt
		# get the next line
		set line [gets $file]
	    }

	    # get route info

	    set dat ""
	    while { [string range $line 5 7] == "RTE" } {
		# need to accomodate errors here !!!
		set thedat [lrange [UnPackData [split $line ""] \
			[concat [list string] $PDTYPE(RTIN)]] 1 8]
		lappend dat $thedat
		set line [gets $file]
	    }
	    InDataRT $dat
	}
	TRK {
	    set block_cnt 0
	    while { [string length $line]} {
		# process each line at a time
		set tmp [lrange [UnPackData [split [string range $line 0 \
			[expr [string length $line] -4]] ""] \
			[concat [list string] $PDTYPE(TRIN)]] 1 8]
		lappend tps [lindex [ConvTPData $tmp] 1]
		incr block_cnt
		set line [gets $file]
	    }

	    set tname [NewName TR]
	    set ix [IndexNamed TR $tname]
	    set data [FormData TR "Name Obs Datum TPoints" \
			  [list $tname $block_cnt "WGS 84" $tps]]
	    StoreTR $ix $tname $data $LFileVisible($file)
	}
    }
    return
}

proc Import_MapSend {file what how} {
    #   Matt.Martin@ieee.org
    # MF contribution: added $what argument, not used
    global LFileEOF LFileVisible LFileLNo MESS ms_ver

    # track points
    set tps ""

    set date [Now]
    set dt "" ; set ixs "" ; set ns ""
    fconfigure $file -translation {binary binary}
    set ftype [ReadMapSendHeader $file]

    switch $ftype {
	1 {
	    set wp_cnt 0
	    # waypoint count
	    set tmp [ReadBinData $file [list long]]
	    set block_cnt [lindex $tmp 0]

	    set i 0
	    set wps ""

	    while { $i < $block_cnt } {
		ReadMapSendWP $file
		# need to accomodate errors here !!!
		incr i
	    }

	    # get route count
	    set rt_cnt 0
	    set tmp [ReadBinData $file [list long]]
	    set rt_cnt [lindex $tmp 0]

	    set i 0
	    while { $i < $rt_cnt } {
		ReadMapSendRT $file
		# need to accomodate errors here !!!
		incr i
	    }
	}
	2 {
	    # getting track data
	    #  read track name
	    set tname [ReadString $file]

	    # get block count
	    set block_cnt 0
 	    set tmp [ReadBinData $file [list long]]
 	    set block_cnt [lindex $tmp 0]

	    set i 0
	    while { $i < $block_cnt } {
		lappend tps [ReadMapSendTP $file]
		# need to accomodate errors here !!!
		incr i
	    }

	    set ix [IndexNamed TR $tname]
	    set data [FormData TR "Name Obs Datum TPoints" \
			  [list $tname $block_cnt "WGS 84" $tps]]
	    StoreTR $ix $tname $data $LFileVisible($file)
	}
    }
    return
}

proc MapSendHeader {file type} {
    puts -nonewline $file [binary format c 0xd]
    puts -nonewline $file "4D533334 MS34"
    WriteBinData $file [list long] [list $type]
    return
}


proc ExportMeridianWP {f items} {

    set cnt 0
    foreach i $items {
	# MF contribution
	if { [SlowOpAborted] } { return }
	#--
	incr cnt
	set outdat [PrepMagWPData $i]
	puts -nonewline $f [join [MakeMagPacket WPL $outdat] ""]
    }
    return
}

proc ExportMeridianRT {f items} {
    global RTWPoints

    # First, Dump out all waypoints
    set wplist ""
    foreach i $items {
	# MF contribution
	if { [SlowOpAborted] } { return }
	#--
	set wps [Apply "$RTWPoints($i)" IndexNamed WP]
	foreach w $wps {
	    # only add to list if not there already
	    if { [lsearch -exact $wplist $w] == -1 } {
		lappend wplist $w
	    }
	}
    }
    ExportMeridianWP $f $wplist

    # Then the routes
    foreach i $items {
	# MF contribution
	if { [SlowOpAborted] } { return }
	#--
	set wps [Apply "$RTWPoints($i)" IndexNamed WP]
	set lncnt [expr int(([llength $wps] + 1 )/2)]
	set lnnum 1
	while { [llength $wps]} {
	    set outdat [PrepMagRTdata [lindex $wps 0] [lindex $wps 1] \
		    $lncnt $lnnum $i]
	    set wps [lreplace $wps 0 1]

	    puts -nonewline $f [join [MakeMagPacket RTE $outdat] ""]
	    incr lnnum
	}
    }
    return
}

proc ExportMeridianTR {f items} {
    global TRTPoints TRDatum

    set cnt 0
    foreach i $items {
	# MF contribution: only change the datum if needed
	if { [SlowOpAborted] } { return }
	if { $TRDatum($i) != "WGS 84" } {
	    set tps [ChangeTPsDatum $TRTPoints($i) $TRDatum($i) "WGS 84"]
	} else { set tps $TRTPoints($i) }
	#--
	incr cnt
	foreach p $tps {
	    # setup the packet
	    # MF change: datum conversion made above
	    set outdat [PrepMagTRData [lreplace $p 2 3]]
	    # add the checksum and write
	    puts -nonewline $f [join [MakeMagPacket TRK $outdat] ""]
	}
    }
    return
}

proc DumpMapSendWP {f items} {
    WriteBinData $f [list long] [list [llength $items]]
    set cnt 0
    foreach i $items {
	incr cnt
	WriteMapSendWP $f $i $cnt
    }
}

proc ExportMapSendWP {f items} {
    MapSendHeader $f 1
    DumpMapSendWP $f $items
    WriteBinData $f [list long] [list 0]
    return
}

proc ExportMapSendRT {f items} {
    global RTWPoints RTData RTIdNumber WPName WPPosn WPSymbol MAG_SYMTAB

    # MF contribution
    set badrts ""
    ##

    # First, Dump out all waypoints
    set wplist ""
    foreach i $items {
	# MF contribution
	if { ! [CheckNumber Ignore $RTIdNumber($i)] } {
	    lappend badrts $i
	    continue
	}
	##
	set wps [Apply "$RTWPoints($i)" IndexNamed WP]
	foreach w $wps {
	    # only add to list if not there already
	    if { [lsearch -exact $wplist $w] == -1 } {
		lappend wplist $w
	    }
	}
    }
    # MF contribution
    if { [set n [llength $badrts]] > 0 } {
	global MESS
	GMMessage [format $MESS(cantsaveRTid) $n]
    }
    ##

    MapSendHeader $f 1
    DumpMapSendWP $f $wplist

    #############################################################
    # Then the routes
    #############################################################

    # number of routes
    WriteBinData $f [list long] [list [llength $items]]

    foreach i $items {
	# MF contribution
	if { $i == [lindex $badrts 0] } {
	    set badrts [lreplace $badrts 0 0]
	    continue
	}
	##
	set wps [Apply "$RTWPoints($i)" IndexNamed WP]
	set lncnt [expr int(([llength $wps] + 1 )/2)]
	# route name
	WriteString $f $RTIdNumber($i)
	#route num, block cnt
	WriteBinData $f [list long long] [list $RTIdNumber($i) [llength $wps]]

	set wpnum 1
	foreach w $wps {
	    # name
	    WriteString $f $WPName($w)
	    # wp index
	    # long
	    set longd [lindex $WPPosn($w) 1]
	    # lat
	    set latd [lindex $WPPosn($w) 0]
	    # sym
	    set snum [lsearch -exact $MAG_SYMTAB $WPSymbol($w)]
	    incr wpnum
	    WriteBinData $f [list long double double byte] \
		    [list [expr [lsearch $wplist $w]+1] $longd \
		          [expr $latd * -1] $snum]
	}
    }
    return
}

proc ExportMapSendTR {of ixs} {
    global TRName TRTPoints

    MapSendHeader $of 2
    set i [lindex $ixs 0]
    WriteString $of $TRName($i)
    WriteBinData $of [list long] [list [llength $TRTPoints($i)]]
    
    foreach tp $TRTPoints($i) {
	WriteMapSendTP $of $tp
    }
    return
}






