#!/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)