home *** CD-ROM | disk | FTP | other *** search
/ PC World 2002 May / PCWorld_2002-05_cd.bin / Software / TemaCD / activetcltk / ActiveTcl8.3.4.1-8.win32-ix86.exe / ActiveTcl8.3.4.1-win32-ix86 / demos / TclXML / flatten.tcl next >
Encoding:
Text File  |  2001-10-22  |  8.0 KB  |  317 lines

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