3 # openisis - an open implementation of the CDS/ISIS database
4 # Version 0.8.x (patchlevel see file Version)
5 # Copyright (C) 2001-2003 by Erik Grziwotz, erik@openisis.org
7 # This library is free software; you can redistribute it and/or
8 # modify it under the terms of the GNU Lesser General Public
9 # License as published by the Free Software Foundation; either
10 # version 2.1 of the License, or (at your option) any later version.
12 # This library is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 # Lesser General Public License for more details.
17 # You should have received a copy of the GNU Lesser General Public
18 # License along with this library; if not, write to the Free Software
19 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 # see README for more information
25 # $Id: openIsis.tcl,v 1.11 2003/06/05 13:34:59 mawag Exp $
26 # the openIsis package
29 package require Tcl 8.3
31 package provide openIsis 0.8.7
33 namespace eval openIsis {
34 namespace export puts puth putu MHL MHLl isis res v save
35 array set put {= s \" h ? u}
36 set umap [list \n =0A { } + & =26 = =3D ? =3F]
37 set srv "" ;# current server
38 array set fsp {db "" proc ""} ;# cache field selection procedure
39 set ver 0 ;# verbosity
43 # create Tcl code for Tcl Server Page
44 # the returned code is usually evaluated in a namespace
45 proc openIsis::Tspc {tsp {intro ""}} {
47 # vars global and main hold global and main code, resp
50 # which section we are in
53 set lit {$j >= $i};# condition for having a literal
54 while {"#" == [string index $tsp $i]} {
55 set i [string first "\n" $tsp $i]
56 if {0 > $i} { error "unterminated line" }
60 # we're in literal (outside <% %>) mode
61 if {0 <= [set j [string first <% $tsp $i]]} {
65 set lit {$i <= [string length $tsp]}
68 append $sect "puts " [list [string range $tsp $i $j]] "\n"
70 if {"end" == $j} break
71 # have <% code, j was last char before
73 if {0 > [set i [string first %> $tsp $j]]} {
74 error "unbalanced <% at pos $j '[string range $tsp $j [expr $i+80]]'..."
77 switch -- [set type [string index $tsp $j]] \
78 - { # comment -- actually should start --
79 # extend to end of comment, skipping all intervening %>
80 if {0 > [set i [string first --%> $tsp $j]]} {
81 error "unbalanced <%-- at pos $j ..."
87 set expr [string range $tsp $j $i]
88 # apply some magic guessing
89 if [regexp {^[.\-\d]} $expr] { # looks like v get args
90 append $sect put$put($type) { [lindex [v get } $expr "] 0]\n"
91 } elseif [regexp {^[\w\d:]+($|\()} $expr] { # looks like variable
92 append $sect "put$put($type) \"\$" $expr "\"\n"
93 } elseif [regexp {^\w} $expr] { # looks like command
94 append $sect put$put($type) { [} $expr "]\n"
96 append $sect "put$put($type) \"" $expr "\"\n"
100 error "@-section is meaningless with TSPs"
103 if [set globlit [expr {"!" == [string index $tsp $i]}]] {
112 append $sect [string range $tsp $j $i] "\n"
131 proc openIsis::puts {str} {
132 ::puts -nonewline $str
135 proc openIsis::2html {str} { # substitution of very important entities
136 string map {& & < < > > \" "} $str
139 proc openIsis::puth {str} {
143 proc openIsis::2url {str} {
145 regsub -all {[\0-\37]} [
146 string map $umap $str
151 proc openIsis::putu {str} {
155 # run tspfile, compiling if necessary
157 proc openIsis::Tspr {tspfile} {
160 set mtime [file mtime $tspfile]
162 if {[set ::tsp::${tspfile}::mtime] == $mtime} { set compile 0 }
165 catch {namespace delete ::tsp::$tspfile}
166 set f [open $tspfile]
167 namespace eval ::tsp::$tspfile [Tspc [read $f] "
168 variable mtime $mtime
169 namespace import ::openIsis::*
173 ::puts stderr "compiled mtime [set ::tsp::${tspfile}::mtime]
174 [info body ::tsp::${tspfile}::main]"
177 ::tsp::${tspfile}::main
180 proc openIsis::MHL {str} {
182 if [info exists env(ENCODING)] {
183 set str [encoding convertfrom $env(ENCODING) $str]
185 # ::puts stderr "MHLing '$str'"
186 regsub -all {(<[^=>]*)=[^>]+>} $str {\1>} str; # dump <a=b> substitutions
187 regsub -all {><} $str {; } str; # replace >< pairs
188 regsub -all {[><]} $str {} str; # nuke other ><
189 regsub {^\^.} $str {} str; # kill initial subfield spec
190 regsub -all {\^a} $str {; } str; # ^a -> ;
191 regsub -all {\^[b-i]} $str {, } str; # ^[b-i] -> ,
192 regsub -all {\^.} $str {. } str; # others -> .
196 proc openIsis::MHLl {list} {
199 lappend ret [MHL $str]
204 # a procedure to make live with requests easier:
205 # it uses the current value of the $srv server variable to send a request
206 # so we can switch servers by setting srv, yet use a single command
207 proc openIsis::isis {request args} {
211 $srv .req set -type $request
212 # eval so that the single list of args becomes multiple parameters
215 ::puts stderr "###req
216 [$srv .req serialize]###res
217 [$srv .res serialize]###"
219 # return the size (number of records or mfns)
220 $srv .res get {size 0}
223 # convenient access to the result
224 proc openIsis::res {args} {
229 # wrapper for current result record
230 # add a "get first", if args is a single integer
231 proc openIsis::v {args} {
233 # if {"rowid" == $args} { return [$srv .res get {rowid 0}] }
234 if {[string is integer $args]} { # v 24
235 return [$srv .res .rec get "$args {}"]
237 eval $srv .res .rec $args
241 # write record rec as mfn to server srv
242 proc openIsis::save {{rec rec} {mfn 0}} {
246 set dbpath [$srv get 5]/[$srv get 721] ;# syspath/db
247 if {$fsp(db) != $dbpath} { # check for fsp
249 if {[catch {source $dbpath.fsp} fsp(proc)]
250 || "" == [info procs $fsp(proc)]} {
251 ::puts stderr "fsp $fsp(proc) : $::errorInfo"
254 ::puts stderr "using fsp $fsp(proc)"
258 if {"" != $fsp(proc)} {
259 $srv new ::openIsis::idx
261 if $mfn { # delete entries for old version
262 isis read -rowid $mfn
263 idx add -1 del ;# set delete mode
264 $fsp(proc) ::openIsis::v ::openIsis::idx
265 idx add -1 add ;# back to add mode
267 # add new index entries
268 $fsp(proc) $rec ::openIsis::idx
269 ::puts stderr "index entries:\n[idx serialize]"
270 $srv .req wrap -tag 926 ::openIsis::idx
273 $srv .req set 904 $mfn
275 # wrap the record into request
276 $srv .req wrap -tag 908 $rec
278 ::puts stderr "write:\n[$srv .req serialize]"
280 $srv req -type update -rowid $mfn
282 $srv req -type insert
288 proc openIsis::prnres {msg stb} {
290 set res [$stb .res get sid ser error error2]
293 catch { set tms [$stb .res get tms] }
294 catch { set dta [$stb .res get rec] }
295 ::puts stderr "\[$stb/[lindex $res 0]] $msg:\
296 ser [lindex $res 1],\
297 err [lindex $res 2]/[lindex $res 3],\
301 ::puts stderr "\[$stb] $msg: no response available"
305 proc openIsis::errno { stb } {
306 if { ![ $stb .res ] } { return 0 }
307 set err [$stb .res get error]
308 if { $err } { return $err }
309 set err [$stb .res get error2]
313 proc openIsis::maxrow {stb db wrn} {
315 $stb req type maxrow db $db
316 prnres "maxrow $db" $stb
319 set row [$stb .res get rowid]
323 ::puts stderr $errorInfo
326 # puts stderr "maxrow = $row"
331 # ######################################################################
333 namespace eval openIsis::util {
335 # construct fdt from list
337 set fdt [openIsisRec]
340 set fd [eval openIsisRec \{\} set $ent]
341 $fdt wrap -tag 881 -done $fd
348 # build list of records
349 proc buildlst {lst {emb {}} {fdt {}}} {
350 if [string length $fdt] {
355 set rec [eval openIsisRec \{\} $fdt]
356 foreach {tag val} "$ent" {
357 if {0 <= [lsearch $emb $tag]} {
358 set rec2 [eval openIsisRec \{\} add $val]
359 $rec wrap -done $tag $rec2
361 $rec add -ignore $tag $val
369 # split recfield into list of subfield's char and value
373 while {0 <= [set e [string first ^ $fld]]} {
374 set val [string range $fld 0 [expr $e - 1]]
375 set nc [string index $fld [incr e]]
376 if ![string length $nc] {
379 if {[string length $c] || [string length $val]} {
380 lappend lst "$c" "$val"
383 set fld [string range $fld [incr e] end]
385 lappend lst "$c" "$fld"
391 set num [$fdt get 880]
393 for {set j 0} {$num > $j} {incr j} {
394 set fd [$fdt .881\[$j\]]
395 if {15 == [$fd get 862]} {
396 lappend res [$fd get 860]
404 proc _tgttag {tag rel} {
413 # make a deep record copy
414 # @param src command of source record
415 # @param tgt target record, optional
416 # @param fdt fdt of target record, optional
417 # @param rel relation between source and target record tags
418 # if empty, source tags are taken over unmodified
419 # @return target record
420 proc deepcopy {src {tgt {}} {fdt {}} {rel {}}} {
421 if [catch {$tgt row}] {
422 if [string length $fdt] {
423 set tgt [openIsisRec -fdt $fdt]
425 set tgt [openIsisRec]
428 set hasr [llength $rel]
431 set subrecs [_rec2subs $src]
433 foreach {t v} [$src get -tags] {
438 if {$subend == [incr subcnt]} {
440 $tgt wrap -tag $subtag -done $emb
446 if {0 <= [lsearch $subrecs $t]} {
450 set subtag [_tgttag $t "$rel"]
454 set hast [expr 0 <= $subtag]
456 set emb [openIsisRec]
461 set tt [_tgttag $t "$rel"]
466 $tgt add -ignore $tt $v
474 if ![catch { $emb done }] {
476 catch { set lst [$src get -tags] }
477 puts "deepcopy: corrupted source: $lst"