#
#  gpsman --- GPS Manager: a manager for GPS receiver data
#
#  Copyright (c) 2002 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.tcl
#  Last change:  4 February 2002
#
# Includes contributions by Brian Baulch (baulchb@onthenet.com.au)
#  marked "BSB contribution"
#
# Includes an adaptation of a Perl script by Niki Hammler, http://www.nobaq.net
#  that converts exported FUGAWI data to DDD GPSman data

array set FCOMMAND {
    format  "!Format:"
    pformat "!Position:"
    datum   "!Datum:"
    dates   "!Creation:"
    0   no
    1   yes
    WP   "!W:"
    RT   "!R:"
    RS   "!RS:"
    TR   "!T:"
    GR   "!G:"
    GRWP   "!GW:"
    GRRT   "!GR:"
    GRTR   "!GT:"
    GRGR   "!GG:"
    nb   "!NB:"
    comment   "%"
    mapback    "!Image:"
    mapproj    "!Projection:"
    maptransf  "!Transf:"
    mapscale   "!Scale:"
    mapbackat  "!Image at:"
    maporigin  "!Origin:"
    mapinfo    "!Map:"
}

set FCOMMARGS {RT RS TR GR GRWP GRRT GRTR GRGR nb format pformat datum dates
               mapback mapproj maptransf maporigin mapscale mapbackat}

  # WP attribute-value pairs:
  #    name of attribute, data array name, var for default value, and proc
  #    to check value (Ignore if appropriate; proc should return 1 on error
  #    and will be called with the value as argument)
set FWPATTRPAIRS {"symbol WPSymbol DEFAULTSYMBOL BadSymbol"
                  "dispopt WPDispOpt DEFAULTDISPOPT BadDispOpt"
                  "alt WPAlt EMPTYSTR BadAltitude"}

##### saving/exporting

proc WriteHeader {file pformt} {
    # write header to file in GPSMan format, using $pformt for positions
    global TimeOffset Datum FCOMMAND CREATIONDATE \
	    SFilePFrmt SFilePFType SFileDatum SFileHeader MESS

    if { $SFileHeader($file) } {
	if { "$SFilePFrmt($file)" != "$pformt" } {
	    WriteChgPFormat $file $pformt
	}
	if { "$SFileDatum($file)" != "$Datum" } {
	    WriteChgDatum $file $Datum
	}	
    } else {
	puts $file "$FCOMMAND(comment) $MESS(written) GPSManager [NowTZ]"
	puts $file "$FCOMMAND(comment) $MESS(editrisk)"
	puts $file ""
	puts $file "$FCOMMAND(format) $pformt $TimeOffset $Datum"
	puts $file "$FCOMMAND(dates) $FCOMMAND($CREATIONDATE)"
	puts $file ""
	set SFilePFrmt($file) $pformt
	set SFilePFType($file) [PosType $pformt]
	set SFileDatum($file) $Datum
	set SFileHeader($file) 1
    }
    return
}

proc ExportHeader {file} {
    # write header to file in GPStrans format, using $pformt for positions
    global TimeOffset Datum SFilePFrmt SFilePFType 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
    set SFilePFType($file) latlong
    return
}

proc WriteChgPFormat {file pformt} {
    # write command for changing position format to file in GPSMan format
    global FCOMMAND SFilePFrmt SFilePFType

    puts $file "$FCOMMAND(pformat) $pformt"
    set SFilePFrmt($file) $pformt ; set SFilePFType($file) [PosType $pformt]
    return
}

proc WriteChgDatum {file datum} {
    # write command for changing datum to file in GPSMan format
    global FCOMMAND SFilePFrmt SFileDatum

    puts $file "$FCOMMAND(datum) $datum"
    set SFileDatum($file) $datum
    return
}

proc FormatPosn {posn type} {
    # from position to string under given format type

    switch $type {
	latlong { return [format "%s\t%s" [lindex $posn 2] [lindex $posn 3]] }
	utm {
	    return [format "%s\t%s\t%s\t%s" [lindex $posn 2] \
		      [lindex $posn 3] [lindex $posn 4] [lindex $posn 5]] 
	}
	grid {
	    return [format "%s\t%s\t%s" [lindex $posn 2] \
		      [lindex $posn 3] [lindex $posn 4]]
	}
	nzgrid {
	    return [format "%s\t%s" [lindex $posn 2] [lindex $posn 3]]
	}
	mh {
	    return [lindex $posn 2]
	}
    }
}

proc SaveNB {file obs} {
    # write NB data to file in GPSMan format
    global FCOMMAND

    if { "$obs" != "" } {
	puts $file "$FCOMMAND(nb)\t$obs"
	puts $file ""
    }
    return
}

proc WriteWPsRSs {file ixs stages} {
    # write WPs with indices in list $ixs and RT $stages (may be void) to
    #  file in GPSMan format
    global WPName WPCommt WPObs WPPFrmt WPPosn WPDatum WPDate WPHidden \
	    FWPATTRPAIRS CREATIONDATE SFilePFrmt SFilePFType SFileDatum

    foreach i $ixs st $stages {
	if { $i != -1 } {
	    if { "$WPPFrmt($i)" != "$SFilePFrmt($file)" } {
		WriteChgPFormat $file $WPPFrmt($i)
	    }
	    if { "$WPDatum($i)" != "$SFileDatum($file)" } {
		WriteChgDatum $file $WPDatum($i)
	    }
	    set p [FormatPosn $WPPosn($i) $SFilePFType($file)]
	    if { $CREATIONDATE } {
		puts -nonewline $file \
			"$WPName($i)\t$WPCommt($i)\t$WPDate($i)\t$p"
	    } else {
		puts -nonewline $file "$WPName($i)\t$WPCommt($i)\t$p"
	    }
	    foreach fd $FWPATTRPAIRS {
		set an [lindex $fd 1] ; set dv [lindex $fd 2]
		global $an $dv
		set x [set [set an]($i)]
		if { "$x" != "[set $dv]" } {
		    puts -nonewline $file "\t[lindex $fd 0]=$x"
		}
	    }
	    foreach h $WPHidden($i) {
		puts -nonewline $file "\t$h"
	    }
	    puts $file ""
	    SaveNB $file $WPObs($i)
	} else {
	    puts "UNDEF"
	}
	if { "$st" != "" } {
	    WriteRTStage $file $st
	}
    }
    puts $file ""
    return
}

proc WriteRTStage {file stage} {
    # write a non-empty RT stage to file in GPSMan format
    global FCOMMAND DataIndex

    set c [lindex $stage $DataIndex(RScommt)]
    set l [lindex $stage $DataIndex(RSlabel)]
    puts -nonewline $file "$FCOMMAND(RS)\t$c\t$l"
    foreach h [lindex $stage $DataIndex(RShidden)] {
	puts -nonewline $file "\t$h"
    }
    puts $file ""
    return
}

proc JustfLeft {n string} {
    # justify string left to get a $n-characters string 
    set k [expr $n-[string length $string]]
    if { $k > 0 } {
	set sp [format "%${k}s" " "]
	return [append string $sp]
    }
    return $string
}

proc ExportWP {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 WPPFrmt WPPosn WPDatum WPDate
    global CREATIONDATE SFileDatum

    set d [Today MMDDYYYY]
    foreach i $ixs {
	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 [ConvertDatum $latd $longd \
			            $WPDatum($i) $SFileDatum($file) DDD]
		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 ExportRT {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

    foreach i $ixs {
	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)]]
	    ExportWP $file $wps $tofile
	}
    }
    return
}

proc ExportTR {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 {
	set c [string compare $TRDatum($i) $SFileDatum($file)]
	foreach tp $TRTPoints($i) {
	    set latd [lindex $tp 0] ; set longd [lindex $tp 1]
	    if { $c } {
		set p [ConvertDatum $latd $longd \
			            $TRDatum($i) $SFileDatum($file) DDD]
		set latd [lindex $p 0] ; set longd [lindex $p 1]
	    }
	    puts $file [format "T\t%s\t%lf\t%lf" [lindex $tp 4] $latd $longd]
	}
	puts $file ""
    }
    return
}

proc SaveWP {file ixs} {
    # save data for WPs with indices in list $ixs to file in GPSMan format
    global FCOMMAND PositionFormat

    WriteHeader $file $PositionFormat
    puts $file $FCOMMAND(WP)
    WriteWPsRSs $file $ixs ""
    return
}

proc SaveRT {file ixs} {
    # save data for RTs with indices in list $ixs to file in GPSMan format
    global FCOMMAND RTIdNumber RTCommt RTObs RTWPoints RTStages \
	    PositionFormat MESS

    WriteHeader $file $PositionFormat
    foreach i $ixs {
	set wpixs [Apply "$RTWPoints($i)" IndexNamed WP]
	if { [Undefined $wpixs] } {
	    GMMessage [format $MESS(undefWP) $RTIdNumber($i)]
	} else {
	    puts $file "$FCOMMAND(RT)\t$RTIdNumber($i)\t$RTCommt($i)"
	    SaveNB $file $RTObs($i)
	    WriteWPsRSs $file $wpixs $RTStages($i)
	}
    }
    return
}

