Home | History | Annotate | Download | only in scripts
      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