Add a list of wiki pages to link to and automatically insert links.
[chocolate-doom.git] / man / docgen
blob0319bbc79883cb8d315f4fc6f7f45eecdc10487f
1 #!/usr/bin/env python
2 #
3 # Command line parameter self-documentation tool. Reads comments from
4 # the source code in the following form:
6 # //!
7 # // @arg <extra arguments>
8 # // @category Category
9 # // @platform <some platform that the parameter is specific to>
10 # //
11 # // Long description of the parameter
12 # //
14 # something_involving = M_CheckParm("-param");
16 # From this, a manpage can be automatically generated of the command
17 # line parameters.
19 import sys
20 import re
21 import glob
22 import getopt
24 class Category:
25 def __init__(self, description):
26 self.description = description
27 self.params = []
29 def add_param(self, param):
30 self.params.append(param)
32 # Find the maximum width of a parameter in this category
34 def paramtext_width(self):
35 w = 0
37 for p in self.params:
38 pw = len(p.name) + 5
40 if p.args:
41 pw += len(p.args)
43 if pw > w:
44 w = pw
46 return w
48 # Plain text output
50 def plaintext_output(self):
51 result = "=== %s ===\n\n" % self.description
53 self.params.sort()
55 w = self.paramtext_width()
57 for p in self.params:
58 if p.should_show():
59 result += p.plaintext_output(w)
61 result = result.rstrip() + "\n"
63 return result
65 def manpage_output(self):
66 result = ".SH " + self.description.upper() + "\n"
68 self.params.sort()
70 for p in self.params:
71 if p.should_show():
72 result += ".TP\n"
73 result += p.manpage_output()
75 return result
77 def wiki_output(self):
78 result = "=== %s ===\n" % self.description
80 self.params.sort()
82 for p in self.params:
83 if p.should_show():
84 result += "; " + p.wiki_output() + "\n"
86 # Escape special HTML characters
88 result = result.replace("&", "&amp;")
89 result = result.replace("<", "&lt;")
90 result = result.replace(">", "&gt;")
92 return result
94 categories = {
95 None: Category("General options"),
96 "video": Category("Display options"),
97 "demo": Category("Demo options"),
98 "net": Category("Networking options"),
99 "mod": Category("Dehacked and WAD merging"),
100 "compat": Category("Compatibility"),
103 wikipages = []
105 # Show options that are in Vanilla Doom? Or only new options?
107 show_vanilla_options = True
109 class Parameter:
110 def __cmp__(self, other):
111 if self.name < other.name:
112 return -1
113 else:
114 return 1
116 def __init__(self):
117 self.text = ""
118 self.name = ""
119 self.args = None
120 self.platform = None
121 self.category = None
122 self.vanilla_option = False
124 def should_show(self):
125 return not self.vanilla_option or show_vanilla_options
127 def add_text(self, text):
128 if len(text) <= 0:
129 pass
130 elif text[0] == "@":
131 match = re.match('@(\S+)\s*(.*)', text)
133 if not match:
134 raise "Malformed option line: %s" % text
136 option_type = match.group(1)
137 data = match.group(2)
139 if option_type == "arg":
140 self.args = data
141 elif option_type == "platform":
142 self.platform = data
143 elif option_type == "category":
144 self.category = data
145 elif option_type == "vanilla":
146 self.vanilla_option = True
147 else:
148 raise "Unknown option type '%s'" % option_type
150 else:
151 self.text += text + " "
153 def manpage_output(self):
154 result = self.name
156 if self.args:
157 result += " " + self.args
159 result = '\\fB' + result + '\\fR'
161 result += "\n"
163 if self.platform:
164 result += "[%s only] " % self.platform
166 escaped = re.sub('\\\\', '\\\\\\\\', self.text)
168 result += escaped + "\n"
170 return result
172 def wiki_output(self):
173 result = self.name
175 if self.args:
176 result += " " + self.args
178 result += ": "
180 result += add_wiki_links(self.text)
182 if self.platform:
183 result += "'''(%s only)'''" % self.platform
185 return result
187 def plaintext_output(self, w):
189 # Build the first line, with the argument on
191 line = " " + self.name
192 if self.args:
193 line += " " + self.args
195 # pad up to the plaintext width
197 line += " " * (w - len(line))
199 # Build the description text
201 description = self.text
203 if self.platform:
204 description += " (%s only)" % self.platform
206 # Build the complete text for the argument
207 # Split the description into words and add a word at a time
209 result = ""
210 for word in re.split('\s+', description):
212 # Break onto the next line?
214 if len(line) + len(word) + 1 > 75:
215 result += line + "\n"
216 line = " " * w
218 # Add another word
220 line += word + " "
222 result += line + "\n\n"
224 return result
226 # Read list of wiki pages
228 def read_wikipages():
229 f = open("wikipages")
231 try:
232 for line in f:
233 line = line.rstrip()
235 line = re.sub('\#.*$', '', line)
237 if not re.match('^\s*$', line):
238 wikipages.append(line)
239 finally:
240 f.close()
242 # Add wiki page links
244 def add_wiki_links(text):
245 for pagename in wikipages:
246 page_re = re.compile('(%s)' % pagename, re.IGNORECASE)
247 # text = page_re.sub("SHOES", text)
248 text = page_re.sub('[[\\1]]', text)
250 return text
252 def process_file(file):
253 f = open(file)
255 try:
256 param = None
257 waiting_for_checkparm = False
259 for line in f:
260 line = line.rstrip()
262 # Currently reading a doc comment?
264 if param:
265 # End of doc comment
267 if not re.match('\s*//', line):
268 waiting_for_checkparm = True
270 # Waiting for the M_CheckParm call that contains the
271 # name of the parameter we are documenting?
273 if waiting_for_checkparm:
274 match = re.search('M_CheckParm\s*\(\s*"(.*?)"\s*\)', line)
276 if match:
277 # Found the name! Finished documenting this
278 # parameter.
280 param.name = match.group(1)
281 categories[param.category].add_param(param)
282 param = None
284 else:
285 # More documentation text
287 munged_line = re.sub('\s*\/\/\s*', '', line, 1)
288 munged_line = re.sub('\s*$', '', munged_line)
289 param.add_text(munged_line)
291 # Check for start of a doc comment
293 if re.search("//!", line):
294 param = Parameter()
295 waiting_for_checkparm = False
296 finally:
297 f.close()
299 def process_files(dir):
300 # Process all C source files.
302 files = glob.glob(dir + "/*.c")
304 for file in files:
305 process_file(file)
307 def print_file_contents(file):
308 f = open(file)
310 try:
311 for line in f:
312 print line.rstrip()
314 finally:
315 f.close()
317 def manpage_output(dir):
319 process_files(dir)
321 print_file_contents("header")
323 print categories[None].manpage_output()
325 for c in categories:
326 if c != None:
327 print categories[c].manpage_output()
329 print_file_contents("footer")
331 def wiki_output(dir):
332 read_wikipages()
333 process_files(dir)
335 print categories[None].wiki_output()
337 for c in categories:
338 if c != None:
339 print categories[c].wiki_output()
341 def plaintext_output(dir):
342 process_files(dir)
344 print "== Command line parameters =="
345 print
346 print "This is a list of the command line parameters supported by "
347 print "Chocolate Doom. A number of additional parameters are supported "
348 print "in addition to those present in Vanilla Doom. "
349 print
351 print categories[None].plaintext_output()
353 for c in categories:
354 if c != None:
355 print categories[c].plaintext_output()
357 def usage():
358 print "Usage: %s [-V] ( -m | -w | -p ) <directory>" % sys.argv[0]
359 print " -m : Manpage output"
360 print " -w : Wikitext output"
361 print " -p : Plaintext output"
362 print " -V : Don't show Vanilla Doom options"
363 sys.exit(0)
365 # Parse command line
367 opts, args = getopt.getopt(sys.argv[1:], "mwpV")
369 output_function = None
371 for opt in opts:
372 if opt[0] == "-m":
373 output_function = manpage_output
374 elif opt[0] == "-w":
375 output_function = wiki_output
376 elif opt[0] == "-p":
377 output_function = plaintext_output
378 elif opt[0] == "-V":
379 show_vanilla_options = False
381 if output_function == None or len(args) != 1:
382 usage()
383 else:
384 output_function(args[0])