proc SaveTR {file ixs} {
    # save data for TRs with indices in list $ixs to file in GPSMan format
    global FCOMMAND TRName TRObs TRDatum TRTPoints TRHidden DataIndex \
	    SFileDatum

    WriteHeader $file DMS
    set ilt $DataIndex(TPlatDMS) ; set ilg $DataIndex(TPlongDMS)
    set idt $DataIndex(TPdate)
    set ial $DataIndex(TPalt) ; set idp $DataIndex(TPdepth)
    foreach i $ixs {
	if { "$TRDatum($i)" != "$SFileDatum($file)" } {
	    WriteChgDatum $file $TRDatum($i)
	}
	puts -nonewline $file [format "%s\t%s" $FCOMMAND(TR) $TRName($i)]
	foreach h $TRHidden($i) {
	    puts -nonewline $file "\t$h"
	}
	puts $file ""
	SaveNB $file $TRObs($i)
	foreach tp $TRTPoints($i) {
	    set alt [lindex $tp $ial] ; set dep [lindex $tp $idp]
	    if { "$dep" == "" } {
		if { "$alt" == "" } {
		    puts $file [format "\t%s\t%s\t%s" [lindex $tp $idt] \
			    [lindex $tp $ilt] [lindex $tp $ilg]]
		} else {
		    puts $file [format "\t%s\t%s\t%s\t%s" [lindex $tp $idt] \
			    [lindex $tp $ilt] [lindex $tp $ilg] $alt]
		}
	    } else { 
		puts $file [format "\t%s\t%s\t%s\t%s\t%s" [lindex $tp $idt] \
			[lindex $tp $ilt] [lindex $tp $ilg] $alt $dep]
	    }
	}
	puts $file ""
    }
    return
}

proc SaveGR {file ixs} {
    # save data for GRs with indices in list $ixs to file in GPSMan format
    global SFileHeader TYPES FCOMMAND MESS GRName GRObs GRConts

    foreach wh $TYPES {
	set l [GRsElements $ixs 1 $wh]
	if { "$wh" != "GR" } {
	    if { "$l" != "" } { Save$wh $file $l }
	} else { set nixs $l }
    }
    if { ! $SFileHeader($file) } {
	puts $file "$FCOMMAND(comment) $MESS(written) GPSManager [NowTZ]"
	puts $file ""
    }
    foreach i $nixs {
	puts $file [format "%s\t%s" $FCOMMAND(GR) $GRName($i)]
	SaveNB $file $GRObs($i)
	foreach p $GRConts($i) {
	    set c $FCOMMAND(GR[lindex $p 0])
	    foreach e [lindex $p 1] {
		puts $file [format "%s\t%s" $c "$e"]
		set c ""
	    }
	}
	puts $file ""
    }
    return
}

proc SaveWPDistBear {file w} {
    # save distances and bearings between WPs from window $w

    puts $file [$w.fr.fromto cget -text]
    puts $file ""
    puts $file [$w.fr.fr1.dist cget -text]
    puts $file [$w.fr.fr1.bear cget -text]
    puts $file ""
    return
}

proc SaveWPNearest {file w} {
    # save nearest WPs information from window $w
    global MESS

    puts $file [$w.fr.from cget -text]
    puts $file ""
    puts $file $MESS(WPNearest)
    set fb $w.fr.fr1.frbx
    foreach n [$fb.bxn get 0 end] d [$fb.bxd get 0 end] b [$fb.bxb get 0 end] {
	puts $file "$n\t$d\t$b"
    }
    puts $file ""    
    return
}

proc SaveRTComp {file w} {
    # save results of RT computation from window $w
    global MESS

    puts $file [$w.fr.fr1.ntitle cget -text]
    puts $file $MESS(RTcomp)
    set w $w.fr.fr3.fr31
    foreach n [$w.frbx.bxn get 0 end] \
	    p [$w.frbx.box get 0 end] \
	    d [$w.frbx.bxd get 0 end] \
	    b [$w.frbx.bxb get 0 end] \
	    da [$w.frbx.bxda get 0 end] \
	    sc [$w.frbx.bxsc get 0 end] \
	    sl [$w.frbx.bxsl get 0 end] {
	puts $file "$n\t$p\t$d\t$b\t$da\t$sc\t$sl"
    }
    puts $file [$w.frt.tt cget -text]
    puts $file ""
    return
}

proc SaveTRComp {file w} {
    # save results of TR computation from window $w
    global MESS

    puts $file [$w.fr.fr1.ntitle cget -text]
    puts $file $MESS(TRcomp)
    set w $w.fr.fr3
    foreach n [$w.frbx.bxn get 0 end] \
	    l [$w.frbx.bxl get 0 end] \
	    k [$w.frbx.btl get 0 end] \
	    t [$w.frbx.bdt get 0 end] \
	    a [$w.frbx.bal get 0 end] \
	    s [$w.frbx.bsp get 0 end] \
	    b [$w.frbx.bbg get 0 end] {
	puts $file "$n\t$l\t$k\t$a\t$t\t$s\t$b"
    }
    foreach k "frt.td frt.tt frsp.max frsp.min" {
	puts $file [$w.$k cget -text]
    }
    puts $file "[$w.frd0.toend cget -text] [$w.frd0.max cget -text]"
    puts $file ""
    return
}

proc SavePVTData {file w} {
    # save real-time log (Garmin PVT) information from window $w
    global TXT

    puts $file $TXT(realtimelog)
    set fx .pvt.fri.frtbx
    foreach bname "d lat long alt fix epe eph epv velx vely velz bg" {
	puts -nonewline $file "[$fx.tit$bname cget -text]\t"
	set $bname [$fx.bx$bname get 0 end]
    }
    puts $file "" ; puts $file ""
    foreach xd $d xlat $lat xlong $long xalt $alt xfix $fix xepe $epe \
	    xeph $eph xepv $epv xvelx $velx xvely $vely xvelz $velz xbg $bg {
	puts $file "$xd\t$xlat\t$xlong\t$xalt\t$xfix\t$xepe\t$xeph\t$xepv\t$xvelx\t$xvely\t$xvelz\t$xbg"
    }
    puts $file ""
    return
}

proc SaveMapBkInfo {file args} {
    # save map background information
    #  $args: list with path to image file, projection data, transformation
    #    data, scale, and data on subsidary images
    # projection and transformation data are lists with a name and a sequence
    #  of attribute=value pairs
    # data on subsidary images is a list of pairs with path and grid
    #  coordinates
    global Datum FCOMMAND MESS

    set args [lindex $args 0]
    puts $file "$FCOMMAND(comment) $MESS(written) GPSManager [NowTZ]"
    puts $file "$FCOMMAND(comment) $MESS(edityourrisk)"
    puts $file ""
    puts $file "$FCOMMAND(mapback) [lindex $args 0]"
    puts $file "$FCOMMAND(datum) $Datum"
    set pd [lindex $args 1]
    puts -nonewline $file "$FCOMMAND(mapproj) [lindex $pd 0]"
    foreach p [lrange $pd 1 end] {
	puts -nonewline $file "\t$p"
    }
    puts $file ""
    set pt [lindex $args 2]
    puts -nonewline $file "$FCOMMAND(maptransf) [lindex $pt 0]"
    foreach p [lrange $pt 1 end] {
	puts -nonewline $file "\t$p"
    }
    puts $file ""
    puts $file "$FCOMMAND(mapscale) [lindex $args 3]"
    puts $file ""
    foreach ixsp [lindex $args 4] {
	puts $file "$FCOMMAND(mapbackat) [lindex $ixsp 0] [lindex $ixsp 1]"
    }
    puts $file ""
    return
}

proc SaveMapInfo {file args} {
    # save map datum, projection, transformation, grid and scale
    #  $args is list with projection data, transformation data, position
    #    format of coordinates, and scale
    # projection and transformation data are lists with a name and a sequence
    #  of attribute=value pairs
    global Datum FCOMMAND MESS

    set args [lindex $args 0]
    puts $file "$FCOMMAND(comment) $MESS(written) GPSManager [NowTZ]"
    puts $file "$FCOMMAND(comment) $MESS(edityourrisk)"
    puts $file ""
    puts $file $FCOMMAND(mapinfo)
    puts $file "$FCOMMAND(datum) $Datum"
    set pd [lindex $args 0]
    puts -nonewline $file "$FCOMMAND(mapproj) [lindex $pd 0]"
    foreach p [lrange $pd 1 end] {
	puts -nonewline $file "\t$p"
    }
    puts $file ""
    set pt [lindex $args 1]
    puts -nonewline $file "$FCOMMAND(maptransf) [lindex $pt 0]"
    foreach p [lrange $pt 1 end] {
	puts -nonewline $file "\t$p"
    }
    puts $file ""
    puts $file "$FCOMMAND(pformat) [lindex $args 2]"
    puts $file "$FCOMMAND(mapscale) [lindex $args 3]"
    puts $file ""
    return
}

proc SaveGREls {how args} {
    # save elements of group to file in GPSMan format
    #  $how==all: all elements in all groups
    #  $how==select: groups and element types chosen from list
    #  $args not used but in call-back
    global GRName SFilePFrmt SFilePFType SFileDatum SFileHeader TYPES MESS TXT

    switch $how {
	all {
	    set ixs [array names GRName]
	    set whs $TYPES
	}
	select {
	    if { "[set ixs [ChooseItems GR]]" == "" } { return }
	    set ts ""
	    foreach k $TYPES { lappend ts $TXT(name$k) }
	    if { "[set whs \
		    [GMChooseFrom many $MESS(putwhat) 6 $ts $TYPES]]" == "" } {
	       return
	   }
	}
    }
    set f [GMOpenFile $TXT(saveto) GR w]
    if { "$f" != ".." } {
	SetCursor . watch
	set SFileHeader($f) 0
	if { [set i [lsearch -exact $whs GR]] != -1 } {
	    set whs [lreplace $whs $i $i]
	    set r 1
	} else { set r 0 }
	foreach wh $whs {
	    if { "[set l [GRsElements $ixs $r $wh]]" != "" } {
		Save${wh} $f $l
	    }
	}
	catch {
	    unset SFilePFrmt($f) ; unset SFilePFType($f) ; unset SFileDatum($f)
	    unset SFileHeader($f)
	}
	close $f
	ResetCursor .
    }
    return
}

