chiark / gitweb /
WIP dictionary updates - wip convert to ssh-remote
[ypp-sc-tools.db-test.git] / pctb / dictionary-manager
1 #!/usr/bin/wish
2
3 # helper program for OCR in PCTB upload client
4
5 # This is part of ypp-sc-tools, a set of third-party tools for assisting
6 # players of Yohoho Puzzle Pirates.
7 #
8 # Copyright (C) 2009 Ian Jackson <ijackson@chiark.greenend.org.uk>
9 #
10 # This program is free software: you can redistribute it and/or modify
11 # it under the terms of the GNU General Public License as published by
12 # the Free Software Foundation, either version 3 of the License, or
13 # (at your option) any later version.
14 #
15 # This program is distributed in the hope that it will be useful,
16 # but WITHOUT ANY WARRANTY; without even the implied warranty of
17 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 # GNU General Public License for more details.
19 #
20 # You should have received a copy of the GNU General Public License
21 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
22 #
23 # Yohoho and Puzzle Pirates are probably trademarks of Three Rings and
24 # are used without permission.  This program is not endorsed or
25 # sponsored by Three Rings.
26
27
28 # invocation:
29 # OUT OF DATE
30 #  run this without args
31 #  then on stdin write
32 #     one line which is a Tcl list for unk_{l,r} unk_contexts glyphsdone etc.
33 #     the xpm in the format expected
34 #  then expect child to exit 0, or write a single 0 byte to fd 4
35 #  if it wrote a byte to fd 4, it can take another question
36
37
38 #---------- library routines ----------
39
40 proc manyset {list args} {
41     foreach val $list var $args {
42         upvar 1 $var my
43         set my $val
44     }
45 }
46
47 proc must_gets {f lvar} {
48     upvar 1 $lvar l
49     if {[gets $f l] < 0} { error "huh?" }
50 }
51
52 proc read_counted {f var} {
53     upvar 1 $var var
54     must_gets $f count
55     set var [read $f $count]
56     if {[eof $f]} { error ? }
57 }
58
59 #---------- display core ----------
60
61 set mul 6
62 set inter 1
63
64 set gotsh 20
65 set csrh 20
66 set ctxh 20
67
68 proc init_widgets {} {
69     # idempotent
70     global csrh gotsh ctxh
71
72     if {[winfo exists .d]} return
73     
74     frame .d
75
76     image create bitmap image/main
77     label .d.mi -image image/main -borderwidth 0
78
79     frame .d.csr -bg black -height $csrh
80     frame .d.got -bg black -height $gotsh
81     frame .d.ctx -bg black
82
83     image create bitmap image/cursor -data \
84 {#define csr_width 11
85 #define csr_height 11
86 static unsigned char csr_bits[] = {
87    0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x21, 0x04, 0x22, 0x02, 0x25, 0x05,
88    0xaa, 0x02, 0x74, 0x01, 0xa8, 0x00, 0x70, 0x00, 0x20, 0x00};
89 }
90
91     frame .d.csr.csr
92     label .d.csr.csr.l -image image/cursor -compound left
93     entry .d.csr.csr.e -bd 0
94     pack .d.csr.csr.l -side left
95
96     frame .d.mi.csr_0 -bg white -width 1
97     frame .d.mi.csr_1 -bg white -width 1
98     frame .d.pe
99     frame .d.pe.grid
100     button .d.pe.ok -text OK
101     pack .d.pe.grid .d.pe.ok -side left
102
103     pack .d.mi .d.ctx -side top
104     pack .d
105
106     frame .help
107     pack .help
108 }
109
110 proc resize_widgets_core {} {
111     global mulcols mulrows csrh gotsh ctxh glyphsdone
112     global unk_l unk_contexts
113     
114     foreach w {.d.csr .d.got .d.ctx} {
115         $w configure -width $mulcols
116     }
117
118     eval destroy [winfo children .d.ctx]
119 }
120
121 set last_ht {}
122
123 proc helptext {t} {
124     global last_ht
125     if {![string compare $t $last_ht]} return
126     eval destroy [grid slaves .help]
127     set y 0; foreach l $t {
128         set x 0; foreach c $l {
129             set w .help.at${x}x${y}
130             label $w -text $c
131             grid $w -row $y -column $x -padx 5
132             incr x
133         }
134         incr y
135     }
136     set last_ht $t
137 }
138
139 proc bind_key {k proc} {
140     global keybindings
141     bind . <Key-$k> $proc
142     set keybindings($k) [expr {!![string length $proc]}]
143 }
144 proc unbind_all_keys {} {
145     global keybindings
146     foreach k [array names keybindings] { bind_key $k {} }
147 }
148
149 #---------- database read and write common wrapper ----------
150
151 proc db_getsl {f} {
152     if {[gets $f l] < 0} { error "unexpected db eof" }
153     return $l
154 }
155
156 proc read_database {fn} {
157     global reqkind database database_fn
158     upvar #0 database_magic/$reqkind magic
159     catch { unset database }
160
161     set database_fn $fn
162     if {![file exists $database_fn]} return
163     set f [open $database_fn r]
164     if {[string compare [db_getsl $f] $magic]} { error "$l $reqkind ?" }
165
166     read_database_header/$reqkind $f
167     while 1 {
168         set l1 [db_getsl $f]
169     
170         if {![string length $l1]} continue
171         if {[regexp {^\#} $l1]} continue
172         if {![string compare . $l1]} break
173
174         read_database_entry/$reqkind $f $l1
175     }
176     close $f
177 }
178
179 proc write_database {} {
180     global reqkind database_fn database
181     upvar #0 database_magic/$reqkind magic
182     
183     set f [open $database_fn.new w]
184     puts $f $magic
185
186     write_database_header/$reqkind $f
187
188     set ol {}
189     foreach bm [array names database] {
190         lappend ol [format_database_entry/$reqkind $bm $database($bm)]
191     }
192     foreach o [lsort $ol] {
193         puts $f $o
194     }
195     puts $f "."
196     close $f
197     file rename -force $database_fn.new $database_fn
198 }
199
200 proc required/char {} {
201     global mulrows glyphsdone unk_l unk_r unk_contexts rows
202     
203     must_gets stdin l
204
205     manyset [lrange $l 0 3] unk_l unk_r unk_contexts
206     set glyphsdone [lrange $l 3 end]
207     debug "GOT $l"
208
209     char_read_xpm stdin
210
211     resize_widgets_core
212     foreach w {0 1} {
213         .d.mi.csr_$w configure -height $mulrows
214     }
215     set maxh 0
216     foreach {min max contexts got} $glyphsdone {
217         show_context maxh $min $contexts
218     }
219     show_context maxh $unk_l $unk_contexts
220     .d.ctx configure -height $maxh
221     pack forget .d.pe
222     pack .d.csr -side top -before .d.mi
223     pack .d.got .d.ctx -side top -after .d.mi
224
225     read_database ./charset-$rows.txt
226     draw_glyphsdone
227     startup_cursor
228 }
229
230 #========== PIXMAPS ==========
231
232 #---------- pixmap database read and write ----------
233
234 set database_magic/pixmap {# ypp-sc-tools pctb pixmaps v1}
235
236 proc read_database_header/pixmap {f} { }
237 proc read_database_entry/pixmap {f def} {
238     global database
239
240     set im ""
241     
242     set p3 [db_getsl $f];       append im $p3    "\n"
243     if {[string compare $p3 P3]} { error "$p3 ?" }
244
245     set wh [db_getsl $f];       append im $wh    "\n";   manyset $wh w h
246     set depth [db_getsl $f];    append im $depth "\n"
247
248     for {set y 0} {$y < $h} {incr y} {
249         set line [db_getsl $f]; append im $line  "\n"
250     }
251     set database($im) $def
252 }
253 proc write_database_header/pixmap {f} { puts $f "" }
254 proc format_database_entry/pixmap {im def} {
255     return "$def\n$im"
256 }
257
258 #---------- pixmap display and input handling ----------
259
260 proc foreach_pixmap_col {var body} {
261     global alloptions
262     upvar 1 $var col
263     for {set col 0} {$col < [llength $alloptions]/3} {incr col} {
264         uplevel 1 $body
265     }
266 }
267
268 proc pixmap_select {ncol} {
269     global alloptions
270     debug "PIX SELECT $ncol [llength $alloptions]"
271     foreach_pixmap_col col {
272         if {$col==$ncol} continue
273         .d.pe.grid.l$col selection clear 0 end
274     }
275     pixmap_maybe_ok
276 }
277 proc pixmap_maybe_ok {} {
278     global alloptions pixmap_selcol pixmap_selrow
279     set nsel 0
280     foreach_pixmap_col col {
281         set cs [.d.pe.grid.l$col curselection]
282         incr nsel [llength $cs]
283         set pixmap_selcol $col
284         set pixmap_selrow [lindex $cs 0]
285     }
286     if {$nsel==1} {
287         .d.pe.ok configure -state normal -command pixmap_ok
288     } else {
289         .d.pe.ok configure -state disabled -command {}
290     }
291 }
292 proc pixmap_ok {} {
293     global database ppm pixmap_selcol pixmap_selrow mainkind alloptions
294     foreach_pixmap_col col {
295         .d.pe.grid.l$col configure -state disabled
296     }
297     .d.pe.ok configure -state disabled
298     helptext {{{ Processing }}}
299     manyset [lrange $alloptions [expr {$pixmap_selcol*3}] end] \
300         colname coldesc rows
301     manyset [lrange $rows [expr {$pixmap_selrow*2}] end] \
302         rowname rowdesc
303     set result "$colname - $rowname"
304     debug "UPDATE PIXMAP AS >$result<"
305     set database($ppm) $result
306     write_database
307     done/$mainkind
308 }
309
310 proc required/pixmap {} {
311     global unk_what ppm mulcols alloptions
312     must_gets stdin unk_what
313     debug "GOT pixmap $unk_what"
314     set ppm {}
315     while 1 {
316         must_gets stdin ppml
317         if {![string length $ppml]} break
318         append ppm $ppml "\n"
319     }
320     set data [exec pnmscale 2 << $ppm]
321     image create photo image/main -data $data
322
323     set alloptions [exec ./yppsc-resolver-pixoptions $unk_what]
324
325     read_database ./pixmaps.txt
326
327     set mulcols [image width image/main]
328     set mulrows [image height image/main]
329     resize_widgets_core
330     place forget .d.mi.csr_0
331     place forget .d.mi.csr_1
332
333     pack forget .d.csr .d.got
334     pack .d.pe -side top -before .d.mi -pady 10
335
336     eval destroy [winfo children .d.pe.grid]
337     set col 0; foreach {colname coldesc rows} $alloptions {
338         debug "INIT $col $colname \"$coldesc\""
339         label .d.pe.grid.t$col -text $colname
340         listbox .d.pe.grid.l$col
341         foreach {rowname rowdesc} $rows {
342             debug "INIT $col $colname \"$coldesc\" $rowname \"$rowdesc\""
343             .d.pe.grid.l$col insert end $rowdesc
344         }
345         bind .d.pe.grid.l$col <<ListboxSelect>> [list pixmap_select $col]
346         grid .d.pe.grid.t$col -column $col -row 0
347         grid .d.pe.grid.l$col -column $col -row 1
348         incr col
349     }
350     pixmap_maybe_ok
351     
352     helptext {
353         {{Indicate the correct parse of this image, and click OK.}}
354     }
355 }
356
357 #========== CHARACTER SET ==========
358
359 #---------- xpm input processor ----------
360
361 proc char_read_xpm {f} {
362     global glyphsdone mul inter rhsmost_max unk_l unk_r mulcols mulrows
363     global cols rows wordmap
364     
365     set o {}
366     set y -3
367     while 1 {
368         must_gets $f l
369         if {![regexp {^"(.*)",$} $l dummy l]} {
370             append o "$l\n"
371             if {[regexp {^\}\;$} $l]} break
372             continue
373         }
374         if {$y==-3} {
375             manyset $l cols rows colours cpp
376             if {$colours!=2 || $cpp!=1} { error "$l ?" }
377
378             set chop_l [expr {$unk_l - 80}]
379             set chop_r [expr {$cols - $unk_l - 100}]
380             if {$chop_l<0} { set chop_l 0 }
381
382             set unk_l [expr {$unk_l - $chop_l}]
383             set unk_r [expr {$unk_r - $chop_l}]
384             set ngd {}
385             foreach {min max contexts got} $glyphsdone {
386                 lappend ngd \
387                     [expr {$min-$chop_l}] \
388                     [expr {$max-$chop_l}] \
389                     $contexts $got
390             }
391             set glyphsdone $ngd
392
393             set realcols $cols
394             set cols [expr {$cols - $chop_l - $chop_r}]
395             debug "NOW cols=$cols chop_l,r=$chop_l,$chop_r rows=$rows\
396                 $unk_l $unk_r $ngd"
397             
398             set mulcols [expr {$cols*$mul+$inter}]
399             set mulrows [expr {$rows*$mul+$inter}]
400             append o "\"$mulcols $mulrows 9 1\",\n"
401             for {set x 0} {$x<$cols} {incr x} { set wordmap($x) 0 }
402         } elseif {$y==-2} { # first pixel
403             append o \
404 "\"+ c #111\",
405 \"a c #800\",
406 \"A c #fcc\",
407 \"b c #00c\",
408 \"B c #fff\",
409 \"u c #000\",
410 \"U c #ff0\",
411 \"q c #000\",
412 \"Q c #ff0\",\n"
413         } elseif {$y==-1} { # 2nd pixel but we've already printed ours
414         } else {
415             set ybit [expr {1<<$y}]
416             set x 0
417             set ol "\"+"
418             set olh $ol
419             if {$chop_r>=0} {
420                 set l [string range $l $chop_l end-$chop_r]
421             } else {
422                 set l [string range $l $chop_l end]
423                 append l [string repeat " " [expr -$chop_r]]
424             }
425             foreach c [split $l ""] {
426                 set how "u"
427                 if {$x >= $unk_l && $x <= $unk_r} {
428                     set how q
429                 } else {
430                     set ab 0
431                     foreach {min max contexts got} $glyphsdone {
432                         set rhsmost_max $max
433                         if {$x >= $min && $x <= $max} {
434                             set how [lindex {a b} $ab]
435                             break
436                         }
437                         set ab [expr {!$ab}]
438                     }
439                 }
440                 switch -exact $c {
441                     " " { set p $how }
442                     "o" {
443                         set p [string toupper $how]
444                         incr wordmap($x) $ybit
445                     }
446                     default { error "$c ?" }
447                 }
448                 append ol "[string repeat $p [expr {$mul-$inter}]][
449                          string repeat + $inter]"
450                 append olh [string repeat + $mul]
451                 incr x
452             }
453             set ole "\",\n"
454             append ol $ole
455             append olh $ole
456             set olhn [string repeat $olh $inter]
457             if {!$y} { append o $olhn }
458             append o [string repeat $ol [expr {$mul-1}]]
459             append o $olhn
460         }
461         incr y
462     }
463     set data [exec xpmtoppm << $o]
464     image create photo image/main -data $data
465 }
466
467 #---------- character set editor display ----------
468
469 proc show_context {maxhv x ctxs} {
470     global mul
471     upvar 1 $maxhv maxh
472     set w .d.ctx.at$x
473     if {[llength $ctxs]==1} { set fg blue } { set fg yellow }
474     label $w -bg black -fg $fg -text [join $ctxs "/\n"] -justify left
475     place $w -x [expr {($x-1)*$mul}] -y 0
476     set wh [winfo reqheight $w]
477     if {$wh > $maxh} { set maxh $wh }
478 }
479
480 proc draw_glyphsdone {} {
481     global glyphsdone mul inter
482     eval destroy [winfo children .d.got]
483     foreach {min max contexts got} $glyphsdone {
484         frame .d.got.m$min -bd 0 -background \#888
485         label .d.got.m$min.l -text "$got" -fg white -bg black -bd 0
486         pack .d.got.m$min.l -padx 1 -pady 1
487         place .d.got.m$min -x [expr {$min*$mul+$inter}] -y 0
488     }
489 }
490
491 proc startup_cursor {} {
492     global cur_already cur_mode cur_0 cur_1 last_ht
493     global glyphsdone unk_l unk_r
494     
495     set cur_already [expr {[llength $glyphsdone]/4-1}]
496     set cur_mode 1 ;# one of:   0 1 already text
497
498     set cur_0 $unk_l
499     set cur_1 [expr {$unk_r+1}]
500
501     recursor
502 }
503
504 #---------- character set runtime display and keystroke handling ----------
505
506 proc recursor/0 {} { recursor//01 0 }
507 proc recursor/1 {} { recursor//01 1 }
508 proc recursor//01 {z1} {
509     global mul rhsmost_max cols glyphsdone
510     upvar #0 cur_$z1 cur
511     .d.csr.csr.l configure -text {adjust}
512     place .d.csr.csr -x [expr {$cur*$mul - 7}]
513     bind_key space { othercursor }
514     bind_leftright_q cur_$z1 0 [expr {$cols-1}]
515     if {[llength $glyphsdone]} {
516         bind_key Tab { set cur_mode already; recursor }
517     } else {
518         bind_key Tab {}
519     }
520     bind_key Return {
521         if {$cur_0 != $cur_1} {
522             .d.csr.csr.e delete 0 end
523             set cur_mode text
524             recursor
525         }
526     }
527     helptext {
528         {{<- ->}   {move cursor, adjusting area to define}}
529         {Space     {switch to moving other cursor}}
530         {Return    {confirm location, enter letter(s)}}
531         {Tab       {switch to correcting earlier ocr}}
532         {Q         {quit and abandon OCR run}}
533     }
534 }
535 proc othercursor {} {
536     global cur_mode
537     set cur_mode [expr {!$cur_mode}]
538     recursor
539 }
540
541 proc recursor/text {} {
542     helptext {
543         {Return   {confirm entry of new glyph}}
544         {Escape   {abandon entry}}
545     }
546     unbind_all_keys
547     .d.csr.csr.l configure -text {define:}
548     pack .d.csr.csr.e -side left
549     focus .d.csr.csr.e
550     bind_key Return {
551         set strq [.d.csr.csr.e get]
552         if {[regexp {^(?:[!-[]|[]-~]|\\\\|\\x[0-9a-f]{2})+} $strq]} {
553             RETURN_RESULT DEFINE "$cur_0 $cur_1 $strq"
554         }
555     }
556     bind_key Escape {
557         bind_key Escape {}
558         pack forget .d.csr.csr.e
559         set cur_mode 1
560         recursor
561     }
562 }
563
564 proc recursor/already {} {
565     global mul
566     global glyphsdone
567     global cur_already mul
568     global glyphsdone cur_already mul
569     .d.csr.csr.l configure -text {correct}
570     set rmax [lindex $glyphsdone [expr {$cur_already*4}]]
571     place .d.csr.csr -x [expr {$rmax*$mul-3}]
572     bind_key Return {}
573     bind_key space {}
574     bind_leftright_q cur_already 0 [expr {[llength $glyphsdone]/4-1}]
575     bind_key Tab { bind_key Delete {}; set cur_mode 1; recursor }
576     bind_key Delete {
577         RETURN_RESULT DELETE [lrange $glyphsdone \
578                                   [expr $cur_already*4] \
579                                   [expr $cur_already*4+2]]
580     }
581     helptext {
582         {{<- ->}   {move cursor, selecting glyph to correct}}
583         {Del       {clear this glyph from the recognition database}}
584         {Tab       {switch to selecting area to define as new glyph}}
585         {Q         {quit and abandon OCR run}}
586     }
587 }
588
589 proc bind_leftright_q {var min max} {
590     bind_key Left  [list leftright $var $min $max -1]
591     bind_key Right [list leftright $var $min $max +1]
592     bind_key q     {
593         puts stderr "\nCharacter resolver quitting as you requested."
594         exit 1
595     }
596 }
597 proc leftright {var min max inc} {
598     upvar #0 $var v
599     set vnew $v
600     incr vnew $inc
601     if {$vnew < $min || $vnew > $max} return
602     set v $vnew
603     recursor
604 }
605
606 proc recursor {} {
607     global csrh cur_mode cur_0 cur_1 mul
608     foreach z1 {0 1} {
609         place .d.mi.csr_$z1 -y 0 -x [expr {[set cur_$z1] * $mul}]
610     }
611     recursor/$cur_mode
612 }
613
614 #---------- character database read and write ----------
615
616 # OUT OF DATE
617 # database format:
618 # series of glyphs:
619 #   <context> <ncharacters> <hex>...
620 #   width
621 #   <hex-bitmap>
622
623 # $database($context 0x<bits> 0x<bits>...) = $hex
624
625 set database_magic/char {# ypp-sc-tools pctb font v1}
626     
627 proc read_database_header/char {f} {
628     global rows
629     if {([db_getsl $f])+0 != $rows} { error "wrong h ?" }
630 }
631 proc read_database_entry/char {f context} {
632     global database
633     set bm $context
634     set strq [db_getsl $f]
635     while 1 {
636         set l [db_getsl $f]
637         if {![string length $l]} break
638         lappend bm [format %x 0x$l]
639     }
640     set database($bm) $strq
641 }
642
643 proc write_database_header/char {f} {
644     puts $f "$rows\n"
645 }
646 proc format_database_entry/char {bm strq} {
647     global database rows
648     set o "[lindex $bm 0]\n$strq\n"
649     foreach x [lrange $bm 1 end] { append o "$x\n" }
650     return $o
651 }
652
653 proc dbkey {ctx l r} {
654     global wordmap
655     set bm $ctx
656     for {set x $l} {$x <= $r} {incr x} {
657         lappend bm [format %x $wordmap($x)]
658     }
659     return $bm
660 }
661
662 proc update_database/DEFINE {c0 c1 strq} {
663     global glyphsdone unk_l unk_contexts wordmap database
664     if {$c0 > $c1} { manyset [list $c0 $c1] c1 c0 }
665     if {$c0 == $unk_l} {
666         set ncontexts $unk_contexts
667     } else {
668         foreach {l r contexts got} $glyphsdone {
669             if {$l==$c0} { set ncontexts $contexts; break }
670         }
671         if {![info exists ncontexts]} {
672             puts stderr "must start at letter LHS!"
673             return
674         }
675     }
676     incr c1 -1
677     foreach c $ncontexts {
678         set bm [dbkey $c $c0 $c1]
679         set database($bm) $strq
680     }
681     write_database
682 }
683
684 proc update_database/DELETE {l r ctxs} {
685     global database
686     foreach ctx $ctxs {
687         set bm [dbkey $ctx $l $r]
688         catch { unset database($bm) }
689     }
690     write_database
691 }
692     
693 proc RETURN_RESULT {how what} {
694     global mainkind
695     place forget .d.csr.csr
696     pack forget .d.csr.csr.e
697     helptext {{{ Processing }}}
698     unbind_all_keys
699     update idletasks
700     debug "$how $what"
701     eval update_database/$how $what
702     done/$mainkind
703 }
704
705 #========== server for approving updates ==========
706
707 proc remote-serv-log {pirate event} {
708     set t [clock format [clock seconds] -format {%Y-%m-%d %H:%M:%S %Z}]
709     set s [format "%s %15s %s" $t $pirate $event]
710 }
711
712 proc remote-serv/list {} {
713     global dropdir
714     foreach file [glob -nocomplain -type f -directory $dropdir _update.*.rdy] {
715         puts yes
716         puts $file
717         set f [open $file]
718         set d [read $f]
719         close $f
720         puts [string length $d]
721         puts -nonewline $d
722     }
723     puts end
724 }
725
726 proc remote-serv/take {f args} {
727     global dropdir rows reqkind
728     set rows ""
729     manyset $args yesno file pirate reqkind rows
730     read_counted stdin desc
731     read_counted stdin key
732     read_counted stdin val
733     if {$yesno} {
734         read_database
735         set database($key) $val
736         write_database
737     }
738     set ar [lindex {reject approve} $yesno]
739     remote-serv-log $pirate "$ar $reqkind $rows $desc"
740     file remove $file
741 }
742
743 proc main/remoteserv {} {
744     global argv dropdir
745     manyset $argv dropdir
746     while 1 {
747         puts {ypp-sc-tools pctb remote-server v1}
748         if {[gets stdin l] < 0} break
749         eval remote-serv/$l
750     }
751 }
752
753 #========== main program ==========
754
755 proc main/default {} {
756     puts stderr "Do not run this program directly."
757     exit 12
758 }
759 proc done/default {} {
760 }
761
762 proc required {} {
763     global reqkind
764
765     fileevent stdin readable {}
766     fconfigure stdin -blocking yes
767     
768     if {[gets stdin reqkind]<0} {
769         if {[eof stdin]} { fconfigure stdin -blocking yes; exit 0 }
770         return
771     }
772     init_widgets
773
774     required/$reqkind
775 }
776
777 proc main/automatic {} {
778     fconfigure stdin -blocking no
779     fileevent stdin readable required
780 }
781 proc done/automatic {} {
782     exec sh -c {printf \\0 >&4}
783     main/automatic
784 }
785
786 proc debug {m} { }
787
788 set mainkind default
789 set ai 0
790 foreach arg $argv {
791     incr ai
792     switch -exact -- $arg {
793         {--debug}           { proc debug {m} { puts stderr "SHOW-THING $m" } }
794         {--noop-arg}        { }
795         {--automatic-1}     { set mainkind automatic }
796         {--remote-server-1} { set mainkind remoteserv; break }
797         {--automatic*} - {--remote-server}
798                             { error "incompatible versions - install problem" }
799         default             { error "huh $argv ?" }
800     }
801 }
802 set argv [lrange $argv $ai end]
803
804 main/$mainkind