Bug fix: shell might terminate even though new request was initiated
[egate.git] / www / z39util.tcl
1 #
2 # $Id: z39util.tcl,v 1.12 1995/12/20 16:31:34 adam Exp $
3 #
4 proc saveState {} {
5     uplevel #0 {
6     set f [open "tcl.state.${sessionId}" w]
7     foreach var [info globals] {
8         if {$var == "f"} continue
9         if {$var == "sessionId"} continue
10         if {$var == "errorInfo"} continue
11         if {[catch {set names [array names $var]}]} {
12             eval "set v \$${var}"
13             puts $f "set ${var} \{$v\}"
14         } else {
15             foreach n $names {
16                 eval "set v \$${var}(\$n)"
17                 puts $f "set ${var}($n) \{$v\}"
18             }
19             catch {
20                 eval "set v \$${var}"
21                 puts $f "set ${var} \{$v\}"
22             }
23         }
24     }
25     close $f
26     }
27 }
28
29 proc search-response {zz} {
30     global sessionWait
31
32     set status [$zz responseStatus]
33     if {[lindex $status 0] == "NSD"} {
34         $zz nextResultSetPosition 0
35         set code [lindex $status 1]
36         set msg [lindex $status 2]
37         set addinfo [lindex $status 3]
38         displayError "Diagnostic message" \
39                 "$msg: $addinfo<br>\n(error code $code)"
40         set sessionWait -2
41     } else {
42         set sessionWait 1
43     }
44 }
45
46 proc ok-response {} {
47     global sessionWait
48     set sessionWait 1
49 }
50
51 proc fail-response {} {
52     global sessionWait
53     set sessionWait -1
54 }
55
56 proc display-brief {zset no tno} {
57     global env
58     global setNo
59     global sessionId
60
61     set type [$zset type $no]
62     if {$type == "SD"} {
63         set err [lindex [$zset diag $no] 1]
64         set add [lindex [$zset diag $no] 2]
65         if {$add != {}} {
66             set add " :${add}"
67         }
68         html "${no} Error ${err}${add} <br>\n"
69         return
70     }
71     if {$type != "DB"} {
72         return
73     }
74     html "${no}"
75     set rtype [$zset recordType $no]
76     if {$rtype == "SUTRS"} {
77         html [join [$zset getSutrs $no]]
78         html "<br>\n"
79         return
80     } 
81     if {![catch {
82         set title [lindex [$zset getMarc $no field 245 * a] 0]
83         set year [lindex [$zset getMarc $no field 260 * c] 0]
84     } ] } {
85         html {<a href="http:} $env(SCRIPT_NAME) /
86         html $sessionId {/showfull.egw/} $setNo + $tno + $no + full 
87         html {"> } $title {</a>} " <i> ${year} </i>"
88     }
89     html "<br>\n"
90 }
91
92 proc display-raw {zset no tno} {
93     set type [$zset type $no]
94     if {$type == "SD"} {
95         set err [lindex [$zset diag $no] 1]
96         set add [lindex [$zset diag $no] 2]
97         if {$add != {}} {
98             set add " :${add}"
99         }
100         html "<h3>${no}</h3>\n"
101         html "Error ${err}${add} <br>\n"
102         return
103     }
104     if {$type != "DB"} {
105         return
106     }
107     set rtype [$zset recordType $no]
108     if {$rtype == "SUTRS"} {
109         html [join [$zset getSutrs $no]] "<br>\n"
110         return
111     } 
112     if {[catch {set r [$zset getMarc $no line * * *]}]} {
113         html "Unknown record type: $rtype <br>\n"
114         return
115     }
116     foreach line $r {
117         set tag [lindex $line 0]
118         set indicator [lindex $line 1]
119         set fields [lindex $line 2]
120         set l [string length $indicator]
121         html "<tt>$tag "
122         if {$l > 0} {
123             for {set i 0} {$i < $l} {incr i} {
124                 if {[string index $indicator $i] == " "} {
125                     html "-"
126                 } else {
127                     html [string index $tag $i]
128                 }
129             }
130         }
131         html "</tt>"
132         foreach field $fields {
133             set id [lindex $field 0]
134             set data [lindex $field 1]
135             if {$id != ""} {
136                 html " <b>\$$id</b> "
137             }
138             html $data
139         }
140         htmlr {<br>}
141     }
142 }
143
144 proc put-marc-contents {cc} {
145     set ref ""
146     if {[string first :// $cc] > 0} {
147         foreach urltype {gopher http ftp mailto} {
148             if {[string first ${urltype}:// $cc] == 0} {
149                 set ref $urltype
150                 break
151             }
152         }
153     } 
154     if {$ref != ""} {
155         html {<a href="}
156     }
157     html $cc
158     if {$ref != ""} {
159         html {">} $cc {</a>}
160     }
161 }
162
163 proc dl-marc-field {zset no tag id la lb sep} {
164     set n 0
165     set c [$zset getMarc $no field $tag * $id]
166     set len [llength $c]
167     if {$len == 0} {
168         return 0
169     }
170     if {$len > 1 && "x$lb" != "x"} {
171         html "<dt><b>$lb</b>\n<dd>"
172     } else {
173         html "<dt><b>$la</b>\n<dd>"
174     }
175     foreach cc $c {
176         if {$n > 0} {
177             html $sep
178         }
179         put-marc-contents $cc
180         incr n
181     }
182     return $n
183 }
184
185 proc dd-marc-field {zset no tag id start stop} {
186     set n 0
187     set c [$zset getMarc $no field $tag * $id]
188     set len [llength $c]
189     if {$len == 0} {
190         return 0
191     }
192     foreach cc $c {
193         html $start
194         put-marc-contents $cc
195         html $stop
196         incr n
197     }
198     return $n
199 }
200
201 proc dl-marc-field-rec {zset no tag lead start stop startid sep} {
202     set n 0
203     set lines [$zset getMarc $no line $tag * *]
204     foreach line $lines {
205         foreach field [lindex $line 2] {
206             if {$n == 0} {
207                 html "<dt><b>$lead</b>"
208                 html "\n<dd>"
209             }
210             set id [lindex $field 0]
211             if {$id == $startid} {
212                 if {$n > 0} {
213                     html $stop
214                 }
215                 html $start
216                 incr n
217                 html [lindex $field 1]
218             } else {
219                 html $sep
220                 html [lindex $field 1]
221             }
222         }
223     }
224     if {$n > 0} {
225         html $stop
226     }
227 }
228
229 proc display-full {zset no tno} {
230     set type [$zset type $no]
231     if {$type == "SD"} {
232         set err [lindex [$zset diag $no] 1]
233         set add [lindex [$zset diag $no] 2]
234         if {$add != {}} {
235             set add " :${add}"
236         }
237         html "Error ${err}${add} <br>\n"
238         return
239     }
240     if {$type != "DB"} {
241         return
242     }
243     set rtype [$zset recordType $no]
244     if {$rtype == "SUTRS"} {
245         html [join [$zset getSutrs $no]] "<br>\n"
246         return
247     } 
248     if {[catch {set r [$zset getMarc $no line * * *]}]} {
249         html "Unknown record type: $rtype <br>\n"
250         return
251     }
252     html "<dl>\n"
253     set n [dl-marc-field $zset $no 700 a "Author" "Authors" "<br>\n"]
254     if {$n == 0} {
255         set n [dl-marc-field $zset $no 100 a "Author" "Authors" "<br>\n"]
256     }
257     set n [dl-marc-field $zset $no 710 a "Corporate Name" {} ", "]
258     if {$n == 0} {
259         set n [dl-marc-field $zset $no 710 a "Corporate Name" {} ", "]
260     }
261     set n [dl-marc-field $zset $no 245 {a} "Title" {} " "]
262     if {$n > 0} {
263         dd-marc-field $zset $no 245 b "<em>" "</em>"
264         dd-marc-field $zset $no 245 c " " ""
265     } else {
266         dl-marc-field $zset $no 245 {[ab]} "Title" {} " "
267     }
268     dl-marc-field $zset $no 520 a "Abstract" {} ", "
269     dl-marc-field $zset $no 072 * "Subject code" "Subject codes" ", "
270     dl-marc-field $zset $no 650 * "Subject" {} ", "
271     dl-marc-field $zset $no 260 * "Publisher" {} " "
272     dl-marc-field $zset $no 300 * "Physical Description" {} " "
273
274     dl-marc-field-rec $zset $no 500 "Notes" "" "<br>\n" "a" ", "
275
276     dl-marc-field-rec $zset $no 510 "References" "" "<br>\n" "a" ", "
277
278     dl-marc-field-rec $zset $no 511 "Participant note" "" "<br>\n" "a" ", "
279
280     dl-marc-field $zset $no 513 a "Report type" {} ", "
281     dl-marc-field $zset $no 513 b "Period covered" {} ", "
282     dl-marc-field-rec $zset $no 515 "Numbering notes" "" "<br>\n" "a" ", "
283     dl-marc-field-rec $zset $no 516 "Data notes" "" "<br>\n" "a" ", "
284     dl-marc-field-rec $zset $no 518 "Date/time notes" "" "<br>\n" "a" ", "
285
286     dl-marc-field $zset $no 350 a "Price" {} ", "
287     dl-marc-field $zset $no 362 a "Dates of publication" {} ", "
288     dl-marc-field $zset $no 850 a "Holdings" {} ", "
289
290     dl-marc-field-rec $zset $no 270 "Contact name" "" "<br>\n" p ", "
291     if {0} {
292         set n [dl-marc-field $zset $no 270 p "Contact name" {} ", "]
293         if {$n > 0} {
294             html "\n<dl>\n"
295             
296             if {0} {
297                 dl-marc-field $zset $no 270 a "Street" {} ", "
298                 dl-marc-field $zset $no 270 b "City" {} ", "
299                 dl-marc-field $zset $no 270 c "State" {} ", "
300                 dl-marc-field $zset $no 270 e "Zip code" {} ", "
301                 dl-marc-field $zset $no 270 d "Country" {} ", "
302                 dl-marc-field $zset $no 270 m "Network address" {} ", "
303                 dl-marc-field $zset $no 301 a "Service hours" {} ", "
304                 dl-marc-field $zset $no 270 k "Phone" {} ", "
305                 dl-marc-field $zset $no 270 l "Fax" {} ", "
306             } else {
307                 dl-marc-field $zset $no 270 {[abcedmakl]} "Address" {} "<br>\n"
308             }
309             
310             html "\n</dl>\n"
311         }
312     }
313     dl-marc-field $zset $no 010 a "LC control number" {} ", "
314     dl-marc-field $zset $no 010 b "NUCMC control number" {} ", "
315     dl-marc-field $zset $no 020 a "ISBN" {} ", "
316     dl-marc-field $zset $no 022 a "ISSN" {} ", "
317     set url [$zset getMarc $no field 856 * u]
318     set sp [$zset getMarc $no field 856 * 3]
319     if {"x$url" != "x"} {
320         html "<dt><b>URL</b>\n"
321         if {"x$sp" == "x"} {
322             set sp reference
323         }
324         html {<dd><a href="} $url {">} [join $sp] "</a>\n"
325     }
326     dl-marc-field $zset $no 037 {[abc]} "Acquisition" {} "<br>\n"
327     dl-marc-field $zset $no 037 {[f6]} "Form of issue" {} "<br>\n"
328     dl-marc-field $zset $no 537 * "Source of data" {} "<br>\n"
329     dl-marc-field $zset $no 538 * "System details" {} "<br>\n"
330     dl-marc-field $zset $no 787 {[rstw6]} "Related information" {} "<br>\n"
331     dl-marc-field $zset $no 001 * "Local control number" {} ", "
332     html "</dl>\n"
333 }
334
335
336 proc display-rec {from to dfunc tno} {
337     global setNo
338
339     if {$tno > 0} {
340         while {$from <= $to} { 
341             eval "$dfunc z39${tno}.${setNo} $from $tno"
342             incr from
343         }
344     } else {
345         while {$from <= $to} { 
346             eval "$dfunc z39.${setNo} $from 0"
347             incr from
348         }
349     }
350 }
351
352 proc build-query {t} {
353     global targets
354
355     set op {}
356     set q {}
357     for {set i 1} {$i < 4} {incr i} {
358         set term [wform entry$i]
359         if {$term != ""} {
360             set field [wform menu$i]
361             foreach x [lindex $targets($t) 2] {
362                 if {[lindex $x 0] == $field} {
363                     set attr [lindex $x 1]
364                 }
365             }
366             switch $op {
367             And
368                 { set q "@and $q ${attr} ${term}" }
369             Or
370                 { set q "@or $q ${attr} ${term}" }
371             {And not}
372                 { set q "@not $q ${attr} ${term}" }
373             {}
374                 { set q "${attr} ${term}" }
375             }
376             set op [wform logic$i]
377         }
378     }
379     return $q
380 }
381
382 proc z39search {setNo piggy tno elements} {
383     global hist
384     global sessionWait
385     global targets
386
387     if {$tno > 0} {
388         set zz z39$tno
389         set host $hist($setNo,$tno,host)
390         set idAuth $hist($setNo,$tno,idAuthentication)
391         set database $hist($setNo,$tno,database)
392         set query $hist($setNo,$tno,query)
393     } else {
394         set zz z39
395         set host $hist($setNo,host)
396         set idAuth $hist($setNo,idAuthentication)
397         set database $hist($setNo,database)
398         set query $hist($setNo,query)
399     }
400     if {[catch [list $zz failback fail-response]]} {
401         ir $zz
402     }
403     if {[catch [list set oldHost [$zz connect]]]} {
404         set oldHost ""
405     }
406     $zz callback ok-response
407     $zz failback fail-response
408     if {$oldHost != $host} {
409         catch [list $zz disconnect]
410
411         set sessionWait 0
412         if {[catch [list $zz connect $host]]} {
413             displayError "Cannot connect to target" $host
414             return 0
415         } elseif {$sessionWait == 0} {
416             if {[catch {zwait sessionWait 300}]} {
417                 $zz disconnect
418                 displayError "Cannot connect to target" $host
419                 return 0
420             }
421             if {$sessionWait != 1} {
422                 displayError "Cannot connect to target" $host
423                 return 0
424             }
425         }
426         $zz idAuthentication $idAuth
427         set sessionWait 0
428         if {[catch {$zz init}]} {
429             displayError "Cannot initialize target" $host
430             $zz disconnect
431             return 0
432         }
433         if {[catch {zwait sessionWait 60}]} {
434             displayError "Cannot initialize target" $host
435             $zz disconnect
436             return 0
437         }
438         if {$sessionWait != "1"} {
439             displayError "Cannot initialize target" $host
440             $zz disconnect
441             return 0
442         }
443         if {![$zz initResult]} {
444             set u [$zz userInformationField]
445             $zz disconnect
446             displayError "Cannot initialize target $host" $u
447             return 0
448         }
449     } else {
450         if {![catch [list $zz.$setNo smallSetUpperBound 0]]} {
451             return 1
452         }
453     }
454     ir-set $zz.$setNo $zz
455     
456     if {![lindex $targets($host) 5]} {
457         set elements {}
458     }
459     $zz.$setNo smallSetElementSetNames $elements
460     $zz.$setNo mediumSetElementSetNames $elements
461     $zz.$setNo recordElements $elements
462
463     wlog debug "database=$database"
464     eval $zz.$setNo databaseNames $database
465
466     $zz.$setNo preferredRecordSyntax USMARC
467
468     $zz callback [list search-response $zz.$setNo]
469     if {$piggy} {
470         $zz.$setNo largeSetLowerBound 999999
471         $zz.$setNo smallSetUpperBound 0
472         $zz.$setNo mediumSetPresentNumber $hist($setNo,maxPresent)
473     } else {
474         $zz.$setNo largeSetLowerBound 2
475         $zz.$setNo smallSetUpperBound 0
476         $zz.$setNo mediumSetPresentNumber 0
477     }
478     set sessionWait 0
479     $zz.$setNo search $query
480
481     if {[catch {zwait sessionWait 600}]} {
482         wlog debug "timeout/cancel in search"
483         displayError "Timeout in search" {}
484         html "</body></html>\n"
485         $zz disconnect
486         return 0
487     }
488         
489     if {$sessionWait == -1} {
490         displayError "Search fail" "Connection closed"
491         html "</body></html>\n"
492         $zz disconnect
493     }
494     if {$sessionWait != 1} {
495         return 0
496     }
497     set hist($setNo,hits) [$zz.$setNo resultCount]
498     return 1
499 }
500
501 proc init-m-response {i} {
502     global zstatus
503     global zleft
504
505     wlog debug "init-m-response"
506
507     set zstatus($i) 1
508     incr zleft -1
509 }
510
511 proc connect-m-response {i} {
512     global zstatus
513     global zleft
514
515     wlog debug "connect-m-response"
516     z39$i callback [list init-m-response $i]
517     if {[catch {z39$i init}]} {
518         set zstatus($i) -1
519         incr zleft -1
520     }
521 }
522
523 proc fail-m-response {i} {
524     global zstatus
525     global zleft
526     
527     wlog debug "fail-m-response"
528     set zstatus($i) -1
529     incr zleft -1
530 }
531
532 proc search-m-response {setNo i} {
533     global zleft
534     global zstatus
535
536     incr zleft -1
537     set zstatus($i) 2
538 }
539
540 proc z39msearch {setNo piggy elements} {
541     global zleft
542     global zstatus
543     global hist
544     global targets
545
546     set not $hist($setNo,0,host)
547
548     for {set i 1} {$i <= $not} {incr i} {
549         set host $hist($setNo,$i,host)
550         if {[catch {z39 failback fail-response}]} {
551             ir z39$i
552         }
553         if {[catch {set oldHost [z39$i connect]}]} {
554             set oldHost ""
555         }
556         if {$oldHost != $host} {
557             catch {z39$i disconnect}
558         }
559         z39$i callback [list connect-m-response $i]
560         z39$i failback [list fail-m-response $i]
561     }
562     set zleft 0
563     for {set i 1} {$i <= $not} {incr i} {
564         set oldHost [z39$i connect]
565         set host $hist($setNo,$i,host)
566         if {$oldHost == $host} {
567             set zstatus($i) 1
568             continue
569         }
570         z39$i idAuthentication $hist($setNo,$i,idAuthentication)
571         html "Connecting to target " $host " <br>\n"
572         set zstatus($i) -1
573         if {![catch {z39$i connect $host}]} {
574             incr zleft
575         } 
576     }
577     while {$zleft > 0} {
578         wlog debug "Waiting for init response"
579         if {[catch {zwait zleft 10}]} {
580             break
581         }
582     }
583     set zleft 0
584     for {set i 1} {$i <= $not} {incr i} {
585         html "host " $hist($setNo,$i,host) ": "
586         if {$zstatus($i) >= 1} {
587             html "ok <br>\n"
588             ir-set z39$i.$setNo z39$i
589             set hist($setNo,$i,offset) 0
590             eval z39$i.$setNo databaseNames $hist($setNo,$i,database)
591
592             if {![lindex $targets($hist($setNo,$i,host)) 5]} {
593                 set thisElements {}
594             } else {
595                 set thisElements $elements
596             }
597             z39$i.$setNo smallSetElementSetNames $thisElements
598             z39$i.$setNo mediumSetElementSetNames $thisElements
599             z39$i.$setNo recordElements $thisElements
600
601             z39$i.$setNo preferredRecordSyntax USMARC
602             z39$i callback [list search-m-response $setNo $i]
603
604             if {$piggy} {
605                 z39$i.$setNo largeSetLowerBound 999999
606                 z39$i.$setNo smallSetUpperBound 0
607                 z39$i.$setNo mediumSetPresentNumber $hist($setNo,maxPresent)
608             } else {
609                 z39$i.$setNo largeSetLowerBound 2
610                 z39$i.$setNo smallSetUpperBound 0
611                 z39$i.$setNo mediumSetPresentNumber 0
612             }
613             set zstatus($i) 1
614             wlog debug "search " $hist($setNo,$i,query)
615             z39$i.$setNo search $hist($setNo,$i,query)
616             incr zleft
617         } else {
618             html "fail <br>\n"
619         }
620     }
621     while {$zleft > 0} {
622         wlog debug "Waiting for search response"
623         if {[catch {zwait zleft 30}]} {
624             break
625         }
626     }
627     for {set i 1} {$i <= $not} {incr i} {
628         if {$zstatus($i) != 2} continue
629         set status [z39$i.$setNo responseStatus]
630         if {[lindex $status 0] != "NSD"} {
631             set hist($setNo,$i,offset) [z39$i.$setNo numberOfRecordsReturned]
632         }
633     }
634 }
635
636 proc z39present {setNo tno setOffset setMax dfunc elements} {
637     global hist
638     global sessionWait
639     global targets
640
641     if {$tno > 0} {
642         set zz z39$tno
643         set host $hist($setNo,$tno,host)
644     } else {
645         set zz z39
646         set host $hist($setNo,host)
647     }
648
649     if {![lindex $targets($host) 5]} {
650         set elements {}
651     }
652
653     $zz.$setNo elementSetNames $elements
654     $zz.$setNo recordElements $elements
655     set toGet [expr 1 + $setMax - $setOffset]
656
657     $zz callback [list search-response $zz.$setNo]
658
659     while {$setMax > 0 && $toGet > 0} {
660         for {set got 0} {$got < $toGet} {incr got} {
661             if {[$zz.$setNo type [expr $setOffset + $got]] == ""} {
662                 break
663             }
664         }
665         if {$got < $toGet} {
666             set sessionWait 0
667             $zz.$setNo present $setOffset $toGet
668             if {[catch {zwait sessionWait 300}]} {
669                 wlog debug "timeout/cancel in present"
670                 $zz disconnect
671                 break
672             }
673             if {$sessionWait == "0"} {
674                 $zz disconnect
675             }
676             if {$sessionWait != "1"} {
677                 break
678             }
679             set got [$zz.$setNo numberOfRecordsReturned]
680             if {$got <= 0} {
681                 break
682             }
683         }
684         display-rec $setOffset [expr $got + $setOffset - 1] $dfunc $tno
685         set setOffset [expr $got + $setOffset]
686         set toGet [expr 1 + $setMax - $setOffset]
687         wflush
688     }
689 }
690
691 proc z39history {} {
692     global nextSetNo
693     global hist
694     global env
695     global sessionId
696     global targets
697
698     if {![info exists nextSetNo]} {
699         return
700     }
701     html "<hr><h3>History</h3><dl>\n"
702     for {set setNo 1} {$setNo < $nextSetNo} {incr setNo} {
703         html {<dt> <a href="http:} $env(SCRIPT_NAME)
704         html / $sessionId {/search.egw/} $setNo + 1
705         html + $hist($setNo,maxPresent)
706         html {"> } [lindex $targets($hist($setNo,host)) 0]
707         if {[llength $hist($setNo,database)] > 1} {
708             html ": "
709             foreach b $hist($setNo,database) {
710                 html " $b"
711             }
712         }
713         html "</a>\n"
714         html "<dd> "
715         if {[info exists hist($setNo,hits)]} {
716             html $hist($setNo,hits) " hits"
717         } else {
718             html failed
719         }
720         html "\n"
721     }
722     html "</dl>\n"
723 }
724
725 proc displayError {msga msgb} {
726     html "<p><center>\n"
727     html {<img src="/gif/noway.gif">}
728     html "<h2>" $msga "</h2>\n"
729     if {$msgb != ""} {
730         html "<h3>" $msgb "</h3>\n"
731     }
732     html "</center><p>\n"
733 }
734
735 set useIcons 1