proc ExportGREls {how args} {
    # write data on elements of group to file in GPStrans format
    #  $how==all: from all groups, element type chosen from list
    #  $how==select: groups and element type chosen from list
    #  $args not used but in call-back
    # in any case only elements of one type in {WP, RT, TR} will be considered
    global GRName SFilePFrmt SFilePFType SFileDatum MESS TXT

    set types "WP RT TR 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) }
    while {1} {
	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 }
    	switch [llength $whs] {
	    0 {
		return
	    }
	    1 {
		break
	    }
	    default {
		GMMessage $MESS(exportonly1)
	    }
	}
    }
    set f [GMOpenFile $TXT(exportto) GR w]
    if { "$f" != ".." } {
	SetCursor . watch
	ExportHeader $f
	if { "[set l [GRsElements $ixs $rec $whs]]" != "" } {
	    Export${whs} $f $l 1
	}
	catch {
	    unset SFilePFrmt($f) ; unset SFilePFType($f) ; unset SFileDatum($f)
	}
	close $f
	ResetCursor .
    }
    return
}

proc SaveFile {how what args} {
    # save information to file in GPSMan format
    #  $how==comp: computation results;
    #            $what in {WPDistBear, WPNearest, RTComp, TRComp, PVTData}
    #            $args: normally the window containing the information; see
    #              procs Save$what
    #  $how==all: all items of type $what
    #            $what in $TYPES or $what==Data
    #  $how==select: items of type $what chosen from list
    #            $what in $TYPES
    #  $how==mapback: map background image information;
    #            $what: MapBkInfo
    #            $args: see proc SaveMapBkInfo
    #  $how==mapinfo: map projection, transformation, position format and scale
    #            $what: MapInfo
    #            $args: see proc SaveMapInfo

    eval [list SaveFileTo {} $how $what $args]
    return
}

proc SaveFileTo {f how what args} {
    # save information to file $f in GPSMan format
    # if $f=="" ask user to select output file
    # see proc SaveFile for description of the arguments
    global SFilePFrmt SFilePFType SFileDatum SFileHeader Storage TXT TYPES \
	    Number

    switch $how {
	all {
	    if { "$what" != "Data" } {
		set ids [lindex $Storage($what) 0]
		global $ids
		set ixs [array names $ids]
		set lp [list [list $what $ixs]]
	    } else {
		set single 0 ; set lp ""
		foreach wh $TYPES {
		    if { $Number($wh) > 0 } {
			set ids [lindex $Storage($wh) 0]
			global $ids
			set ixs [array names $ids]
			lappend lp [list $wh $ixs]
		    }
		}
	    }
	}
	select {
	    if { "[set ixs [ChooseItems $what]]" == "" } { return }
	    set lp [list [list $what $ixs]]
	}
	comp -  mapinfo -
	mapback { }
    }
    if { "$f" != "" || "[set f [GMOpenFile $TXT(saveto) $what w]]" != ".." } {
	SetCursor . watch
	set SFileHeader($f) 0
	switch $how {
	    comp -  mapinfo -
	    mapback {
		Save${what} $f $args
	    }
	    default {
		foreach p $lp {
		    Save[lindex $p 0] $f [lindex $p 1]
		}
	    }
	}
	catch {
	    unset SFilePFrmt($f) ; unset SFilePFType($f) ; unset SFileDatum($f)
	    unset SFileHeader($f)
	}
	close $f
	ResetCursor .
    }
    return
}

proc ExportFile {how what} {
    # write data to file in GPStrans format
    #  $how==all: all items of type $what
    #  $how==select: items of type $what chosen from list
    #  $what in {WP, RT, TR}
    global SFilePFrmt SFilePFType SFileDatum Storage TXT

    switch $how {
	all {
	    set ids [lindex $Storage($what) 0]
	    global $ids
	    set ixs [array names $ids]
	}
	select {
	    if { "[set ixs [ChooseItems $what]]" == "" } { return }
	}
    }
    set f [GMOpenFile $TXT(exportto) $what w]
    if { "$f" != ".." } {
	SetCursor . watch
	ExportHeader $f
	Export$what $f $ixs 1
	ResetCursor .
	catch {
	    unset SFilePFrmt($f) ; unset SFilePFType($f) ; unset SFileDatum($f)
	}
	close $f
    }
    return
}

## saving/restoring state

proc SaveState {} {
    # save current state of interface and current data
    global USvState USvData USvMap MESS Number MapEmpty Map TYPES EdWindow \
	    MapPFormat

    set todispl "" ; set toopen ""
    if { $Number(Data) != 0 } {
	if { [catch "set f [open $USvData w]"] } {
	    GMMessage [format $MESS(cantwrtsstate) $USvData]
	    return
	}
	set saveddata $USvData
	SaveFileTo $f all Data
	foreach wh $TYPES {
	    global ${wh}Displ GM${wh}Index

	    set ns "" ; set os ""
	    foreach ix [array names ${wh}Displ] {
		set name [NameOf $wh $ix]
		if { [set [set wh]Displ($ix)] } {
		    lappend ns $name
		}
		if { [winfo exists .gm${wh}sh$ix] } {
		    lappend os $name
		}
	    }
	    if { [winfo exists $EdWindow($wh)] && [set GM${wh}Index] != -1 } {
		set os [linsert $os 0 [NameOf $wh [set GM${wh}Index]]]
	    }
	    if { "$ns" != "" } { lappend todispl [list $wh $ns] }
	    if { "$os" != "" } { lappend toopen [list $wh $os] }
	}
    } else { set saveddata "" }
    if { ! $MapEmpty } {
	if { [catch "set f [open $USvMap w]"] } {
	    GMMessage [format $MESS(cantwrtsstate) $USvMap]
	    return
	}
	set savedmap $USvMap
	if { "[$Map find withtag mapimage]" != "" } {
	    set mapfile Back
	    SaveMapBack $f
	} else {
	    set mapfile Params
	    SaveMapParams $f
	}
    } else { set savedmap "" }
    if { [catch "set f [open $USvState w]"] } {
	GMMessage [format $MESS(cantwrtsstate) $USvState]
	return
    }
    puts $f ""
    puts $f "# $MESS(written) GPSMan [NowTZ]"
    puts $f "# $MESS(editrisk)"
    puts $f ""
    puts $f "set saveddata \"$saveddata\""
    puts $f "set todispl \{$todispl\}"
    puts $f "set toopen \{$toopen\}"
    puts $f ""
    puts $f "set savedmap \"$savedmap\""
    if { "$savedmap" != "" } {
	puts $f "set mapfile $mapfile"
	puts $f "set mappformat $MapPFormat"
    }
    puts $f ""
    puts $f "cd \"[pwd]\""
    puts $f ""
    # must be the last instruction:
    puts $f "set saved 1"
    puts $f ""
    close $f
    return
}

proc RestoreState {} {
    # restore saved state and clear it if the user agrees
    global USvState MESS CREATIONDATE Proc DELSTATE MapPFormat ASKPROJPARAMS \
	    Map

    if { [file size $USvState] == 0 } { return }
    source $USvState
    if { [catch "set saved"] } {
	GMMessage [format $MESS(corruptsstate) $USvState]
	return
    }
    if { "$DELSTATE" == "always" || \
	    ("$DELSTATE" == "ask" && [GMConfirm $MESS(delsstate)]) } {
	set del 1
    } else { set del 0 }
    if { "$savedmap" != "" } {
	if { "$mapfile" == "Back" } {
	    set r [LoadMapFixedBk $savedmap]
	    if { [lindex $r 0] == 1 } {
		eval LoadMapBackGeoRef [lrange $r 1 end]
		set MapPFormat $mappformat
	    } else { return }
	} else {
	    # $mapfile == "Info"
	    set r [LoadMapInfo $savedmap]
	    if { $r == -1 } { return }
	    eval LoadMapParams $r
	}
	if { $del } { DeleteFile $savedmap }
    }
    if { "$saveddata" != "" } {
	if { [catch "set f [open $saveddata r]"] } {
	    GMMessage [format $MESS(cantrdsstate) $saveddata]
	    return
	}
	LoadFileFrom $f Data
	if { $del } { DeleteFile $saveddata }
    }
    set a $ASKPROJPARAMS ; set ASKPROJPARAMS 0
    foreach p $todispl {
	set wh [lindex $p 0]
	foreach n [lindex $p 1] {
	    PutMap $wh [IndexNamed $wh $n]
	}
    }
    set ASKPROJPARAMS $a
    foreach p $toopen {
	set wh [lindex $p 0]
	set opts "change revert create forget cancel"
	foreach n [lindex $p 1] {
	    set ix [IndexNamed $wh $n]
	    $Proc($wh) $ix $opts [ItemData $wh $ix]
	    set opts ""
	}
    }
    if { $del } { DeleteFile $USvState }
    # delete logo
    $Map delete dummy
    return
}

## saving canvas to Postscript

