1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316
|
#!/bin/sh
# -*- tcl -*- \
exec tclsh8.3 "$0" "$@"
# flatten.tcl --
#
# Parse a DTD, resolve all external entities, parameter
# entities and conditional sections and save the result.
#
# Copyright (c) 2000 Zveno Pty Ltd
# http://www.zveno.com/
#
# Zveno makes this software and all associated data and documentation
# ('Software') available free of charge for any purpose.
# Copies may be made of this Software but all of this notice must be included
# on any copy.
#
# The Software was developed for research purposes and Zveno does not warrant
# that it is error free or fit for any purpose. Zveno disclaims any
# liability for all claims, expenses, losses, damages and costs any user may
# incur as a result of using, copying or modifying the Software.
#
# CVS: $Id: flatten.tcl,v 1.2 2000/05/19 23:56:20 steve Exp $
# Allow the script to work from the source directory
set auto_path [linsert $auto_path 0 [file dirname [file dirname [file join [pwd] [info script]]]]]
# We need TclXML
package require xml 2.0
# Process --
#
# Parse a XML document or DTD and emit result
#
# Arguments:
# data XML text
# type "xml" or "dtd"
# out output channel
# args configration options
#
# Results:
# Data is parsed and flattened DTD written to output channel
proc Process {data type out args} {
global elementDeclCount PEDeclCount AttListDeclCount CommentCount
global config
set elementDeclCount [set PEDeclCount [set AttListDeclCount [set CommentCount 0]]]
# Create the parser object.
# We want to use the Tcl-only parser for this application,
# because it can resolve entities without doing full
# validation.
set parser [eval ::xml::parser \
-elementstartcommand ElementStart \
-validate 1 \
$args \
]
if {$config(wantElementDecls)} {
$parser configure -elementdeclcommand [list ElementDeclaration $out]
}
if {$config(wantPEDecls)} {
$parser configure -parameterentitydeclcommand [list PEDecl $out]
}
if {$config(wantAttListDecls)} {
$parser configure -attlistdeclcommand [list AttListDecl $out]
}
if {$config(wantComments)} {
$parser configure -commentcommand [list Comment $out]
}
switch $type {
xml {
# Proceed with normal parsing method
$parser parse $data
}
dtd {
# Use the DTD parsing method instead
$parser parse $data -dtdsubset external
}
}
# Clean up parser object
#$parser free
#rename $parser {}
return {}
}
# ElementStart --
#
# Callback for the start of an element.
#
# Arguments:
# name tag name
# attlist attribute list
# args other information
#
# Results:
# Returns break error code, since we don't
# care about the document instance, only the DTD
proc ElementStart {name attlist args} {
return -code break
}
# ElementDeclaration --
#
# Callback for an element declaration.
#
# Arguments:
# out output channel
# name tag name
# cmodel content model specification
#
# Results:
# Writes element declaration to output channel
proc ElementDeclaration {out name cmodel} {
global elementDeclCount
incr elementDeclCount
regsub -all "\[ \t\n\r\]+" $cmodel { } cmodel
puts $out "<!ELEMENT $name $cmodel>"
return {}
}
# PEDecl --
#
# Callback for a parameter entity declaration.
#
# Arguments:
# out output channel
# name PE name
# repl replacement text
#
# Results:
# Writes info to stderr
proc PEDecl {out name repl args} {
global PEDeclCount
incr PEDeclCount
if {[llength $args]} {
puts $out "<!ENTITY % $name PUBLIC \"[lindex $args 0]\" \"$repl\">"
} else {
puts $out "<!ENTITY % $name \"[string trim $repl]\">"
}
return {}
}
# AttListDecl --
#
# Callback for an attribute list declaration.
#
# Arguments:
# out output channel
# name element name
# attname attribute name
# type attribute definition type
# dflt default type
# dfltval default value
#
# Results:
# Writes info to stderr
proc AttListDecl {out name attname type dflt dfltval} {
global AttListDeclCount
incr AttListDeclCount
puts $out "<!ATTLIST $name $attname $type $dflt $dfltval>"
return {}
}
# Comment --
#
# Callback for a comment.
#
# Arguments:
# out output channel
# data comment data
#
# Results:
# Writes info to stderr
proc Comment {out data} {
global CommentCount
incr CommentCount
puts $out "<!--${data}-->"
return {}
}
# Open --
#
# Manage opening document in GUI environment
#
# Arguments:
# None
#
# Results:
# XML or DTD document opened and parsed
proc Open {} {
global currentDir status
set filename [tk_getOpenFile -parent . -title "Open Document" -initialdir $currentDir -defaultextension ".xml" -filetypes {
{{XML Documents} {.xml} }
{{DTD Files} {.dtd} }
{{All File} * }
}]
if {![string length $filename]} {
return {}
}
set currentDir [file dirname $filename]
set savename [file join [file rootname $filename].dtd]
set savename [tk_getSaveFile -parent . -title "Save DTD" -initialdir $currentDir -initialfile $savename -defaultextension ".dtd" -filetypes {
{{XML Documents} {.xml} }
{{DTD Files} {.dtd} }
{{All File} * }
}]
if {![string length $savename]} {
return {}
}
set status Processing
set oldcursor [. cget -cursor]
. configure -cursor watch
grab .elementDecls
update
set ch [open $filename]
set out [open $savename w]
if {[catch {Process [read $ch] [expr {[file extension $filename] == ".dtd" ? "dtd" : "xml"}] $out -baseurl file://[file join [pwd] $filename]} err]} {
tk_messageBox -message [format [mc {Unable to process document "%s" due to "%s"}] $filename $err] -icon error -default ok -parent . -type ok
} else {
tk_messageBox -message [mc "DTD Saved OK"] -icon info -default ok -parent . -type ok
}
close $ch
close $out
set status {}
grab release .elementDecls
. configure -cursor $oldcursor
return {}
}
### Main script
# Initialize message catalog, in case it is used
package require msgcat
namespace import msgcat::mc
catch {::msgcat::mcload [file join [file dirname [info script]] msgs]}
# Usage: flatten.tcl file1 file2 ...
# "-" reads input from stdin
# No arguments - Tk means read from stdin
# Files read from stdin assumed to be XML documents
# When given files to read, all output goes to stdout
# No arguments + Tk means use GUI
switch [llength $argv] {
0 {
if {![catch {package require Tk}]} {
# Create a nice little GUI
array set config {wantElementDecls 1 wantPEDecls 0 wantAttlistDecls 1 wantComments 0}
checkbutton .wantElementDecls -variable config(wantElementDecls)
label .elementDeclLabel -text [mc "Element declarations:"]
label .elementDecls -textvariable elementDeclCount
checkbutton .wantPEDecls -variable config(wantPEDecls)
label .peDeclLabel -text [mc "PE declarations:"]
label .peDecls -textvariable PEDeclCount
checkbutton .wantAttListDecls -variable config(wantAttListDecls)
label .attListDeclLabel -text [mc "Atttribute List declarations:"]
label .attListDecls -textvariable AttListDeclCount
checkbutton .wantComments -variable config(wantComments)
label .commentLabel -text [mc "Comments:"]
label .comments -textvariable CommentCount
label .status -textvariable status -foreground red
grid .wantElementDecls .elementDeclLabel .elementDecls
grid .wantPEDecls .peDeclLabel .peDecls
grid .wantAttListDecls .attListDeclLabel .attListDecls
grid .wantComments .commentLabel .comments
grid .status - -
. configure -menu .menu
menu .menu -tearoff 0
.menu add cascade -label [mc File] -menu .menu.file
menu .menu.file
.menu.file add command -label [mc Open] -command Open
.menu.file add separator
.menu.file add command -label [mc Quit] -command exit
set currentDir [pwd]
} else {
Process [read stdin] xml stdout
}
}
default {
foreach filename $argv {
if {$filename == "-"} {
Process [read stdin] xml stdout
} else {
set ch [open $filename]
Process [read $ch] [expr {[file extension $filename] == ".dtd" ? "dtd" : "xml"}] stdout -baseurl file://[file join [pwd] $filename]
close $ch
}
}
}
}
|