flatten.tcl

Go to the documentation of this file.
00001 /* !/bin/sh*/
00002 /*  -*- tcl -*- \*/
00003 exec tclsh8.3 "$0" "$@"
00004 
00005 /*  flatten.tcl --*/
00006 /* */
00007 /*  Parse a DTD, resolve all external entities, parameter*/
00008 /*  entities and conditional sections and save the result.*/
00009 /* */
00010 /*  Copyright (c) 2000 Zveno Pty Ltd*/
00011 /*  http://www.zveno.com/*/
00012 /*  */
00013 /*  Zveno makes this software and all associated data and documentation*/
00014 /*  ('Software') available free of charge for any purpose.*/
00015 /*  Copies may be made of this Software but all of this notice must be included*/
00016 /*  on any copy.*/
00017 /*  */
00018 /*  The Software was developed for research purposes and Zveno does not warrant*/
00019 /*  that it is error free or fit for any purpose.  Zveno disclaims any*/
00020 /*  liability for all claims, expenses, losses, damages and costs any user may*/
00021 /*  incur as a result of using, copying or modifying the Software.*/
00022 /* */
00023 /*  CVS: $Id: flatten.tcl,v 1.2 2000/05/19 23:56:20 steve Exp $*/
00024 
00025 /*  Allow the script to work from the source directory*/
00026  auto = _path [linsert $auto_path 0 [file dirname [file dirname [file join [pwd] [info script]]]]]
00027 
00028 /*  We need TclXML*/
00029 package require xml 2.0
00030 
00031 /*  Process --*/
00032 /* */
00033 /*  Parse a XML document or DTD and emit result*/
00034 /* */
00035 /*  Arguments:*/
00036 /*  data    XML text*/
00037 /*  type    "xml" or "dtd"*/
00038 /*  out output channel*/
00039 /*  args    configration options*/
00040 /* */
00041 /*  Results:*/
00042 /*  Data is parsed and flattened DTD written to output channel*/
00043 
00044 ret  Process (type data , type type , type out , type args) {
00045     global elementDeclCount PEDeclCount AttListDeclCount CommentCount
00046     global config
00047     set elementDeclCount [set PEDeclCount [set AttListDeclCount [set CommentCount 0]]]
00048 
00049     # Create the parser object.
00050     # We want to use the Tcl-only parser for this application,
00051     # because it can resolve entities without doing full
00052     # validation.
00053 
00054     set parser [eval ::xml::parser \
00055         -elementstartcommand ElementStart \
00056         -validate 1 \
00057         $args \
00058         ]
00059 
00060     if {$config(wantElementDecls)} {
00061     $parser configure -elementdeclcommand [list ElementDeclaration $out]
00062     }
00063     if {$config(wantPEDecls)} {
00064     $parser configure -parameterentitydeclcommand [list PEDecl $out]
00065     }
00066     if {$config(wantAttListDecls)} {
00067     $parser configure -attlistdeclcommand [list AttListDecl $out]
00068     }
00069     if {$config(wantComments)} {
00070     $parser configure -commentcommand [list Comment $out]
00071     }
00072 
00073     switch $type {
00074     xml {
00075         # Proceed with normal parsing method
00076         $parser parse $data
00077     }
00078 
00079     dtd {
00080         # Use the DTD parsing method instead
00081         $parser parse $data -dtdsubset external
00082     }
00083     }
00084 
00085     # Clean up parser object
00086     #$parser free
00087     #rename $parser {}
00088 
00089     return {}
00090 }
00091 
00092 /*  ElementStart --*/
00093 /* */
00094 /*  Callback for the start of an element.*/
00095 /* */
00096 /*  Arguments:*/
00097 /*  name    tag name*/
00098 /*  attlist attribute list*/
00099 /*  args    other information*/
00100 /* */
00101 /*  Results:*/
00102 /*  Returns break error code, since we don't*/
00103 /*  care about the document instance, only the DTD*/
00104 
00105 ret  ElementStart (type name , type attlist , type args) {
00106     return -code break
00107 }
00108 
00109 /*  ElementDeclaration --*/
00110 /* */
00111 /*  Callback for an element declaration.*/
00112 /* */
00113 /*  Arguments:*/
00114 /*  out output channel*/
00115 /*  name    tag name*/
00116 /*  cmodel  content model specification*/
00117 /* */
00118 /*  Results:*/
00119 /*  Writes element declaration to output channel*/
00120 
00121 ret  ElementDeclaration (type out , type name , type cmodel) {
00122     global elementDeclCount
00123     incr elementDeclCount
00124 
00125     regsub -all "\[ \t\n\r\]+" $cmodel { } cmodel
00126     puts $out "<!ELEMENT $name $cmodel>"
00127 
00128     return {}
00129 }
00130 
00131 /*  PEDecl --*/
00132 /* */
00133 /*  Callback for a parameter entity declaration.*/
00134 /* */
00135 /*  Arguments:*/
00136 /*  out output channel*/
00137 /*  name    PE name*/
00138 /*  repl    replacement text*/
00139 /* */
00140 /*  Results:*/
00141 /*  Writes info to stderr*/
00142 
00143 ret  PEDecl (type out , type name , type repl , type args) {
00144     global PEDeclCount
00145     incr PEDeclCount
00146 
00147     if {[llength $args]} {
00148     puts $out "<!ENTITY % $name PUBLIC \"[lindex $args 0]\" \"$repl\">"
00149     } else {
00150     puts $out "<!ENTITY % $name \"[string trim $repl]\">"
00151     }
00152 
00153     return {}
00154 }
00155 
00156 /*  AttListDecl --*/
00157 /* */
00158 /*  Callback for an attribute list declaration.*/
00159 /* */
00160 /*  Arguments:*/
00161 /*  out output channel*/
00162 /*  name    element name*/
00163 /*  attname attribute name*/
00164 /*  type    attribute definition type*/
00165 /*  dflt    default type*/
00166 /*  dfltval default value*/
00167 /* */
00168 /*  Results:*/
00169 /*  Writes info to stderr*/
00170 
00171 ret  AttListDecl (type out , type name , type attname , type type , type dflt , type dfltval) {
00172     global AttListDeclCount
00173     incr AttListDeclCount
00174 
00175     puts $out "<!ATTLIST $name $attname $type $dflt $dfltval>"
00176 
00177     return {}
00178 }
00179 
00180 /*  Comment --*/
00181 /* */
00182 /*  Callback for a comment.*/
00183 /* */
00184 /*  Arguments:*/
00185 /*  out output channel*/
00186 /*  data    comment data*/
00187 /* */
00188 /*  Results:*/
00189 /*  Writes info to stderr*/
00190 
00191 ret  Comment (type out , type data) {
00192     global CommentCount
00193     incr CommentCount
00194 
00195     puts $out "<!--${data}-->"
00196 
00197     return {}
00198 }
00199 
00200 /*  Open --*/
00201 /* */
00202 /*  Manage opening document in GUI environment*/
00203 /* */
00204 /*  Arguments:*/
00205 /*  None*/
00206 /* */
00207 /*  Results:*/
00208 /*  XML or DTD document opened and parsed*/
00209 
00210 ret  Open () {
00211     global currentDir status
00212 
00213     set filename [tk_getOpenFile -parent . -title "Open Document" -initialdir $currentDir -defaultextension ".xml" -filetypes {
00214     {{XML Documents}    {.xml}  }
00215     {{DTD Files}        {.dtd}  }
00216     {{All File}     *   }
00217     }]
00218     if {![string length $filename]} {
00219     return {}
00220     }
00221 
00222     set currentDir [file dirname $filename]
00223     set savename [file join [file rootname $filename].dtd]
00224     set savename [tk_getSaveFile -parent . -title "Save DTD" -initialdir $currentDir -initialfile $savename -defaultextension ".dtd" -filetypes {
00225     {{XML Documents}    {.xml}  }
00226     {{DTD Files}        {.dtd}  }
00227     {{All File}     *   }
00228     }]
00229     if {![string length $savename]} {
00230     return {}
00231     }
00232 
00233     set status Processing
00234     set oldcursor [. cget -cursor]
00235     . configure -cursor watch
00236     grab .elementDecls
00237     update
00238 
00239     set ch [open $filename]
00240     set out [open $savename w]
00241     if {[catch {Process [read $ch] [expr {[file extension $filename] == ".dtd" ? "dtd" : "xml"}] $out -baseurl file://[file join [pwd] $filename]} err]} {
00242 
00243     tk_messageBox -message [format [mc {Unable to process document "%s" due to "%s"}] $filename $err] -icon error -default ok -parent . -type ok
00244     } else {
00245     tk_messageBox -message [mc "DTD Saved OK"] -icon info -default ok -parent . -type ok
00246     }
00247 
00248     close $ch
00249     close $out
00250     set status {}
00251     grab release .elementDecls
00252     . configure -cursor $oldcursor
00253     return {}
00254 }
00255 
00256 /*  Main script*/
00257 
00258 /*  Initialize message catalog, in case it is used*/
00259 package require msgcat
00260 namespace import msgcat::mc
00261 catch {::msgcat::mc
00262 
00263 /*  Usage: flatten.tcl file1 file2 ...*/
00264 /*  "-" reads input from stdin*/
00265 /*  No arguments - Tk means read from stdin*/
00266 /*  Files read from stdin assumed to be XML documents*/
00267 /*  When given files to read, all output goes to stdout*/
00268 /*  No arguments + Tk means use GUI*/
00269 
00270 switch [llength $argv] {
00271     0 {
00272     if {![catch {package require Tk}]} {
00273         /*  Create a nice little GUI*/
00274         array  config =  {wantElementDecls 1 wantPEDecls 0 wantAttlistDecls 1 wantComments 0}
00275         checkbutton .wantElementDecls -variable config(wantElementDecls)
00276         label .elementDeclLabel -text [mc "Element declarations:"]
00277         label .elementDecls -textvariable elementDeclCount
00278         checkbutton .wantPEDecls -variable config(wantPEDecls)
00279         label .peDeclLabel -text [mc "PE declarations:"]
00280         label .peDecls -textvariable PEDeclCount
00281         checkbutton .wantAttListDecls -variable config(wantAttListDecls)
00282         label .attListDeclLabel -text [mc "Atttribute List declarations:"]
00283         label .attListDecls -textvariable AttListDeclCount
00284         checkbutton .wantComments -variable config(wantComments)
00285         label .commentLabel -text [mc "Comments:"]
00286         label .comments -textvariable CommentCount
00287         label .status -textvariable status -foreground red
00288         grid .wantElementDecls .elementDeclLabel .elementDecls
00289         grid .wantPEDecls .peDeclLabel .peDecls
00290         grid .wantAttListDecls .attListDeclLabel .attListDecls
00291         grid .wantComments .commentLabel .comments
00292         grid .status - -
00293         . configure -menu .menu
00294         menu .menu -tearoff 0
00295         .menu add cascade -label [mc File] -menu .menu.file
00296         menu .menu.file
00297         .menu.file add command -label [mc Open] -command Open
00298         .menu.file add separator
00299         .menu.file add command -label [mc Quit] -command exit
00300          currentDir =  [pwd]
00301     } else {
00302         Process [read stdin] xml stdout
00303     }
00304     }
00305     default {
00306     foreach filename $argv {
00307         if {$filename == "-"} {
00308         Process [read stdin] xml stdout
00309         } else {
00310          ch =  [open $filename]
00311         Process [read $ch] [expr {[file extension $filename] == ".dtd" ? "dtd" : "xml"}] stdout -baseurl file://[file join [pwd] $filename]
00312         close $ch
00313         }
00314     }
00315     }
00316 }
00317 

Generated on 21 Sep 2010 for Gui by  doxygen 1.6.1