proc SaveCanvas {c bounds} {
    # save canvas $c as a postscript file
    #  $bounds is bounding box of visible part of canvas
    #    (should be set to "" if there is no need for it)
    # items with tag "temp" can be created in and deleted from $c
    # if "$c"=="$Map" inclusion of map scale is an option
    global PAPERSIZE PAGEWIDTH PAGEHEIGHT GFMode GFRot \
	    GFPaper GFLegend GFVisible GFScale MESS TXT MAPCOLOUR \
	    Map MpW MAPSCLENGTH

    set leg "=$TXT(legend)"
    set cms [list $TXT(colour) $TXT(grey) $TXT(mono)]
    set wrs [list $TXT(portr) $TXT(landsc)]
    set pps [array names PAGEWIDTH]
    if { [set i [lsearch -exact $pps $PAPERSIZE]] != 0 } {
	set pps [linsert [lreplace $pps $i $i] 0 $PAPERSIZE]
    }
    set vs "GFLegend GFMode GFRot GFPaper"
    set ds [list $leg $cms $wrs $pps]
    set GFVisible 0
    if { "$bounds" != "" } {
	set vs [linsert $vs 1 GFVisible]
	set ds [linsert $ds 1 "@$TXT(psvisible)"]
    }
    if { "$c"=="$Map" } {
	set GFScale 1
	set vs [linsert $vs 1 GFScale]
	set ds [linsert $ds 1 "@$TXT(incscale)"]
    } else { set GFScale 0 }
    set fn [GMGetFileName $TXT(saveto) Plot w $vs $ds]
    if { "$fn" != ".." } {
	SetCursor . watch
	foreach m $cms tm "color gray mono" {
	    if { "$GFMode" == "$m" } {
		set GFMode $tm
		break
	    }
	}
	if { $GFVisible } {
	    set mbs $bounds
	} else { set mbs [$c bbox all] }
	set y0 [lindex $mbs 1] ; set x0 [lindex $mbs 0]
	set yf [lindex $mbs 3] ; set xf [lindex $mbs 2]
	set sm [expr ($xf+$x0)/2.0]
	if { $GFScale } {
	    # only applies when "$c"=="$Map"
	    set ys [expr $yf+60]
	    set m $MAPSCLENGTH ; set m2 [expr $m/2.0]
	    scan [$MpW.frm.frmap3.cv.val cget -text] "%d %s" sc su
	    if { [set xs0 [expr $sm-$m2]] < $x0 } {
		set m $m2
		if { [set xs0 [expr $sm-$m/2.0]] < $x0 } {
		    set x0 $xs0
		}
		set sc [expr $sc/2.0]
		if { int($sc) != $sc } {
		    set sc [format "%.1f" $sc]
		} else { set sc [expr int($sc)] }
	    }
	    if { [set xsf [expr $xs0+$m]] > $xf } {
		set xf $xsf
	    }
	    set yd [expr $ys-7.5] ; set yf [expr $ys+7.5]
	    $Map create line $xs0 $ys $xsf $ys -arrow both \
		    -fill $MAPCOLOUR(mapsel) -tags temp
	    $Map create line $xs0 $yd $xs0 $yf -fill $MAPCOLOUR(mapsel) \
		    -tags temp
	    $Map create line $xsf $yd $xsf $yf -fill $MAPCOLOUR(mapsel) \
		    -tags temp
            $Map create text $sm [expr $ys-8] -font fixed \
		    -fill $MAPCOLOUR(mapsel) -text "$sc $su" -tags temp
	}
	if { "$GFLegend" != "" } {
	    set yt [expr $yf+20]
	    $c create text $sm $yt -font fixed \
		    -fill $MAPCOLOUR(mapleg) -text $GFLegend -tags temp
	    set yf [expr $yt+5]
	}
	set rot [expr [string compare $GFRot $TXT(portr)]!=0]
	set h [expr $yf-$y0+100] ; set w [expr $xf-$x0+100]
	if { $h > $w } {
	    set sc "-pageheight"
	    if { $rot } {
		set sca $PAGEWIDTH($GFPaper)
	    } else { set sca $PAGEHEIGHT($GFPaper) }
	} else {
	    set sc "-pagewidth"
	    if { $rot } {
		set sca $PAGEHEIGHT($GFPaper)
	    } else { set sca $PAGEWIDTH($GFPaper) }
	}
	if { $GFVisible } {
	    $c postscript -file $fn -colormode $GFMode $sc $sca -rotate $rot
	} else {
	    $c postscript -file $fn -width $w -height $h -colormode $GFMode \
		    -x [expr $x0-50] -y [expr $y0-50] $sc $sca -rotate $rot
	}
	$c delete temp
	ResetCursor .
    }
    return
}

##### loading/importing

proc OpenInputFileFails {what fmt} {
    # open file for loading/importing and set initial values
    #  $what in $TYPES or $what==Data
    #  $fmt in {GPSMan, GPStrans, Fugawi}
    # return 0 unless operation is to be cancelled
    global CREATIONDATE GFVisible TXT

    switch $fmt {
	GPSMan {
	    set for loadfrm ; set cr $CREATIONDATE
	}
	GPStrans {
	    set for importfrm ; set cr 1
	}
	Fugawi {
	    set for importfrm ; set cr 0
	}
    }
    set GFVisible 0
    set f [GMOpenFileParms $TXT($for) $what r \
	                   GFVisible [list @$TXT(mapitems)]]
    if { "$f" == ".." } { return 1 }
    InitInputFileVars $f $what $cr $GFVisible
    return 0
}

proc InitInputFileVars {f what cr vis} {
    # initialise input file $f global variables
    #  $what in $TYPES or $what==Data
    #  $cr set if creation date is in use
    #  $vis set if must display items read in
    global LFileLNo LFileCreat LFileBuffFull LFileEOF LFileDefs LFileVisible \
	    LChannel

    SetCursor . watch
    set LChannel($what) $f
    set LFileLNo($f) 0 ; set LFileCreat($f) $cr
    set LFileBuffFull($f) 0 ; set LFileEOF($f) 0
    set LFileDefs($f) 0 ; set LFileVisible($f) $vis
    return 0
}

proc CloseInputFile {what} {
    # close file opened for loading/importing and unset some global variables
    #  $what in $TYPES
    global LFilePFrmt LFilePFType LFileDatum LFileLNo LFileCreat \
	    LFileId LFileOther LFileBuff LFileBuffFull LFileEOF LFileDefs \
	    LFileVisible LChannel

    set f $LChannel($what)
    close $f
    foreach v "LFilePFrmt($f) LFilePFType($f) LFileDatum($f) \
	    LFileLNo($f) LFileCreat($f) LFileDefs($f) LFileId($f) \
	    LFileOther($f) LFileBuff($f) LFileBuffFull($f) \
	    LFileEOF($f) LFileVisible($f) LChannel($what)" {
	catch "unset $v"
    }
    ResetCursor .
    return
}

proc ReadFileNL {file} {
    # buffered read from file a line (may be empty) at a time
    # uses and sets:
    #   buffer LFileBuff, flag LFileBuffFull set if buffer contents not used
    #   flag LFileEOF, line counter LFileLNo
    global LFileBuffFull LFileBuff LFileLNo LFileEOF

    if { $LFileEOF($file) } { return "" }
    if { $LFileBuffFull($file) } {
	set LFileBuffFull($file) 0
    } else {
	if { [eof $file] } {
	    set LFileEOF($file) 1
	    return ""
	}
	gets $file LFileBuff($file) ; incr LFileLNo($file)
    }
    return $LFileBuff($file)
}

proc ReadFile {file} {
    # buffered read from file a line at a time skipping empty lines
    # uses and sets:
    #   buffer LFileBuff, flag LFileBuffFull set if buffer contents not used
    #   flag LFileEOF, line counter LFileLNo
    global LFileBuffFull LFileBuff LFileLNo LFileEOF

    if { $LFileEOF($file) } { return "" }
    if { $LFileBuffFull($file) } {
	set LFileBuffFull($file) 0
    } else {
	if { [eof $file] } {
	    set LFileEOF($file) 1
	    return ""
	}
	gets $file LFileBuff($file) ; incr LFileLNo($file)
	while { "$LFileBuff($file)" == "" } {
	    if { [eof $file] } { 
		set LFileEOF($file) 1
		return ""
	    }
	    gets $file LFileBuff($file) ; incr LFileLNo($file)
	}
    }
    return $LFileBuff($file)
}

proc ChopInString {m s} {
   # deletes prefix of length $m and leading spaces and tabs from string $s

   return [string trimleft [string range "$s" [string length "$m"] end] " \t"]
}

proc CleanLine {m importing} {
    # deletes leading/trailing blanks, and, if not importing, comments
    global FCOMMAND

    set m [string trim $m " "]
    if { ! $importing && [string first $FCOMMAND(comment) $m] == 0 } {
	return ""
    }
    return $m
}

proc MakeComment {commt} {
    # try to change comment to fit to acceptable character set and length;
    #  on failure return ""
    global COMMENTLENGTH NOLOWERCASE

    if { ! [CheckChars Ignore $commt] } {
	if { $NOLOWERCASE } {
	    set commt [string toupper $commt]
	}
	regsub -all {:\.} $commt "" commt
    }
    if { ! [CheckChars Ignore $commt] } {
	return ""
    }
    if { [string length $commt] > $COMMENTLENGTH } {
	set commt [string trim $commt "\ \t"]
    }
    if { [string length $commt] > $COMMENTLENGTH } {
	set commt [string range $commt 0 [expr $COMMENTLENGTH-1]]
    }
    return $commt
}

proc FindPFormat {line} {
    # parse position format
    global GRIDS

    foreach f "DMS DMM DDD UTM/UPS MH" {
	if { [string first $f $line] == 0 } { return $f }
    }
    foreach i $GRIDS {
	if { [string first $i $line] == 0 } { return $i }
    }
    return BAD
}

proc BadFilePTArgs {file name list params f cf} {
    # parse arguments of projection or transformation command
    #  $f in {Proj, Transf}
    #  $cf in {PROJ, TRANSF}
    # LFile${f}($file) is set to list with name and list of pairs with
    #  parameter name and value
    # return 0 if correct
    global LFile${f} MESS

    set l ""
    foreach av [lrange $list 1 end] {
	set fp [split $av =]
	if { [llength $fp] != 2 } {
	    GMMessage "$MESS(badfield): $f"
	    return 1
	}
	set fn [lindex $fp 0]
	if { [set i [lsearch -exact $params $fn]] == -1 } {
	    GMMessage "$MESS(badattr): $fn"
	    return 1
	}
	set params [lreplace $params $i $i]
	lappend l [list $fn [lindex $fp 1]]
    }
    if { "$params" != "" } {
	GMMessage "$MESS(missattrs): $params"
	return 1
    }
    set LFile${f}($file) [list $name $l]
    return 0
}

proc FilePTComm {file f cf line} {
    # parse projection or transformation command
    #  $f in {Proj, Transf}
    #  $cf in {PROJ, TRANSF}
    # LFile${f}($file) is set to parsed data here or by proc BadFilePTArgs
    # return 1 if correct
    global MAPKNOWN${cf}S MAP${cf}DATA MESS LFile${f}

    set l [split $line \t]
    set p [lindex $l 0]
    if { [lsearch -exact [set MAPKNOWN${cf}S] $p] == -1 } {
	GMMessage "$MESS(unkn$f): $p"
	return 0
    }
    if { [catch "set MAP${cf}DATA($p)"] } {
	# for particular cases of projections with fixed parameters
	set LFile${f}($file) $p
	return 1
    }
    if { [BadFilePTArgs $file $p $l [set MAP${cf}DATA($p)] $f $cf] } {
	GMMessage $MESS(bad${f}args)
	return 0
    }
    return 1
}

proc FindArgs {commd line file} {
    # parse arguments of command when reading file in GPSMan format
    global LFilePFrmt LFilePFType LFileDatum LFileLNo LFileCreat \
	    LFileId LFileOther LFileEOF LFileDefs LFilePath \
	    LFileOrigin LFileScale LFileIxPath MESS

    switch $commd {
	RT {
	    if { [scan $line "%d" LFileId($file)] == 1 } {
		regsub {[0-9]+} $line "" line
		set LFileOther($file) [string trimleft $line " \t"]
		return RT
	    }
	}
	RS {
	    set l [split $line \t]
	    if { [llength $l] >= 2 } {
		set d [list [lindex $l 0] [lindex $l 1] [lrange $l 2 end]]
		set LFileOther($file) [FormData RS "commt label hidden" $d]
		return RS
	    }
	}
	TR {
	    set l [split $line \t]
	    set LFileId($file) [lindex $l 0]
	    # hidden attributes
	    set LFileOther($file) [lreplace $l 0 0]
	    return TR
	}
	GR -
	GRWP -
	GRRT -
	GRTR -
	GRGR {
	    set LFileId($file) [lindex [split $line \t] 0]
	    return $commd
	}
	nb {
	    set t $line
	    while { "[set line [ReadFileNL $file]]" != "" && \
		    ! $LFileEOF($file) } {
		set t [format "%s\n%s" $t $line]
	    }
	    set LFileOther($file) [string trim $t " \n\t"]
	    return nb
	}
	format {
	    if { "[set pf [FindPFormat $line]]" != "BAD" } {
		set line [ChopInString $pf $line]
		if { [scan $line "%f" off] == 1 } {
		    regsub {[+-]?[0-9]+\.?[0-9]*} $line "" line
		    set line [string trimleft $line "\ \t"]
		    if { [DatumRefId $line] != -1 } {
			set LFilePFrmt($file) $pf
			set LFilePFType($file) [PosType $pf]
			set LFileDatum($file) $line
			set LFileDefs($file) 7
			# don't know what to do with time-offset...
			return format
		    }
		    GMMessage "$MESS(unkndatum): $line"
		}
	    }
	}
	dates {
	    switch $line {
		yes { set LFileCreat($file) 1 ; return dates }
		no { set LFileCreat($file) 0 ; return dates }
	    }
	}	
	pformat {
	    if { "[set pf [FindPFormat $line]]" != "BAD" } {
		set LFilePFrmt($file) $pf
		set LFilePFType($file) [PosType $pf]
		set LFileDefs($file) [expr $LFileDefs($file)|6]
		return pformat
	    }
	}
	datum {
	    if { [DatumRefId $line] != -1 } {
		set LFileDatum($file) $line
		set LFileDefs($file) [expr $LFileDefs($file)|1]
		return datum
	    } else {
		GMMessage "$MESS(unkndatum): $line"
	    }
	}
	mapback {
	    if { "$line" != "" } {
		set LFilePath($file) $line
		return mapback
	    }
	}
	mapproj {
	    if { [FilePTComm $file Proj PROJ $line] } {
		# LFileProj set by proc FilePTComm
		return mapproj
	    }
	}
	maptransf {
	    if { [FilePTComm $file Transf TRANSF $line] } {
		# LFileTransf set by proc FilePTComm
		return maptransf
	    }
	}
	maporigin {
	    GMMessage $MESS(oldfilefmt)
	    return BADC
#    	    if { [scan $line %f%f%f%f latd longd x y] == 4 } {
#    		if { [CheckLat Ignore $latd DDD] && \
#    			[CheckLong Ignore $longd DDD] } {
#    		    set LFileOrigin($file) [list $latd $longd $x $y]
#    		    return maporigin
#    		}
#    	    }
	}
	mapscale {
	    if { [scan $line %f s] == 1 && $s > 0 } {
		set LFileScale($file) $s
		return mapscale
	    }
	}
	mapbackat {
	    if { [scan $line %d,%d%s x y p] == 3 } {
		set LFileIxPath($file) [list "$x,$y" $p]
		return mapbackat
	    } else {
		GMMessage "$MESS(mbkbadat): $line"
	    }
	}
    }
    return BADC
}

proc ProcCommand {line file} {
    # parse command when reading file in GPSMan format
    global FCOMMAND FCOMMARGS

    if { [string first ! $line] } { return BAD }
    if { "$FCOMMAND(WP)" == "$line" } { return WP }
    if { "$FCOMMAND(mapinfo)" == "$line" } { return mapinfo }
    foreach c $FCOMMARGS {
	if { [string first $FCOMMAND($c) $line] == 0 } {
	    if { "$c" != "RS" } {
		set line [ChopInString $FCOMMAND($c) $line]
	    } else {
		# first 2 fields may be empty: delete only command and 1st tab
		set line [string range "$line" \
			    [string length "$FCOMMAND($c)"] end]
		regsub {[ ]*\t} $line "" line
	    }
	    return [FindArgs $c $line $file]
	}
    }
    return BADC
}

proc FindCommand {file} {
    # parse command when reading from file in GPSMan format
    global LFileEOF

    while { 1 } {
	set m [ReadFile $file]
	if { $LFileEOF($file) } { return EOF }
	set m [string trim [CleanLine $m 0] "\t"]
	if { [string length $m]>0 && ! [regexp {^%.*} $m] } {
	    return [ProcCommand $m $file]
	}
    }
}

proc ReadNB {file} {
    # parse NB when reading from file in GPSMan format
    global LFileBuffFull LFileOther

    if { "[FindCommand $file]" != "nb" } {
	set LFileBuffFull($file) 1
	return ""
    }
    return $LFileOther($file)
}

proc FindHeader {file} {
    # parse commands at the beginning of file in GPSMan format;
    #  succeed if "format" command found, which may be preceded by
    #  "dates", "position format" or "datum" commands; fail otherwise

    while { 1 } {
	switch [FindCommand $file] {
	    format { return 1 }
	    dates -		
	    pformat -
	    datum { }
	    default { return 0 }
	}
    }
}

