+source yarrglib.tcl
+source panner.tcl
+package require http
+
+#---------- general utilities ----------
+
+set debug 0
+proc debug {m} {
+ global debug
+ if {$debug} { puts "DEBUG $m" }
+}
+
+proc badusage {m} {
+ puts stderr "where-vessels: bad usage: $m"
+ exit 1
+}
+
+proc glset {n val} {
+ upvar #0 $n var
+ set var $val
+}
+
+#---------- expecting certain errors ----------
+
+proc errexpect-setline {lno line} {
+ glset errexpect_lno $lno
+ glset errexpect_line $line
+}
+
+proc errexpect-error {m} {
+ global errexpect_line errexpect_lno
+ error $m "$errexpect_line\n" [list YARRG-ERREXPECT $errexpect_lno]
+}
+
+proc errexpect-arrayget {arrayvar key} {
+ upvar 1 $arrayvar av
+ upvar 1 ${arrayvar}($key) v
+ if {[info exists v]} { return $v }
+ errexpect-error "undefined $key"
+}
+
+proc errexpect-arrayget-boolean {arrayvar key} {
+ switch -exact [uplevel 1 [list errexpect-arrayget $arrayvar $key]] {
+ true { return 1 }
+ false { return 0 }
+ default { errexpect-error "unexpected $key" }
+ }
+}
+
+proc errexpect-catch {code} {
+ global errorInfo errorCode
+ set rc [catch {
+ uplevel 1 $code
+ } rv]
+ debug "ERREXPECT CATCH |$rc|$rv|$errorCode|$errorInfo|"
+ if {$rc==1 && ![string compare YARRG-ERREXPECT [lindex $errorCode 0]]} {
+ return [list 1 $rv [lindex $errorCode 1] $errorInfo]
+ } elseif {$rc==0} {
+ return [list 0 $rv]
+ } else {
+ return -code $rc -errorinfo $errorInfo -errorcode $errorCode $rv
+ }
+}
+
+#---------- argument parsing ----------
+
+proc nextarg {} {
+ global ai argv
+ if {$ai >= [llength $argv]} {
+ badusage "option [lindex $argv [expr {$ai-1}]] needs a value"
+ }
+ set v [lindex $argv $ai]
+ incr ai
+ return $v
+}
+
+set notes_loc vessel-notes
+set scraper {./yppedia-ocean-scraper --chart}
+set info_cache _vessel-info-cache
+set info_source rsync.yarrg.chiark.net::yarrg/vessel-info
+
+proc parseargs {} {
+ global ai argv
+ global debug scraper
+ set ai 0
+
+ while {[regexp {^\-} [set arg [lindex $argv $ai]]]} {
+ incr ai
+ switch -exact -- $arg {
+ -- { break }
+ --pirate { glset pirate [string totitle [nextarg]] }
+ --ocean { glset ocean [string totitle [nextarg]] }
+ --clipboard-file { load-clipboard-file [nextarg] }
+ --local-html-dir { lappend scraper --local-html-dir=[nextarg] }
+ --notes { glset notes_loc [nextarg] }
+ --vessel-info-source { glset info_source [nextarg] }
+ --debug { incr debug }
+ default { badusage "unknown option $arg" }
+ }
+ }
+ set argv [lrange $argv $ai end]
+ if {[llength $argv]} { badusage "non-option args not allowed" }
+}
+
+proc argdefaults {} {
+ global ocean notes_loc pirate scraper
+ if {![info exists ocean] || ![info exists pirate]} {
+ set cmd {./yarrg --find-window-only --quiet}
+ if {[info exists ocean]} { lappend cmd --ocean $ocean }
+ if {[info exists pirate]} { lappend cmd --pirate $pirate }
+ manyset [split [eval exec $cmd] " "] ocean pirate
+ if {![llength $ocean] || ![llength $pirate]} {
+ error "$ocean $pirate ?"
+ }
+ }
+ lappend scraper $ocean
+}
+
+
+#---------- loading and parsing the vessel notes ----------
+
+proc load-notes {} {
+ global notes_loc notes_data
+ if {[regexp {^\w+\:} $notes_loc]} {
+ update
+ debug "FETCHING NOTES $notes_loc"
+ set req [::http::geturl $notes_loc]
+ switch -glob [::http::status $req].[::http::ncode $req] {
+ ok.200 { }
+ ok.* { error "retrieving vessel-notes: [::http::code $req]" }
+ * { error "Retrieving vessel-notes: [::http::error $req]" }
+ }
+ set newdata [::http::data $req]
+ ::http::cleanup $req
+ } else {
+ debug "READING NOTES $notes_loc"
+ set vn [open $notes_loc]
+ set newdata [read $vn]
+ close $vn
+ }
+ set notes_data $newdata
+}
+
+proc parse-notes {} {
+ global notes_data notes
+ catch { unset notes }
+
+ set lno 0
+ foreach l [split $notes_data "\n"] {
+ incr lno
+ errexpect-setline $lno $l
+ set l [string trim $l]
+ if {![string length $l]} continue
+ if {[regexp {^\#} $l]} continue
+ if {![regexp -expanded \
+ {^ (\d+) (?: \s+([^=]*?) )? \s* =
+ (?: \s* (\S+)
+ (?: \s+ (\S+) )?)? $} \
+ $l dummy vid vname owner note]} {
+ errexpect-error "badly formatted"
+ }
+ set vname [string trim $vname]
+ if {[info exists notes($vid)]} {
+ errexpect-error "duplicate vesselid $vid"
+ }
+ set notes($vid) [list $lno $vname $owner $note]
+ }
+}
+
+proc note-info {lno vid name island description} {
+ global note_infos
+ lappend note_infos [list $lno $vid $name $island $description]
+}
+
+proc display-note-infos {} {
+ global note_infos note_missings notes
+
+ set nmissing [llength $note_missings]
+ debug "display-note-infos $nmissing [array size notes]"
+
+ if {[llength $note_infos]} {
+ set tiny "[llength $note_infos] warning(s)"
+ } elseif {$nmissing && [array size notes]} {
+ set tiny "$nmissing missing"
+ } else {
+ return
+ }
+
+ set infodata {}
+
+ foreach info $note_infos {
+ manyset $info lno vid name island description
+ append infodata "vessel"
+ append infodata " $vid"
+ if {[string length $name]} { append infodata " $name" }
+ if {[string length $island]} { append infodata " ($island)" }
+ append infodata ": " $description "\n"
+ }
+
+ if {$nmissing} {
+ if {[string length $infodata]} { append infodata "\n" }
+ append infodata "$nmissing vessel(s) not mentioned in notes:\n"
+ set last_island {}
+ foreach info [lsort $note_missings] {
+ manyset $info island name vid
+ if {[string compare $island $last_island]} {
+ append infodata "# $island:\n"
+ set last_island $island
+ }
+ append infodata [format "%-9d %-29s =\n" $vid $name]
+ }
+ }
+
+ parser-control-failed-core .cp.ctrl.notes notes \
+ white blue 0 \
+ $tiny \
+ "[llength $note_infos] warning(s);\
+ $nmissing vessel(s) missing" \
+ "Full description of warnings and missing vessels:" \
+ $infodata
+}
+
+#---------- vessel properties ----------
+
+proc info-cache-update {} {
+ global info_source info_cache
+ file mkdir $info_cache
+ exec sh -c "cp -u icons/* $info_cache/."
+
+ if {[string length $info_source]} {
+ set cmdl [list \
+ rsync -udLKtOzm \
+ --exclude=*~ --exclude=*.bak --exclude=.* --exclude=*.tmp \
+ $info_source/ $info_cache 2>@ stderr]
+ debug "INFO-CACHE $cmdl"
+ eval exec $cmdl
+ }
+
+ set f [open $info_cache/vessel-info]
+ glset vessel_class_data [read $f]
+ close $f
+}
+
+proc vesselclasses-init {} {
+ global vc_game2code vc_code2abbrev vc_code2full vc_codes
+
+ global vessel_class_data
+ manyset $vessel_class_data classinfos subclassinfos
+
+ set vc_codes {}
+ foreach {game code abbrev full} $classinfos {
+ if {![regexp {^[a-z][a-z]$} $code code]} { error "bad code" }
+ if {![regexp {^[a-z][a-z]$} $abbrev abbrev]} { error "bad abbrev" }
+ lappend vc_codes $code
+ set vc_game2code($game) $code
+ set vc_code2abbrev($code) $abbrev
+ set vc_code2full($code) $full
+ load-icon $abbrev
+ }
+
+ global vsc_code2report
+ global vsc_game2code
+ set vsc_game2code(null) {}
+ set vsc_code2report() Ordinary
+ set vsc_code2report(!) "Special/L.E."
+ foreach {game code full} $subclassinfos {
+ if {![regexp {^[A-Z]$} $code code]} { error "bad code" }
+ set vsc_game2code($game) $code
+ set vsc_code2report($code) $full
+ }
+
+ load-icon atsea
+ foreach a {battle borrow dot} {
+ foreach b {ours dot query} {
+ load-icon-combine $a $b
+ }
+ }
+}
+
+proc load-icon {icon} {
+ global info_cache
+ image create bitmap icon/$icon -file $info_cache/$icon.xbm
+}
+
+proc load-icon-combine {args} {
+ global info_cache
+ set cmd {}
+ set delim "pnmcat -lr "
+ foreach icon $args {
+ append cmd $delim " <(xbmtopbm $info_cache/$icon.xbm)"
+ set delim " <(pbmmake -white 1 1)"
+ }
+ append cmd " | pbmtoxbm"
+ debug "load-icon-combine $cmd"
+ image create bitmap icon/[join $args +] -data [exec bash -c $cmd]
+}
+
+proc code-lockown2icon {lockown} {
+ manyset [split $lockown ""] lock notown
+ return icon/[
+ lindex {battle borrow dot} $lock
+ ]+[
+ lindex {ours dot query} $notown
+ ]
+}
+
+proc canvas-horiz-stack {xvar xoff y bind type args} {
+ upvar 1 $xvar x
+ upvar 1 canvas canvas
+ set id [eval $canvas create $type [expr {$x+$xoff}] $y $args]
+ set bbox [$canvas bbox $id]
+ set x [lindex $bbox 2]
+ $canvas bind $id <ButtonPress> $bind
+ return $id
+}
+
+proc code2canvas {code canvas x yvar qty qtylen bind} {
+ global vc_code2abbrev
+ upvar 1 $yvar y
+
+ manyset [split $code _] inport class subclass lockown xabbrev
+
+ set stackx $x
+ incr stackx 2
+ set imy [expr {$y+2}]
+
+ if {!$inport} { incr qtylen -1 }
+ if {$qtylen<=0} { set qtylen {} }
+ set qty [format "%${qtylen}s" $qty]
+
+ set qtyid [canvas-horiz-stack stackx 0 $y $bind \
+ text -anchor nw -font fixed -text $qty]
+
+ if {!$inport} {
+ canvas-horiz-stack stackx 0 $imy $bind \
+ image -anchor nw -image icon/atsea
+ incr stackx
+ }
+
+ upvar #0 vc_code2abbrev($class) vcabb
+ if {![info exists vcabb]} {
+ set vcabb vc-$class
+ image create bitmap icon/$vcabb -data \
+ [exec pbmtext -builtin fixed $class | pnminvert | pnmcrop >t.pnm]
+ }
+ canvas-horiz-stack stackx -1 $imy $bind \
+ image -anchor nw -image icon/$vcabb
+
+ if {[string length $subclass]} {
+ canvas-horiz-stack stackx 0 $y $bind \
+ text -anchor nw -font fixed -text \
+ $subclass