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