| #!/usr/bin/env python |
| """ |
| document.py -- Simple script to generate manpages from C header |
| files. Looks for the following formatted C comments in the C header files: |
| |
| /* |
| * Function: my_function - This is my function |
| * Description: My function does the following things, in no particular |
| * order: It eats, sleeps, and is merry |
| * Input: arg1 - This argument is healthy |
| * arg2 - This argument is wealthy |
| * arg3 - This argument is wise |
| * Output: arg4 - The location of the porridge |
| * arg5 - The location of the spider |
| * Returns: -1 on error, 0 otherwise |
| */ |
| |
| """ |
| |
| |
| import sys, os, getopt, string, re, time |
| |
| QUIET = 0 |
| |
| def usage(argv0): |
| print "%s [--help]" % argv0 |
| |
| class FuncDoc: |
| def __init__ (self, name): |
| self._name = name |
| self._title = None |
| self._desc = None |
| self._args = None |
| self._retr = None |
| self._defn = None |
| self._output = None |
| self._other = "" |
| |
| def __repr__(self): |
| out = [] |
| out.append("Name: %s" % self._name) |
| if self._title is not None: |
| out.append("Title: %s" % self._title) |
| if self._desc is not None: |
| out.append("Description: %s" % self._desc) |
| if self._args is not None: |
| out.append("Input: %s" % self._args) |
| if self._output is not None: |
| out.append("Output: %s" % self._output) |
| if self._retr is not None: |
| out.append("Returns: %s" % self._retr) |
| if string.strip(self._other): |
| out.append("Other: %s" % self._other) |
| if self._defn is not None: |
| out.append("Definition:") |
| out.append(self._defn) |
| return string.join(out, "\n") |
| |
| class CParser: |
| STATE_OTHER = 0 |
| STATE_COMT = 1 |
| STATE_FUNC = 2 |
| |
| RE_C_comment = re.compile("/\*(.*)") |
| RE_C_define = re.compile("\s*#\s*define (\S+) (.*)") |
| RE_C_typedef = re.compile("typedef (\S+) (.*)") |
| RE_C_func_def = re.compile("[^#]*(\S+)([ \*]+)(\S+)\s*\([^\)]*\);") |
| RE_C_func_def_b = re.compile("[^#]*(\S+)([ \*]+)(\S+)\s*\([^\)]*") |
| RE_C_func_com = re.compile("function:\s*(\S+)(.*)", re.IGNORECASE) |
| RE_C_desc_com = re.compile("description:\s*(.+)", re.IGNORECASE) |
| RE_C_args_com = re.compile("(arguments|input):\s*(.+)", re.IGNORECASE) |
| RE_C_retr_com = re.compile("(return|returns):\s*(.+)", re.IGNORECASE) |
| RE_C_out_com = re.compile("output:\s*(.+)", re.IGNORECASE) |
| RE_C_other_com = re.compile("(\S+):\s*(.+)") |
| RE_C_com_cont = re.compile("[ \*]*(.+)") |
| |
| def __init__ (self, filename): |
| self._filename = filename |
| self._funcs = {} |
| |
| def func (self, name): |
| try: |
| return self._funcs[name] |
| except KeyError: |
| f = FuncDoc(name) |
| self._funcs[name] = f |
| return f |
| |
| def go(self): |
| try: |
| fp = open(self._filename) |
| except IOError: |
| return |
| state = CParser.STATE_OTHER |
| f = None |
| cont = None |
| while 1: |
| line = fp.readline() |
| if not line: break |
| if state == CParser.STATE_OTHER: |
| m = CParser.RE_C_comment.search (line) |
| if m: |
| line = m.group(1) |
| state = CParser.STATE_COMT |
| else: |
| m = CParser.RE_C_define.match(line) |
| if m: continue |
| m = CParser.RE_C_typedef.search(line) |
| if m: continue |
| m = CParser.RE_C_func_def.match(line) |
| if m: |
| func_name = m.group(3) |
| f = self.func(func_name) |
| f._defn = line |
| else: |
| m = CParser.RE_C_func_def_b.match(line) |
| if m: |
| state = CParser.STATE_FUNC |
| func_name = m.group(3) |
| f = self.func(func_name) |
| f._defn = line |
| continue |
| if state == CParser.STATE_COMT: |
| if string.find(line, "*/") != -1: |
| state = CParser.STATE_OTHER |
| continue |
| m = CParser.RE_C_func_com.search(line) |
| if m: |
| cont = "func" |
| f = self.func(m.group(1)) |
| f._title = m.group(2) |
| continue |
| m = CParser.RE_C_desc_com.search(line) |
| if m: |
| cont = "desc" |
| f._desc = m.group(1) |
| continue |
| m = CParser.RE_C_args_com.search(line) |
| if m: |
| cont = "args" |
| f._args = m.group(2) |
| continue |
| m = CParser.RE_C_retr_com.search(line) |
| if m: |
| cont = "retr" |
| f._retr = m.group(2) |
| continue |
| m = CParser.RE_C_out_com.search(line) |
| if m: |
| cont = "out" |
| f._output = m.group(1) |
| continue |
| m = CParser.RE_C_other_com.search(line) |
| if not f: continue |
| if m: |
| cont = "other" |
| f._other = f._other + "%s: %s" % (m.group(1), m.group(2)) |
| continue |
| m = CParser.RE_C_com_cont.search(line) |
| if m: |
| if cont == "func": |
| f._title = f._title + '\n' + m.group(1) |
| elif cont == "desc": |
| f._desc = f._desc + '\n'+ m.group(1) |
| elif cont == "args": |
| f._args = f._args + '\n' + m.group(1) |
| elif cont == "retr": |
| f._retr = f._retr + '\n' + m.group(1) |
| elif cont == "out": |
| f._output = f._output + '\n' + m.group(1) |
| elif cont == "other": |
| f._other = f._other + '\n' + m.group(1) |
| elif state == CParser.STATE_FUNC: |
| f._defn = f._defn+line |
| if string.find(line, ");") != -1: |
| state = CParser.STATE_OTHER |
| |
| def dump(self): |
| for name in self._funcs.keys(): |
| # print name |
| print "%s\n" % self._funcs[name] |
| |
| def dump_manpages(self, directory, owner): |
| global QUIET |
| date = time.strftime("%d %B %Y", time.localtime(time.time())) |
| for name, f in self._funcs.items(): |
| if f._title is None and f._desc is None and f._args is None and f._retr is None: |
| if not QUIET: |
| sys.stderr.write('-W- No info for function "%s()"\n' % name) |
| continue |
| if f._defn is None: |
| if not QUIET: |
| sys.stderr.write('-W- No defn for function "%s()"\n' % name) |
| fp = open("%s/%s.3" % (directory, name), "w") |
| fp.write('.TH %s 3 "%s" "%s" "%s"\n\n' % (name, date, owner, self._filename)) |
| fp.write('.de Ss\n.sp\n.ft CW\n.nf\n..\n') |
| fp.write('.de Se\n.fi\n.ft P\n.sp\n..\n') |
| fp.write('.SH NAME\n') |
| if f._title is None: |
| fp.write('%s\n' % f._name) |
| else: |
| fp.write('%s %s\n' % (f._name, f._title)) |
| fp.write('.SH SYNOPSIS\n') |
| fp.write('.Ss\n#include <%s>\n.Se\n' % self._filename) |
| if f._defn: |
| fp.write('.Ss\n%s\n.Se\n' % f._defn) |
| else: |
| fp.write('.Ss\n%s()\n.Se\n' % f._name) |
| fp.write('\n') |
| if f._args: |
| fp.write('.SH ARGUMENTS\n') |
| fp.write('%s\n\n' % string.replace(f._args, '\n', '\n.br\n')) |
| if f._desc or string.strip(f._other): |
| fp.write('.SH DESCRIPTION\n') |
| if f._desc: fp.write('%s\n\n' % f._desc) |
| if string.strip(f._other): fp.write('%s\n\n' % f._other) |
| if f._output: |
| fp.write('.SH "RETURN VALUE"\n') |
| fp.write('%s\n\n' % string.replace(f._output, '\n', '\n.br\n')) |
| fp.write('.SH "SEE ALSO"\n') |
| fp.write('.BR %s\n' % string.join(self._funcs.keys(), ' "(3), "')) |
| fp.close() |
| |
| def dump_hdf (self, directory, owner): |
| global QUIET |
| sys.path.insert (0, "../python") |
| sys.path.insert (0, "python") |
| import neo_cgi, neo_util |
| hdf = neo_util.HDF() |
| date = time.strftime("%d %B %Y", time.localtime(time.time())) |
| if not self._funcs.items(): return |
| for name, f in self._funcs.items(): |
| if f._title is None and f._desc is None and f._args is None and f._retr is None: |
| if not QUIET: |
| sys.stderr.write('-W- No info for function "%s()"\n' % name) |
| continue |
| if f._defn is None: |
| if not QUIET: |
| sys.stderr.write('-W- No defn for function "%s()"\n' % name) |
| hdf.setValue ("Code.%s" % name, name) |
| obj = hdf.getObj ("Code.%s" % name) |
| obj.setValue ("Name", name) |
| obj.setValue ("filename", self._filename) |
| if f._title: obj.setValue ("Title", f._title) |
| if f._defn: obj.setValue ("Define", neo_cgi.text2html(f._defn)) |
| if f._args: obj.setValue ("Args", neo_cgi.text2html(f._args)) |
| if f._desc: obj.setValue ("Desc", neo_cgi.text2html(f._desc)) |
| if string.strip(f._other): obj.setValue ("Other", neo_cgi.text2html(string.strip(f._other))) |
| if f._output: obj.setValue ("Output", neo_cgi.text2html(f._output)) |
| n = 0 |
| for func in self._funcs.keys(): |
| obj.setValue ("related.%d" % n, func) |
| n = n + 1 |
| |
| fname = self._filename |
| x = string.rindex (fname, "/") |
| if x != -1: fname = fname[x+1:] |
| x = string.rindex (fname, '.') |
| if x != -1: fname = fname[:x] |
| |
| hdf.writeFile ("%s/%s.hdf" % (directory, fname)) |
| |
| def main(argv, environ): |
| alist, args = getopt.getopt(argv[1:], "q", ["help", "outdir=", "owner=", "hdf"]) |
| |
| outdir = "." |
| owner = "" |
| do_hdf = 0 |
| for (field, val) in alist: |
| if field == "--help": |
| usage (argv[0]) |
| return |
| if field == "--outdir": |
| outdir = val |
| if field == "--owner": |
| owner = val |
| if field == "-q": |
| global QUIET |
| QUIET = 1 |
| if field == "--hdf": |
| do_hdf = 1 |
| |
| if args: |
| for file in args: |
| parser = CParser(file) |
| parser.go() |
| if not do_hdf: |
| parser.dump_manpages(outdir, owner) |
| else: |
| parser.dump_hdf (outdir, owner) |
| |
| |
| if __name__ == "__main__": |
| main (sys.argv, os.environ) |