#! /usr/bin/env python

import errno
import os
import re
import sys

if __name__ == "__main__":
    _base = sys.argv[0]
else:
    _base = __file__

_script_home = os.path.abspath(os.path.dirname(_base))

srcdir = os.path.dirname(os.path.dirname(_script_home))

EXCLUDES = ["bitset.h", "cStringIO.h", "graminit.h", "grammar.h",
            "longintrepr.h", "metagrammar.h",
            "node.h", "opcode.h", "osdefs.h", "pgenheaders.h",
            "py_curses.h", "parsetok.h", "symtable.h", "token.h"]


def list_headers():
    """Return a list of headers."""
    incdir = os.path.join(srcdir, "Include")
    return [fn for fn in os.listdir(incdir)
            if fn.endswith(".h") and fn not in EXCLUDES]


def matcher(pattern):
    return re.compile(pattern).match

MATCHERS = [
    matcher(r"\\begin\{cfuncdesc\}\{[^{]*\}\{(?P<sym>[^{]*)\}"),
    matcher(r"\\cfuncline\{[^{]*\}\{(?P<sym>[^{]*)\}"),
    matcher(r"\\begin\{ctypedesc\}(\[[^{]*\])?\{(?P<sym>[^{]*)\}"),
    matcher(r"\\begin\{cvardesc\}\{[^{]*\}\{(?P<sym>[^{]*)\}"),
    matcher(r"\\begin\{cmemberdesc\}\{[^{]*\}\{(?P<sym>[^{]*)\}"),
    matcher(r"\\cmemberline\{[^{]*\}\{(?P<sym>[^{]*)\}"),
    matcher(r"\\begin\{csimplemacrodesc\}\{(?P<sym>[^{]*)\}"),
    ]


def list_documented_items():
    """Return a list of everything that's already documented."""
    apidir = os.path.join(srcdir, "Doc", "api")
    files = [fn for fn in os.listdir(apidir) if fn.endswith(".tex")]
    L = []
    for fn in files:
        fullname = os.path.join(apidir, fn)
        for line in open(fullname):
            line = line.lstrip()
            if not line.startswith("\\"):
                continue
            for matcher in MATCHERS:
                m = matcher(line)
                if m:
                    L.append(m.group("sym"))
                    break
    return L

def split_documented(all, documented):
    """Split the list of all symbols into documented and undocumented
    categories."""
    doc = []
    undoc = []
    for t in all:
        if t[0] in documented:
            doc.append(t)
        else:
            undoc.append(t)
    return doc, undoc

def print_list(L, title=None):
    """Dump a list to stdout."""
    if title:
        print title + ":"
        print "-" * (len(title) + 1)
    w = 0
    for sym, filename in L:
        w = max(w, len(sym))
    if w % 4 == 0:
        w += 4
    else:
        w += (4 - (w % 4))
    for sym, filename in L:
        print "%-*s%s" % (w, sym, filename)


_spcjoin = ' '.join

def main():
    args = sys.argv[1:]
    if args:
        headers = args
        documented = []
    else:
        os.chdir(os.path.join(srcdir, "Include"))
        headers = list_headers()
        documented = list_documented_items()

    cmd = ("ctags -f - --file-scope=no --c-types=dgpstux "
           "-Istaticforward -Istatichere=static "
           + _spcjoin(headers))
    fp = os.popen(cmd)
    L = []
    prevsym = None
    while 1:
        line = fp.readline()
        if not line:
            break
        sym, filename = line.split()[:2]
        if sym == prevsym:
            continue
        if not sym.endswith("_H"):
            L.append((sym, filename))
            prevsym = sym
    L.sort()
    fp.close()

    try:
        if documented:
            documented, undocumented = split_documented(L, documented)
            print_list(documented, "Documented symbols")
            if undocumented:
                print
                print_list(undocumented, "Undocumented symbols")
        else:
            print_list(L)
    except IOError, e:
        if e.errno != errno.EPIPE:
            raise


if __name__ == "__main__":
    main()
