11 elif s.group(2) != None:
12 return '<a href="' + s.group(0) + '">' + s.group(0) + '</a>'
13 elif s.group(3) != None:
14 return '<a href="mailto:' + s.group(0) + '">' + s.group(0) + '</a>'
17 return re.sub ("(<[^>]*>)|(http://[~.:/\w-]+)|([\w._!-]+@[\w_-]+).[\w_-]+", html_subst, str)
20 if s.group(1) != None:
24 return '<a href="http://bugs.gnome.org/db/%s/%s.html">#%s</a>' % (n[0:2], n, n)
27 str = re.sub ("(<[^>]*>)|#(\d+)", bug_subst, str)
31 return re.sub ("[^a-z]","-", string.lower(str))
33 class ParseError (Exception):
38 self.description = None
44 def set_size(self, size):
45 size = string.lower(size)
48 elif size == "medium":
53 raise ParseError, 'size must be "small", "medium", or "big"'
56 if self.size == "Big":
58 elif self.size == "Medium":
63 print '''<table cellspacing="0" cellpadding="2" width="97%%" border="0" bgcolor="#000000">
64 <tbody><tr><td colspan=2>
65 <table cellspacing="0" cellpadding="5" width="100%%" border="0" bgcolor="#ffffff">
68 <td align="left"><font size="+1">%s</font></font></td>
69 <td align="left" width="20%%"><b>Size</b>: %s</td>
70 <td align="center" width="20%%"><b>Status</b>: %s</td>
71 <td align="right" width="20%%"><b>Target Version</b>: %s</td>
76 <table cellspacing="0" cellpadding="0">
77 <tbody>''' % (bgcolor, self.title, self.size, self.status, self.target, htmlify(self.description))
80 print '''<tr><td width="0"><b>More Info</b>:</td>
82 </tr>''' % htmlify (self.url)
85 print '''<tr><td width="0"><b>Bug Reports</b>:</td>
87 </tr>''' % bugify (self.bugs)
89 if self.contact != None:
90 print '''<tr><td width="0"><b>Contact</b>:</td>
92 </tr>''' % htmlify (self.contact)
99 </td></tr></tbody></table>
109 print '<h2><a name="%s">%s</a></h2>' % (make_id(self.title), self.title)
112 for entry in self.entries:
118 class TodoParser (xmllib.XMLParser):
120 xmllib.XMLParser.__init__(self)
133 def start_todo(self,attributes):
135 raise ParseError, "<todo> tags may not be nested"
136 if attributes.has_key ("logourl"):
137 self.logourl = attributes["logourl"]
143 def start_section(self,attributes):
145 raise ParseError, "<section> tags may not be nested"
147 self.section = Section()
149 def end_section(self):
150 if self.section.title == None:
151 raise ParseError, "<section> requires <title>"
153 self.sections.append(self.section)
156 def start_title(self,attributes):
158 raise ParseError, "<title> tag must be in <todo>, <section> or <entry>"
160 raise ParseError, "Unexpected <title> tag in content"
166 self.entry.title = self.data
168 self.section.title = self.data
170 self.title = self.data
173 def start_description(self,attributes):
175 raise ParseError, "<description> tag must be in <entry>"
177 raise ParseError, "Unexpected <description> tag in content"
180 def end_description(self):
182 self.entry.description = self.data
185 def start_url(self,attributes):
187 raise ParseError, "<url> tag must be in <entry>"
189 raise ParseError, "Unexpected <url> tag in content"
194 self.entry.url = self.data
197 def start_contact(self,attributes):
199 raise ParseError, "<contact> tag must be in <entry>"
201 raise ParseError, "Unexpected <contact> tag in content"
204 def end_contact(self):
206 self.entry.contact = self.data
209 def start_bugs(self,attributes):
211 raise ParseError, "<bugs> tag must be in <bugs>"
213 raise ParseError, "Unexpected <bugs> tag in content"
218 self.entry.bugs = self.data
221 def start_entry(self,attributes):
223 raise ParseError, "<entry> tag must be in <section>"
225 raise ParseError, "<entry> tags may not be nested"
229 if not attributes.has_key("size"):
230 raise ParseError, '"size" attribute required for entry'
231 self.entry.set_size(attributes["size"])
233 if not attributes.has_key("status"):
234 raise ParseError, '"status" attribute (completion percentage) required for entry'
235 self.entry.status=attributes["status"]
237 if not attributes.has_key("target"):
238 raise ParseError, '"target" attribute (target version) required for entry'
239 self.entry.target=attributes["target"]
242 if self.entry.title == None:
243 raise ParseError, "<entry> requires <title>"
245 if self.entry.description == None:
246 raise ParseError, "<entry> requires <description>"
248 self.section.entries.append(self.entry)
251 def handle_data(self,data):
253 self.data = self.data + data
255 def unknown_starttag(self,tag,attributes):
257 raise ParseError, "Unexpected start tag: " + tag
259 self.data = self.data + "<" + tag
260 for (key,val) in attributes.items():
261 self.data = self.data + ' %s="%s"' % (key,val)
262 self.data = self.data + ">"
264 def unknown_endtag(self,tag):
266 raise ParseError, "Unexpected end tag: " + tag
268 self.data = self.data + "</%s>" % tag
270 def syntax_error(self, err):
271 if re.match("reference to unknown entity", err):
274 xmllib.XMLParser.syntax_error (self, err)
276 def unknown_entityref(self,ref):
278 raise ParseError, "Unknown entity &" + ref + ";"
280 self.data = self.data + "&" + ref + ";"
282 file = open(sys.argv[1])
283 parser = TodoParser()
287 line = file.readline()
293 except ParseError, err:
294 sys.stderr.write("Parse error at line " + `lineno` + ": " + err.__str__() + "\n")
296 except RuntimeError, err:
297 sys.stderr.write(err.__str__() + "\n")
303 if parser.title == None:
304 sys.stderr.write ("<todo> Document must have a <title>\n")
307 print '''<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
312 <body bgcolor="#ffffff">
313 <table width="100%%" cellspacing="0" cellpadding="0" border="0">
317 <h1>%s</h1>''' % (parser.title, parser.title)
320 for section in parser.sections:
321 ntasks = len(section.entries)
322 id = make_id (section.title)
324 print '<a href="#%s">%s</a> (1 item)<br>' % (id,section.title)
326 print '<a href="#%s">%s</a> (%d items)<br>' % (id,section.title,ntasks)
330 if parser.logourl != None:
331 print ''' <td align="right">
332 <img src="%s" alt="Logo"></img>
333 </td>''' % parser.logourl
341 for section in parser.sections: