3 -- a DTD generator for wireshark
5 -- (c) 2006 Luis E. Garcia Ontanon <luis@ontanon.org>
9 -- Wireshark - Network traffic analyzer
10 -- By Gerald Combs <gerald@wireshark.org>
11 -- Copyright 1998 Gerald Combs
13 -- This program is free software; you can redistribute it and/or
14 -- modify it under the terms of the GNU General Public License
15 -- as published by the Free Software Foundation; either version 2
16 -- of the License, or (at your option) any later version.
18 -- This program is distributed in the hope that it will be useful,
19 -- but WITHOUT ANY WARRANTY; without even the implied warranty of
20 -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 -- GNU General Public License for more details.
23 -- You should have received a copy of the GNU General Public License
24 -- along with this program; if not, write to the Free Software
25 -- Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
28 local xml_fld = Field.new("xml")
30 local function dtd_generator()
31 local displayed = {} -- whether or not a dtd is already displayed
32 local dtds = {} -- the dtds
33 local changed = {} -- whether or not a dtd has been modified
34 local dtd -- the dtd being dealt with
35 local dtd_name -- its name
37 -- we'll tap onto every frame that has xml
39 local ws = {} -- the windows for each dtd
40 local w = TextWindow.new("DTD Generator")
43 local wh = TextWindow.new("DTD Generator Help")
45 wh:set('DTD Generator Help\n')
48 local function get_dtd_from_xml(text,d,parent)
49 -- obtains dtd information from xml
50 -- text: xml to be parsed
51 -- d: the current dtd (if any)
52 -- parent: parent entity (if any)
54 -- cleanup the text from useless chars
55 text = string.gsub(text ,"%s*<%s*","<");
56 text = string.gsub(text ,"%s*>%s*",">");
57 text = string.gsub(text ,"<%-%-(.-)%-%->"," ");
58 text = string.gsub(text ,"%s+"," ");
62 local open_tag = string.match(text,"%b<>")
64 if open_tag == nil then
65 -- no more tags, we're done
69 local name = string.match(open_tag,"[%w%d_-]+")
73 -- there's no current dtd, this is entity is it
77 d = {ents = {}, attrs = {}}
87 if this_ent == nil then
88 -- no entity by this name in this dtd, create it
89 this_ent = {ents = {}, attrs = {}}
90 d.ents[name] = this_ent
91 changed[dtd_name] = true
95 -- add this entity to its parent
97 changed[dtd_name] = true
100 -- add the attrs to the entity
101 for att in string.gmatch(open_tag, "([%w%d_-]+)%s*=") do
102 if not this_ent.attrs[att] then
103 changed[dtd_name] = true
104 this_ent.attrs[att] = true
108 if string.match(open_tag,"/>") then
109 -- this tag is "self closed" just remove it and continue
110 text = string.gsub(text,"%b<>","",1)
112 local close_tag_pat = "</%s*" .. name .. "%s*>"
113 if not string.match(text,close_tag_pat) then return false end
114 local span,left = string.match(text,"%b<>(.-)" .. close_tag_pat .. "(.*)")
117 -- recurse to find child entities
118 if not get_dtd_from_xml(span,d,this_ent) then
123 -- continue with what's left
131 local function entity_tostring(name,entity_data)
132 -- name: the name of the entity
133 -- entity_data: a table containg the entity data
134 -- returns the dtd text for that entity
136 text = text .. '\t<!ELEMENT ' .. name .. ' (' --)
137 for e,j in pairs(entity_data.ents) do
138 text = text .. " " .. e .. ' |'
140 text = text .. " #PCDATA ) >\n"
142 text = text .. "\t<!ATTLIST " .. name
143 for a,j in pairs(entity_data.attrs) do
144 text = text .. "\n\t\t" .. a .. ' CDTATA #IMPLIED'
146 text = text .. " >\n\n"
148 text = string.gsub(text,"<!ATTLIST " .. name .. " >\n","")
153 local function dtd_tostring(name,doctype)
154 local text = '<? wireshark:protocol proto_name="' .. name ..'" hierarchy="yes" ?>\n\n'
155 local root = doctype.ents[name]
156 doctype.ents[name] = nil
158 text = text .. entity_tostring(name,root)
160 for n,es in pairs(doctype.ents) do
161 text = text .. entity_tostring(n,es)
164 doctype.ents[name] = root
170 local function element_body(name,text)
171 -- get the entity's children from dtd text
172 -- name: the name of the element
173 -- text: the list of children
174 text = string.gsub(text,"[%s%?%*%#%+%(%)]","")
175 text = string.gsub(text,"$","|")
176 text = string.gsub(text,
179 if dtd.ents[name] == nil then
180 dtd.ents[name] = {ents={},attrs={}}
183 dtd.ents[name].ents[s] = true
190 local function attlist_body(name,text)
191 -- get the entity's attributes from dtd text
192 -- name: the name of the element
193 -- text: the list of attributes
194 text = string.gsub(text,"([%w%d_-]+) [A-Z]+ #[A-Z]+",
203 local function dtd_body(buff)
204 -- get the dtd's entities from dtd text
205 -- buff: the dtd text
207 local old_buff = buff
209 buff = string.gsub(buff,"<!ELEMENT ([%w%d_-]+) (%b())>%s*",element_body)
210 buff = string.gsub(buff,"<!ATTLIST ([%w%d_-]+) (.-)>%s*",attlist_body)
213 local function load_dtd(filename)
214 local dtd_filename = USER_DIR .. "/dtds/" .. filename
221 for line in io.lines(dtd_filename) do
225 buff = string.gsub(buff ,"%s*<%!%s*","<!");
226 buff = string.gsub(buff ,"%s*>%s*",">");
227 buff = string.gsub(buff ,"<!%-%-(.-)%-%->"," ");
228 buff = string.gsub(buff ,"%s+"," ");
229 buff = string.gsub(buff ,"^%s+","");
232 buff = string.gsub(buff,'(<%?%s*wireshark:protocol%s+.-%s*%?>)',
238 buff = string.gsub(buff,"^<!DOCTYPE ([%w%d_-]+) (%b[])%s*>",
240 dtd = { ents = {}, attrs = {}}
255 if wireshark_info then
256 dtd.wstag = wireshark_info
260 local function load_dtds()
261 -- loads all existing dtds in the user directory
262 local dirname = persconffile_path("dtds")
263 local status, dir = pcall(Dir.open,dirname,".dtd")
265 w:set('Loading DTDs from ' .. dirname .. ' \n')
268 w:append("Error: could not open the directory" .. dirname .. " , make sure it exists.\n")
272 for dtd_filename in dir do
273 w:append("File:" .. dtd_filename .. "\n")
274 load_dtd(dtd_filename)
279 local function dtd_window(name)
281 local wd = TextWindow.new(name .. '.dtd')
282 wd:set(dtd_tostring(name,dtds[name]))
285 local function save()
286 local file = io.open(persconffile_path("dtds/") .. name .. ".dtd" ,"w")
287 file:write(wd:get_text())
291 wd:add_button("Save",save)
295 local function close()
304 -- w:add_button("Help",help)
308 local li = Listener.new("frame","xml")
314 local txt = xml_fld().range:string();
315 get_dtd_from_xml(txt)
320 for name,j in pairs(changed) do
321 w:append("\n" .. name .. " has changed\n")
322 if not displayed[name] then
323 w:add_button(name,dtd_window(name))
324 displayed[name] = true
335 register_menu("DTD Generator",dtd_generator,MENU_TOOLS_UNSORTED)