proc Clean {coord} {
    # replace degree, minute and second characters by space in coordinate

    regsub -all {['"°]} $coord "\ " nc
    return $nc
}

proc ReadPosn {fs i file} {
    # parse position when reading file in GPSMan format
    #  $fs is list of fields, and position starts at index $i
    # sets $PosnLength to number of fields
    global LFilePFrmt LFilePFType LFileDatum PosnLength MESS TXT

    set pformt $LFilePFrmt($file)
    switch [set ptype $LFilePFType($file)] {
	latlong {
	    set PosnLength 2
	    set lat [Clean [lindex $fs $i]]
	    set long [Clean [lindex $fs [expr $i+1]]]
	    if { ! [CheckLat GMMessage $lat $pformt] || \
		 ! [CheckLong GMMessage $long $pformt] } {
	       return -1
	    }
	    set latd [Coord $pformt $lat S]
	    set longd [Coord $pformt $long W]
	    return [list $latd $longd $lat $long]
	}
	utm {
	    set PosnLength 4
	    set ze [lindex $fs $i] ; incr i
	    set zn [lindex $fs $i] ; incr i
	    set eng [lindex $fs $i] ; incr i
	    set nng [lindex $fs $i]
	    if { ! [CheckZE GMMessage $ze] || \
		 ! [CheckZN GMMessage $zn] || \
		 ! [CheckNumber GMMessage $eng] || \
		 ! [CheckNumber GMMessage $nng] } {
	       return -1
	    }
	    set pd [UTMToDegrees $ze $zn $eng $nng $LFileDatum($file)]
	    return [lappend pd $ze $zn $eng $nng]
	}
	grid -
	nzgrid {
	    set PosnLength 4
	    if { "$ptype" == "grid" } {
		set gr 1
		set zone [lindex $fs $i] ; incr i
		if { ! [CheckZone GMMessage $zone $pformt] } { return -1 }
	    } else { set gr 0 ; set zone "" }
	    set eng [lindex $fs $i] ; incr i
	    set nng [lindex $fs $i]
	    if { ! [CheckNumber GMMessage $eng] || \
		 ! [CheckNumber GMMessage $nng] } {
	       return -1
	    }
	    set pd [GridToDegrees $pformt $zone $eng $nng $LFileDatum($file)]
	    if { $pd == 0 } {
		GMMessage $MESS(outofgrid)
		return -1
	    } elseif { $pd == -1 } {
		GMMessage [format $MESS(badgriddatum) $TXT($pformt) \
			          [GridDatum $pformt]]
		return -1
	    }
	    if { $gr } {
		return [lappend pd $zone $eng $nng]
	    } else { return [lappend pd $eng $nng] }
	}
	mh {
	    set PosnLength 1
	    set mhl [lindex $fs $i]
	    if { ! [CheckMHLocator GMMessage $mhl] } { return -1 }
	    return [linsert [MHLocToDegrees $mhl] end $mhl]
	}
    }
}

proc LoadWPAttrPairs {fields ix} {
    # parse attribute-value pairs for WPs
    # return pair with info on open attributes and list of hidden attributes
    global FWPATTRPAIRS MESS DataIndex

    set os "" ; set hs ""
    foreach f [lrange $fields $ix end] {
	set fp [split $f =] ; set l [llength $fp]
	set fn [lindex $fp 0]
	if { [set i [lsearch -glob $FWPATTRPAIRS "$fn *"]] == -1 } {
	    lappend hs $f
	} elseif { $l > 2 } {
	    GMMessage "$MESS(badWPattr): $f"
	    return ""
	} else {
	    set fd [lindex $FWPATTRPAIRS $i]
	    set fv [lindex $fp 1]
	    if { [[lindex $fd 3] $fv] } {
		GMMessage "$MESS(badWPfield): $f"
		return ""
	    }
	    lappend os [list $DataIndex([lindex $fd 1]) $fv]
	}
    }
    return [list $os $hs]
}

proc LoadWPs {file outRT importing how} {
    # parse WPs data from file
    #  $outRT set if not in the context of reading a RT
    #  $importing set if reading from file in GPStrans instead of GPSMan format
    #  $how in {normal, inGR, inRTGR, void} meaning: keep all data, keep
    #    data for WPs with indices in $LFileIxs($file), save data to
    #    LFileWPs($file) for possible use in RTs in GRs, discard data
    global LFileLNo LFileBuffFull LFileCreat LFilePFrmt LFilePFType \
	    LFileOther LFileDatum LFileEOF LFileVisible LFileWPs LFileIxs \
	    LFileRTStages PosnLength DEFAULTSYMBOL DEFAULTDISPOPT \
	    CREATIONDATE MESS DataIndex

    set wps "" ; set ix -1 ; set count 0
    while { 1 } {
	set m [ReadFile $file]
	if { $LFileEOF($file) } { return $wps }
	set m [CleanLine $m $importing]
	if { [string length $m] > 0 } {
	    if { ! $importing && [string first ! $m]==0 } {
		switch -glob [ProcCommand $m $file] {
		    WP -
		    RT -
		    TR -
		    GR {
			set LFileBuffFull($file) 1
			return $wps
		    }
		    RS {
			if { $outRT } {
			    GMMessage "$MESS(badRS) $LFileLNo($file)"
			    return ""
			}
			lappend LFileRTStages($file) \
				[list $count $LFileOther($file)]
			incr count -1
		    }
		    GR?? -
		    map* -
		    nb -
		    BADC {
			GMMessage "$MESS(badcommdWP) $LFileLNo($file)"
			return ""
		    }
		    default {
			incr count -1
		    }
		}
	    } elseif { "$how" != "void" } {
		if { $importing } {
		    if { [regexp {^[RT].*} $m] } {
			set LFileBuffFull($file) 1
			return $wps
		    }
		    if { [string first W $m]!=0 && [string first \t $m]!=1 } {
			GMMessage "$MESS(notabsWP) $LFileLNo($file)"
			return ""
		    }
		    set m [string range $m 2 end]
		}
		set as [split $m \t]
		set n [llength $as]
		if { $n == 1 } {
		    if { $importing || $outRT } {
			GMMessage "$MESS(notabsWP) $LFileLNo($file)"
			return ""
		    }
		    if { ! [CheckName Ignore $m] && \
			    "[set m [AskForName $m]]" == "" } {
			return ""
		    }
		    if { "$how" == "inRTGR" } {
			set undef 1
			foreach e $LFileWPs($file) {
			    if { "[lindex $e 0]" == "$m" } {
				set undef 0
				break
			    }
			}
		    } else { set undef [expr [IndexNamed WP $m] == -1] }
		    if { $undef } {
			GMMessage [format $MESS(undefWP) $m $LFileLNo($file)]
			return ""
		    }
		    lappend wps $m
		    set ix -1
		} else {
		    if { $n < [expr 3+$LFileCreat($file)] } {
			GMMessage "$MESS(nofieldsWP) $LFileLNo($file)"
			return ""
		    }
		    set name [string trim [lindex $as 0] " "]
		    if { ! [CheckName Ignore $name] && \
			    "[set name [AskForName $name]]" == "" } {
			return ""
		    }
		    set commt [string trim [lindex $as 1] " "]
		    if { ! [CheckComment Ignore $commt] } {
			set commt [MakeComment $commt]
		    }
		    if { $LFileCreat($file) } {
			set date [lindex $as 2] ; set i 3
			if { ! [CheckDate GMMessage $date] } { return "" }
		    } else {
			set date [Now] ; set i 2
		    }
		    set posn [ReadPosn $as $i $file] ; incr i $PosnLength
		    if { "$posn" == "-1" } { return "" }
		    set data [FormData WP "Name Commt PFrmt Posn Datum Date" \
			       [list $name $commt $LFilePFrmt($file) $posn \
			             $LFileDatum($file) $date]]
		    if { $n > $i } {
			if { $importing } {
			    GMMessage "$MESS(excfieldsWP) $LFileLNo($file)"
			    return ""
			}
			# optional attribute-value pairs
			set pl [LoadWPAttrPairs $as $i]
			if { "$pl" == "" } { return "" }
			foreach p [lindex $pl 0] {
			    set k [lindex $p 0]
			    set data [lreplace $data $k $k [lindex $p 1]]
			}
			if { "[lindex $pl 1]" != "" } {
			    set k $DataIndex(WPHidden)
			    set data [lreplace $data $k $k [lindex $pl 1]]
			}
		    }
		    # check for a NB
		    if { ! $importing } {
			set m [CleanLine [ReadFile $file] 0]
			while { ! $LFileEOF($file) && [string length $m]==0 } {
			    set m [CleanLine [ReadFile $file] 0]
			}
			if { ! $LFileEOF($file) && [string first ! $m]==0 && \
				"[ProcCommand $m $file]" == "nb" } {
			    set i $DataIndex(WPObs)
			    set data [lreplace $data $i $i $LFileOther($file)]
			} else {
			    set LFileBuffFull($file) 1
			}
		    }
		    set ix [IndexNamed WP $name]
		    switch $how {
			normal {
			    StoreWP $ix $name $data $LFileVisible($file)
			    lappend wps $name
			}
			inGR {
			    if { [lsearch -exact $LFileIxs($file) $ix] != \
				    -1 } {
				StoreWP $ix $name $data $LFileVisible($file)
				lappend wps $name
			    }
			}
			inRTGR {
			    lappend LFileWPs($file) [list $name $ix $data]
			    lappend wps $name
			}
		    }
		}
	    }
	}
	incr count
    }
}

proc BadRTRead {file importing how} {
    # parse RTs data from file in GPSMan or GPStrans format
    #  $how in {normal, inGR, void}: keep data on all RTs, keep data for RTs
    #    with indices in $LFileIxs($file), discard data
    global LFileLNo LFileId LFileOther LFileVisible LFileIxs LFileWPs \
	    LFileRTStages MAXWPINROUTE MESS

    if { ! [CheckComment Ignore $LFileOther($file)] } {
	set LFileOther($file) [MakeComment $LFileOther($file)]
    }
    set id $LFileId($file) ; set commt $LFileOther($file)
    if { $importing } {
	set obs ""
    } else { set obs [ReadNB $file] }
    set LFileRTStages($file) ""
    switch $how {
	normal {
	    set wpshow normal
	}
	inGR {
	    set wpshow inRTGR
	}
	void {
	    LoadWPs $file 0 $importing void
	    return 0
	}
    }
    set wps [LoadWPs $file 0 $importing $wpshow]
    set cstages $LFileRTStages($file)
    set n [llength $wps]
    if { $n == 0 } {
	GMMessage "$MESS(badWPsRT) $LFileLNo($file)"
	return 1
    }
    if { $n > $MAXWPINROUTE } {
	GMMessage [format $MESS(toomuchWPs) $MAXWPINROUTE]
    }
    set stages "" ; set i 1
    foreach cst $cstages {
	if { [set ct [lindex $cst 0]]>$n || $ct==0 } {
	    GMMessage "$MESS(badWPsRSs) $LFileLNo($file)"
	    return 1
	}
	while { $ct > $i } {
	    lappend stages ""  ;  incr i
	}
	lappend stages [lindex $cst 1] ; incr i
    }
    set ix [IndexNamed RT $id]
    if { "$how" == "inGR" } {
	if { [lsearch -exact $LFileIxs($file) $ix] == -1 } { return 0 }
	foreach n $wps {
	    # that there is an entry for the WP was checked in LoadWPs
	    foreach e $LFileWPs($file) {
		if { "[lindex $e 0]" == "$n" } {
		    StoreWP [lindex $e 1] $n [lindex $e 2] $LFileVisible($file)
		    break
		}
	    }
	}
    }
    set l [FormData RT "IdNumber Commt Obs WPoints Stages" \
	    [list $id $commt $obs $wps $stages]]
    StoreRT $ix $id $l $wps $LFileVisible($file)
    return 0
}

proc LoadTPs {file importing e0} {
    # parse TR points data from file in either GPSMan or GPStrans format
    #  $e0 is the expected contents of first field (either "" or "T")
    global LFileLNo LFileBuffFull LFilePFrmt LFileDatum LFileEOF MESS

    set tps ""
    while { 1 } {
	if { $importing } {
	    set line [ReadFileNL $file]
	    if { $LFileEOF($file) || "$line" == "" } { return $tps }
	} else {
	    set line [ReadFile $file]
	    if { $LFileEOF($file) } { return $tps }
	}
	set line [CleanLine $line $importing]
	if { [string length $line] > 0 } {
	    if { ! $importing && [string first ! $line]==0 } {
		switch [ProcCommand $line $file] {
		    format -  datum -  pformat -  dates - 
		    WP -  RT -  TR -
		    GR {
			set LFileBuffFull($file) 1
			return $tps
		    }
		    comment { }
		    default {
			GMMessage "$MESS(badcommdTP) $LFileLNo($file)"
			return ""
		    }
		}
	    } else {
		set fs [split $line \t]
		if { "[lindex $fs 0]" != "$e0" } {
		    GMMessage "$MESS(badTP) $LFileLNo($file)"
		    return ""
		}
		set ad error ; set dep ""
		if { [set l [llength $fs]] == 4 } {
		    set ad ""
		} elseif { ! $importing } {
		    if { $l == 5 } {
			set ad [lindex $fs 4]
			if { [BadAltitude $ad] } { set ad error }
		    } elseif { $l == 6 } {
			# "?"s used in 5.0, 5.1
			if { "[set ad [lindex $fs 4]]" == "?" } {
			    set ad ""
			} elseif { [BadAltitude $ad] } { set ad error }
			if { "[set dep [lindex $fs 5]]" != "?" && \
				[BadAltitude $dep] } { set ad error }
		    }
		}
		if { "$ad" == "" } {
		    if { "$dep" == "" } {
			set ns "latd longd latDMS longDMS date secs"
		    } else {
			set ad $dep
			set ns "latd longd latDMS longDMS date secs depth"
		    }
		} elseif { "$ad" == "error" } {
		    GMMessage "$MESS(badTP) $LFileLNo($file)"
		    return ""
		} elseif { "$dep" == "" } {
		    set ns "latd longd latDMS longDMS date secs alt"
		} else {
		    set ad [list $ad $dep]
		    set ns "latd longd latDMS longDMS date secs alt depth"
		}
		set date [CheckConvDate [string trim [lindex $fs 1] " "]]
		if { "$date" == "" } { return "" }
		set posn [ReadPosn $fs 2 $file]
		if { "$posn" == "-1" } { return "" }
		if { "$LFilePFrmt($file)" != "DMS" } {
		    set posn [CreatePos [lindex $posn 0] [lindex $posn 1] \
			                DMS latlong $LFileDatum($file)]
		}
		set tp [FormData TP $ns [concat $posn $date $ad]]
		lappend tps $tp
	    }
	}
    }
}

proc BadTRRead {file importing how} {
    # parse TRs data from file in either GPSMan or GPStrans format
    #  $how in {normal, inGR, void}: keep data on all TRs, keep data for TRs
    #    with indices in $LFileIxs($file), discard data
    global LFileLNo LFileDatum LFileId LFileOther LFileVisible LFileIxs MESS

    if { $importing } {
	set e0 T ; set obs "" ; set hs ""
	after 1000 ; set id [Now]
    } else {
	set e0 ""
	set id $LFileId($file) ; set hs $LFileOther($file)
	set obs [ReadNB $file]
    }
    set tps [LoadTPs $file $importing $e0]
    if { "$tps" != "" } {
	if { "$how" == "void" } { return 0 }
	set ix [IndexNamed TR $id]
	if { "$how" != "inGR" || \
		[lsearch -exact $LFileIxs($file) $ix] != -1 } {
	    set data [FormData TR "Name Obs Datum TPoints Hidden" \
		               [list $id $obs $LFileDatum($file) $tps $hs]]
	    StoreTR $ix $id $data $LFileVisible($file)
	}
	return 0
    }
    GMMessage "$MESS(badTPsTR) $LFileLNo($file)"
    return 1
}

proc FormGREls {lpertype} {
    # form list of group elements (pairs of type, elements)
    #  $lpertype is a list with sub-lists for each type in $TYPES
    global TYPES

    set els ""
    foreach wh $TYPES l $lpertype {
	if { "$l" != "" } { lappend els [list $wh $l] }
    }
    return $els
}

proc LoadGRElements {file} {
    # parse GR elements from file in GPSMan format
    global LFileEOF LFileLNo LFileId LFileBuffFull TYPES MESS

    foreach wh $TYPES { set l($wh) "" }
    set wh 0
    while { 1 } {
	set line [ReadFile $file]
	if { $LFileEOF($file) } {
	    set fl ""
	    foreach wh $TYPES {
		lappend fl $l($wh)
	    }
	    return [FormGREls $fl]
	}
	set line [CleanLine $line 0]
	if { [string length $line] > 0 } {
	    if { [string first ! $line]==0 } {
		switch -glob [set c [ProcCommand $line $file]] {
		    format -  datum -  pformat -  dates - 
		    WP -  RT -  TR -
		    GR {
			set LFileBuffFull($file) 1
			set fl ""
			foreach wh $TYPES {
			    lappend fl $l($wh)
			}
			return [FormGREls $fl]
		    }
		    GRWP {
			set wh WP
			set wps ""
			foreach n $LFileId($file) {
			    if { ! [CheckName Ignore $n] && \
				    "[set n [AskForName $n]]" == "" } {
				GMMessage "$MESS(badGRel) $LFileLNo($file)"
				return ""
			    }
			    lappend wps $n
			}
			lappend l(WP) $wps
		    }
		    GR* {
			regsub GR $c "" wh
			lappend l($wh) $LFileId($file)
		    }
		    RS -
		    map* -
		    BADC {
			GMMessage "$MESS(badcommdGRel) $LFileLNo($file)"
			return ""
		    }
		}
	    } else {
		if { $wh == 0 } {
		    GMMessage "$MESS(notypeforGRel) $LFileLNo($file)"
		    return ""
		}
		set fs [split $line \t]
		if { "[lindex $fs 0]" != "" || [llength $fs]!=2 } {
		    GMMessage "$MESS(badGRel) $LFileLNo($file)"
		    return ""
		}
		set ns [lindex $fs 1]
		if { "$wh" == "WP" } {
		    set wps ""
		    foreach n $ns {
			if { ! [CheckName Ignore $n] && \
				"[set n [AskForName $n]]" == "" } {
			    GMMessage "$MESS(badGRel) $LFileLNo($file)"
			    return ""
			}
			lappend wps $n
		    }
		}
		lappend l($wh) $ns
	    }
	}
    }
}

proc BadGRRead {file keep} {
    # parse GRs data from file in GPSMan format
    #  $keep is true if data is to be kept
    global LFileLNo LFileId LFileVisible MESS

    set id $LFileId($file) ; set obs [ReadNB $file]
    set els [LoadGRElements $file]
    if { "$els" != "" } {
	if { $keep } {
	    set data [FormData GR "Name Obs Conts" [list $id $obs $els]]
	    StoreGR [IndexNamed GR $id] $id $data $LFileVisible($file)
	}
	return 0
    }
    GMMessage "$MESS(badGRels) $LFileLNo($file)"
    return 1
}

proc LoadAll {file} {
    # load all items from file in GPSMan format
    global LFileLNo LFileDefs MESS FCOMMAND

    while { 1 } {
	switch [FindCommand $file] {
	    WP {
		if { $LFileDefs($file) != 7 } {
		    GMMessage $MESS(noheader)
		    break
		}
		LoadWPs $file 1 0 normal
	    }
	    RT {
		if { $LFileDefs($file) != 7 } {
		    GMMessage $MESS(noheader)
		    break
		}
		if { [BadRTRead $file 0 normal] } { break }
	    }
	    TR {
		if { $LFileDefs($file) != 7 } {
		    GMMessage $MESS(noheader)
		    break
		}
		if { [BadTRRead $file 0 normal] } { break }
	    }
	    GR {
		if { [BadGRRead $file 1] } { break }
	    }
	    GRWP -
	    GRRT -
	    GRTR -
	    GRGR {
		GMMessage "$MESS(GRelout) $LFileLNo($file)"
	    }
	    format -
	    dates -
	    pformat -
	    datum { }
	    EOF { break }
	    NBC -
	    BAD {
		GMMessage "$MESS(loaderr) $LFileLNo($file)"
		break 
	    }
	    BADC {
		GMMessage "$MESS(unkncommd) $LFileLNo($file)"
		break 
	    }
	}
    }
    return
}

proc OpenLoadFileFails {what} {
    # open file in GPSMan format and set initial values for loading
    #  $what in $TYPES or $what==Data
    # return 0 unless operation is to be cancelled

    return [OpenInputFileFails $what GPSMan]
}

proc LoadFile {what} {
    # open file in GPSMan format and load all items in it
    #  $what in $TYPES or $what==Data identifies menu
    #  (see proc LoadAll)
    global LChannel ReplacedNames

    set ReplacedNames ""
    if { [OpenLoadFileFails $what] } { return }
    LoadAll $LChannel($what)
    CloseInputFile $what
    return
}

proc LoadFileFrom {f what} {
     # open file in GPSMan format and load all items in it
    #  $what in $TYPES or $what==Data
    #  (see proc LoadAll)
    global LChannel ReplacedNames CREATIONDATE

    set ReplacedNames ""
    InitInputFileVars $f $what $CREATIONDATE 0
    LoadAll $f
    CloseInputFile $what
    return
}

proc LoadGRElsIn {what ixs fmt} {
    # load items of type $what (in $TYPES except GR) to replace
    #  items with indices in $ixs or new items if -1 in $ixs
    # $fmt is GPSMan
    global LChannel LFileLNo LFileDefs LFileWPs LFileIxs MESS FCOMMAND TYPES

    set file $LChannel(GR)
    set LFileIxs($file) $ixs
    foreach wh $TYPES {
	if { "$wh" == "$what" } {
	    set how$wh inGR
	    if { "$what" == "RT" } { set howWP inRTGR }
	} else { set how$wh void }
    }
    while { 1 } {
	switch [FindCommand $file] {
	    WP {
		if { $LFileDefs($file) != 7 } {
		    GMMessage $MESS(noheader)
		    break
		}
		LoadWPs $file 1 0 $howWP
	    }
	    RT {
		if { $LFileDefs($file) != 7 } {
		    GMMessage $MESS(noheader)
		    break
		}
		if { [BadRTRead $file 0 $howRT] } { break }
	    }
	    TR {
		if { $LFileDefs($file) != 7 } {
		    GMMessage $MESS(noheader)
		    break
		}
		if { [BadTRRead $file 0 $howTR] } { break }
	    }
	    GR {
		if { [BadGRRead $file 0] } { break }
	    }
	    GRWP -
	    GRRT -
	    GRTR -
	    GRGR {
		GMMessage "$MESS(GRelout) $LFileLNo($file)"
	    }
	    format -
	    dates -
	    pformat -
	    datum { }
	    EOF { break }
	    NBC -
	    BAD {
		GMMessage "$MESS(loaderr) $LFileLNo($file)"
		break
	    }
	    BADC {
		GMMessage "$MESS(unkncommd) $LFileLNo($file)"
		break
	    }
	}
    }
    catch { unset LFileIxs($file); unset LFileWPs($file) }
    return
}

proc LoadGREls {what} {
    # load data from file in GPSMan format according to contents of GR(s)
    #  $what in $TYPES identifies menu, not being used
    global TYPES

    InputToGR $TYPES "" OpenLoadFileFails LoadGRElsIn CloseInputFile GPSMan
    return
}

proc LoadMapFixedBk {path} {
    # try to load map background information
    # if file does not start with !Image command, return 0
    #  otherwise, return -1 on error or abort, or on success:
    #  "1 path datum pd td scale lixp" if new format
    #    Before release 5.3 this returned also
    #  "2 path datum origin scale lixp" if old format with !Origin: command
    #    but this command is not accepted anymore by proc FindArgs
    #  where
    #    pd, pt are pairs with name and list of pairs with parameter
    #     name and value
    #    lxip is list of indices and path for each subsidiary image
    global LFileLNo LFileBuffFull LFileEOF LFilePath LFileDatum LFileDefs \
	    LFileProj LFileTransf LFileOrigin LFileScale LFileIxPath MESS TXT

    if { "$path" == "" } {
	set f [GMOpenFile $TXT(loadfrm) MapBkInfo r]
    } else {
	if { [catch "set f [open $path r]" ] } {
	    GMMessage "MESS(cantread) $path"
	    return -1
	}
    }
    set r -1
    if { "$f" != ".." } {
	set LFileLNo($f) 0 ; set LFileBuffFull($f) 0 ; set LFileEOF($f) 0
	if { "[FindCommand $f]" != "mapback" } {
	    set r 0
	} else {
	    SetCursor . watch
	    set LFileDefs($f) 0
	    if { "[FindCommand $f]" != "datum" } {
		GMMessage $MESS(mbkbaddatum)
	    } else {
		switch [FindCommand $f] {
		    maporigin {
			set r [list 2 $LFilePath($f) $LFileDatum($f) \
				$LFileOrigin($f)]
		    }
		    mapproj {
			if { "[FindCommand $f]" != "maptransf" } {
			    GMMessage $MESS(mbkbadtransf)
			} else {
			    set r [list 1 $LFilePath($f) $LFileDatum($f) \
				    $LFileProj($f) $LFileTransf($f)]
			}
		    }
		    default {
			GMMessage $MESS(mbkbadproj)
		    }
		}
		if { $r != -1 } {
		    if { "[FindCommand $f]" != "mapscale" } {
			GMMessage $MESS(mbkbadscale)
			set r -1
		    } else {
			set l ""
			while { "[FindCommand $f]" == "mapbackat" } {
			    set l [linsert $l 0 $LFileIxPath($f)]
			}
			lappend r $LFileScale($f) $l
		    }
		}
	    }
	    ResetCursor .
	}
	foreach v "LFileLNo($f) LFileBuffFull($f) LFileEOF($f) LFilePath($f) \
		LFileDefs($f) LFileDatum($f) LFileProj($f) LFileTransf($f) \
		LFileOrigin($f) LFileScale($f) LFileIxPath($f)" {
	    catch "unset $v"
	}
	close $f
    }
    return $r
}

proc LoadMapInfo {path} {
    # try to load map information
    # if file does not start with !Map command, return 0
    #  otherwise, return -1 on error or abort, or on success a list with
    #    datum pd td pformat scale 
    #  where
    #    pd, pt are pairs with name and list of pairs with parameter
    #     name and value for projection and transformation
    global LFileLNo LFileBuffFull LFileEOF LFileDatum LFileDefs \
	    LFileProj LFileTransf LFilePFrmt LFileScale MESS TXT

    if { "$path" == "" } {
	set f [GMOpenFile $TXT(loadfrm) MapInfo r]
    } else {
	if { [catch "set f [open $path r]" ] } {
	    GMMessage "MESS(cantread) $path"
	    return -1
	}
    }
    set r -1
    if { "$f" != ".." } {
	set LFileLNo($f) 0 ; set LFileBuffFull($f) 0 ; set LFileEOF($f) 0
	if { "[FindCommand $f]" != "mapinfo" } {
	    GMMessage $MESS(badmapinfo)
	} else {
	    SetCursor . watch
	    set LFileDefs($f) 0
	    if { "[FindCommand $f]" != "datum" } {
		GMMessage $MESS(mbkbaddatum)
	    } elseif { "[FindCommand $f]" != "mapproj" || \
		    "[FindCommand $f]" != "maptransf" || \
		    "[FindCommand $f]" != "pformat" || \
		    "[FindCommand $f]" != "mapscale" } {
		GMMessage $MESS(badmapinfo)
	    } else {
		set r [list $LFileDatum($f) $LFileProj($f) $LFileTransf($f) \
			$LFilePFrmt($f) $LFileScale($f)]
	    }
	}
	ResetCursor .
	foreach v "LFileLNo($f) LFileBuffFull($f) LFileEOF($f) \
		LFileDefs($f) LFileDatum($f) LFileProj($f) LFileTransf($f) \
		LFilePFrmt($f) LFileScale($f)" {
	    catch "unset $v"
	}
	close $f
    }
    return $r
}

## BSB contribution
proc LoadAutoMapInfo {} {
    # Load index of mapfiles and bounds

    global ImageIndex MESS TXT

    set f [GMOpenFile $TXT(loadfrm) MapBkInfo r]
    set index -1
    while {! [eof $f]} {
	incr index
	set line [gets $f]
	set ImageIndex($index) [split $line "\t"]
    }
return
}

##### import from file in foreign format

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 ImportFile {what fmt} {
    # open file in foreign format and load data from it
    #  $fmt in {GPStrans, Fugawi}
    #  $what for GPStrans: in {WP, RT, TR}
    #        for Fugawi: WP
    global LChannel ReplacedNames

    set ReplacedNames ""
    if { [OpenImportFileFails $what $fmt] } { return }
    switch $fmt {
	GPStrans {
	    if { [ImportHeader $LChannel($what)] } {
		Import$what $LChannel($what) normal
	    }
	}
	Fugawi {
	    ImportFugawi $LChannel($what) normal
	}
    }
    CloseInputFile $what
    return
}

proc ImportGREls {what fmt} {
    # load data from file in foreign format according to contents of GR(s)
    #  $fmt in {GPStrans, Fugawi}
    #  $what for GPStrans: WP and RT (no way to identify TRs)
    #        for Fugawi: WP
    #  $what in $TYPES identifies menu, not being used
    global TYPES

    switch $fmt {
	GPStrans {
	    set ts $TYPES ; set exc TR
	}
	Fugawi {
	    set ts WP ; set exc ""
	}
    }
    InputToGR $ts $exc OpenImportFileFails ImportGRElsIn CloseInputFile $fmt
    return
}

proc ImportGRElsIn {what ixs fmt} {
    # import items of type $what to replace items with indices in $ixs
    #  or new items if -1 in $ixs from file in foreign format
    #  $fmt in {GPStrans, Fugawi}
    #  $what for GPStrans: in {WP, RT}
    #        for Fugawi: WP
    global LChannel LFileIxs

    set file $LChannel(GR)
    set LFileIxs($file) $ixs
    switch $fmt {
	GPStrans {
	    if { [ImportHeader $file] } {
		Import$what $file inGR
	    }
	}
	Fugawi {
	    ImportFugawi $file inGR
	}
    }
    unset LFileIxs($file)
    return
}

#### import from file in GPStrans format

proc ImportHeader {file} {
    # parse header when reading from file in GPStrans format
    global LFilePFrmt LFilePFType 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 LFilePFType($file) [PosType $LFilePFrmt($file)]
	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 ImportWP {file how} {
    # load WPs from file in GPStrans format
    #  $how in {normal, inGR}: keep all data, keep data on RTs with
    #     indices in $LFileIxs($file)

    LoadWPs $file 1 1 $how
    return
}

proc ImportRT {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)
    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 ImportTR {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 }
    }
}

#### import from file in Fugawi export format

proc ImportFugawi {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 ""
    while { "[set line [ReadFile $file]]" != "" && ! $LFileEOF($file) } {
	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] && \
		"[set name [AskForName $name]]" == "" } { continue }
	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
    }
    switch $how {
	normal {
	    foreach ix $ixs n $ns d $dt {
		set fd [FormData WP "Name Commt PFrmt Posn Datum Date" $d]
		StoreWP $ix $n $fd $LFileVisible($file)
	    }
	}
	inGR {
	    set grixs $LFileIxs($file)
	    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
}

###

proc DeleteFile {path} {
    # delete or try to empty file
    global tcl_platform

    switch $tcl_platform(platform) {
	unix {
	    exec rm -f [glob $path]
	}
	default {
	    if { [file writable $path] } {
		set f [open $path w] ; close $f
	    }
	}
    }
    return
}
