1 #!/usr/bin/env python 2 """ 3 document.py -- Simple script to generate manpages from C header 4 files. Looks for the following formatted C comments in the C header files: 5 6 /* 7 * Function: my_function - This is my function 8 * Description: My function does the following things, in no particular 9 * order: It eats, sleeps, and is merry 10 * Input: arg1 - This argument is healthy 11 * arg2 - This argument is wealthy 12 * arg3 - This argument is wise 13 * Output: arg4 - The location of the porridge 14 * arg5 - The location of the spider 15 * Returns: -1 on error, 0 otherwise 16 */ 17 18 """ 19 20 21 import sys, os, getopt, string, re, time 22 23 QUIET = 0 24 25 def usage(argv0): 26 print "%s [--help]" % argv0 27 28 class FuncDoc: 29 def __init__ (self, name): 30 self._name = name 31 self._title = None 32 self._desc = None 33 self._args = None 34 self._retr = None 35 self._defn = None 36 self._output = None 37 self._other = "" 38 39 def __repr__(self): 40 out = [] 41 out.append("Name: %s" % self._name) 42 if self._title is not None: 43 out.append("Title: %s" % self._title) 44 if self._desc is not None: 45 out.append("Description: %s" % self._desc) 46 if self._args is not None: 47 out.append("Input: %s" % self._args) 48 if self._output is not None: 49 out.append("Output: %s" % self._output) 50 if self._retr is not None: 51 out.append("Returns: %s" % self._retr) 52 if string.strip(self._other): 53 out.append("Other: %s" % self._other) 54 if self._defn is not None: 55 out.append("Definition:") 56 out.append(self._defn) 57 return string.join(out, "\n") 58 59 class CParser: 60 STATE_OTHER = 0 61 STATE_COMT = 1 62 STATE_FUNC = 2 63 64 RE_C_comment = re.compile("/\*(.*)") 65 RE_C_define = re.compile("\s*#\s*define (\S+) (.*)") 66 RE_C_typedef = re.compile("typedef (\S+) (.*)") 67 RE_C_func_def = re.compile("[^#]*(\S+)([ \*]+)(\S+)\s*\([^\)]*\);") 68 RE_C_func_def_b = re.compile("[^#]*(\S+)([ \*]+)(\S+)\s*\([^\)]*") 69 RE_C_func_com = re.compile("function:\s*(\S+)(.*)", re.IGNORECASE) 70 RE_C_desc_com = re.compile("description:\s*(.+)", re.IGNORECASE) 71 RE_C_args_com = re.compile("(arguments|input):\s*(.+)", re.IGNORECASE) 72 RE_C_retr_com = re.compile("(return|returns):\s*(.+)", re.IGNORECASE) 73 RE_C_out_com = re.compile("output:\s*(.+)", re.IGNORECASE) 74 RE_C_other_com = re.compile("(\S+):\s*(.+)") 75 RE_C_com_cont = re.compile("[ \*]*(.+)") 76 77 def __init__ (self, filename): 78 self._filename = filename 79 self._funcs = {} 80 81 def func (self, name): 82 try: 83 return self._funcs[name] 84 except KeyError: 85 f = FuncDoc(name) 86 self._funcs[name] = f 87 return f 88 89 def go(self): 90 try: 91 fp = open(self._filename) 92 except IOError: 93 return 94 state = CParser.STATE_OTHER 95 f = None 96 cont = None 97 while 1: 98 line = fp.readline() 99 if not line: break 100 if state == CParser.STATE_OTHER: 101 m = CParser.RE_C_comment.search (line) 102 if m: 103 line = m.group(1) 104 state = CParser.STATE_COMT 105 else: 106 m = CParser.RE_C_define.match(line) 107 if m: continue 108 m = CParser.RE_C_typedef.search(line) 109 if m: continue 110 m = CParser.RE_C_func_def.match(line) 111 if m: 112 func_name = m.group(3) 113 f = self.func(func_name) 114 f._defn = line 115 else: 116 m = CParser.RE_C_func_def_b.match(line) 117 if m: 118 state = CParser.STATE_FUNC 119 func_name = m.group(3) 120 f = self.func(func_name) 121 f._defn = line 122 continue 123 if state == CParser.STATE_COMT: 124 if string.find(line, "*/") != -1: 125 state = CParser.STATE_OTHER 126 continue 127 m = CParser.RE_C_func_com.search(line) 128 if m: 129 cont = "func" 130 f = self.func(m.group(1)) 131 f._title = m.group(2) 132 continue 133 m = CParser.RE_C_desc_com.search(line) 134 if m: 135 cont = "desc" 136 f._desc = m.group(1) 137 continue 138 m = CParser.RE_C_args_com.search(line) 139 if m: 140 cont = "args" 141 f._args = m.group(2) 142 continue 143 m = CParser.RE_C_retr_com.search(line) 144 if m: 145 cont = "retr" 146 f._retr = m.group(2) 147 continue 148 m = CParser.RE_C_out_com.search(line) 149 if m: 150 cont = "out" 151 f._output = m.group(1) 152 continue 153 m = CParser.RE_C_other_com.search(line) 154 if not f: continue 155 if m: 156 cont = "other" 157 f._other = f._other + "%s: %s" % (m.group(1), m.group(2)) 158 continue 159 m = CParser.RE_C_com_cont.search(line) 160 if m: 161 if cont == "func": 162 f._title = f._title + '\n' + m.group(1) 163 elif cont == "desc": 164 f._desc = f._desc + '\n'+ m.group(1) 165 elif cont == "args": 166 f._args = f._args + '\n' + m.group(1) 167 elif cont == "retr": 168 f._retr = f._retr + '\n' + m.group(1) 169 elif cont == "out": 170 f._output = f._output + '\n' + m.group(1) 171 elif cont == "other": 172 f._other = f._other + '\n' + m.group(1) 173 elif state == CParser.STATE_FUNC: 174 f._defn = f._defn+line 175 if string.find(line, ");") != -1: 176 state = CParser.STATE_OTHER 177 178 def dump(self): 179 for name in self._funcs.keys(): 180 # print name 181 print "%s\n" % self._funcs[name] 182 183 def dump_manpages(self, directory, owner): 184 global QUIET 185 date = time.strftime("%d %B %Y", time.localtime(time.time())) 186 for name, f in self._funcs.items(): 187 if f._title is None and f._desc is None and f._args is None and f._retr is None: 188 if not QUIET: 189 sys.stderr.write('-W- No info for function "%s()"\n' % name) 190 continue 191 if f._defn is None: 192 if not QUIET: 193 sys.stderr.write('-W- No defn for function "%s()"\n' % name) 194 fp = open("%s/%s.3" % (directory, name), "w") 195 fp.write('.TH %s 3 "%s" "%s" "%s"\n\n' % (name, date, owner, self._filename)) 196 fp.write('.de Ss\n.sp\n.ft CW\n.nf\n..\n') 197 fp.write('.de Se\n.fi\n.ft P\n.sp\n..\n') 198 fp.write('.SH NAME\n') 199 if f._title is None: 200 fp.write('%s\n' % f._name) 201 else: 202 fp.write('%s %s\n' % (f._name, f._title)) 203 fp.write('.SH SYNOPSIS\n') 204 fp.write('.Ss\n#include <%s>\n.Se\n' % self._filename) 205 if f._defn: 206 fp.write('.Ss\n%s\n.Se\n' % f._defn) 207 else: 208 fp.write('.Ss\n%s()\n.Se\n' % f._name) 209 fp.write('\n') 210 if f._args: 211 fp.write('.SH ARGUMENTS\n') 212 fp.write('%s\n\n' % string.replace(f._args, '\n', '\n.br\n')) 213 if f._desc or string.strip(f._other): 214 fp.write('.SH DESCRIPTION\n') 215 if f._desc: fp.write('%s\n\n' % f._desc) 216 if string.strip(f._other): fp.write('%s\n\n' % f._other) 217 if f._output: 218 fp.write('.SH "RETURN VALUE"\n') 219 fp.write('%s\n\n' % string.replace(f._output, '\n', '\n.br\n')) 220 fp.write('.SH "SEE ALSO"\n') 221 fp.write('.BR %s\n' % string.join(self._funcs.keys(), ' "(3), "')) 222 fp.close() 223 224 def dump_hdf (self, directory, owner): 225 global QUIET 226 sys.path.insert (0, "../python") 227 sys.path.insert (0, "python") 228 import neo_cgi, neo_util 229 hdf = neo_util.HDF() 230 date = time.strftime("%d %B %Y", time.localtime(time.time())) 231 if not self._funcs.items(): return 232 for name, f in self._funcs.items(): 233 if f._title is None and f._desc is None and f._args is None and f._retr is None: 234 if not QUIET: 235 sys.stderr.write('-W- No info for function "%s()"\n' % name) 236 continue 237 if f._defn is None: 238 if not QUIET: 239 sys.stderr.write('-W- No defn for function "%s()"\n' % name) 240 hdf.setValue ("Code.%s" % name, name) 241 obj = hdf.getObj ("Code.%s" % name) 242 obj.setValue ("Name", name) 243 obj.setValue ("filename", self._filename) 244 if f._title: obj.setValue ("Title", f._title) 245 if f._defn: obj.setValue ("Define", neo_cgi.text2html(f._defn)) 246 if f._args: obj.setValue ("Args", neo_cgi.text2html(f._args)) 247 if f._desc: obj.setValue ("Desc", neo_cgi.text2html(f._desc)) 248 if string.strip(f._other): obj.setValue ("Other", neo_cgi.text2html(string.strip(f._other))) 249 if f._output: obj.setValue ("Output", neo_cgi.text2html(f._output)) 250 n = 0 251 for func in self._funcs.keys(): 252 obj.setValue ("related.%d" % n, func) 253 n = n + 1 254 255 fname = self._filename 256 x = string.rindex (fname, "/") 257 if x != -1: fname = fname[x+1:] 258 x = string.rindex (fname, '.') 259 if x != -1: fname = fname[:x] 260 261 hdf.writeFile ("%s/%s.hdf" % (directory, fname)) 262 263 def main(argv, environ): 264 alist, args = getopt.getopt(argv[1:], "q", ["help", "outdir=", "owner=", "hdf"]) 265 266 outdir = "." 267 owner = "" 268 do_hdf = 0 269 for (field, val) in alist: 270 if field == "--help": 271 usage (argv[0]) 272 return 273 if field == "--outdir": 274 outdir = val 275 if field == "--owner": 276 owner = val 277 if field == "-q": 278 global QUIET 279 QUIET = 1 280 if field == "--hdf": 281 do_hdf = 1 282 283 if args: 284 for file in args: 285 parser = CParser(file) 286 parser.go() 287 if not do_hdf: 288 parser.dump_manpages(outdir, owner) 289 else: 290 parser.dump_hdf (outdir, owner) 291 292 293 if __name__ == "__main__": 294 main (sys.argv, os.environ) 295