#!/usr/bin/env python
"""Script to locate email addresses in the CVS logs."""
__version__ = '$Revision$'

import os
import re
import sys
import UserDict

import cvsinfo


class Acknowledgements(UserDict.UserDict):
    def add(self, email, name, path):
        d = self.data
        d.setdefault(email, {})[path] = name


def open_cvs_log(info, paths=None):
    cvsroot = info.get_cvsroot()
    cmd = "cvs -q -d%s log " % cvsroot
    if paths:
        cmd += " ".join(paths)
    return os.popen(cmd, "r")


email_rx = re.compile("<([a-z][-a-z0-9._]*@[-a-z0-9.]+)>", re.IGNORECASE)

def find_acks(f, acks):
    prev = ''
    filename = None
    MAGIC_WORDS = ('van', 'von')
    while 1:
        line = f.readline()
        if not line:
            break
        if line.startswith("Working file: "):
            filename = line.split(None, 2)[2].strip()
            prev = line
            continue
        m = email_rx.search(line)
        if m:
            words = prev.split() + line[:m.start()].split()
            L = []
            while words \
                  and (words[-1][0].isupper() or words[-1] in MAGIC_WORDS):
                L.insert(0, words.pop())
            name = " ".join(L)
            email = m.group(1).lower()
            acks.add(email, name, filename)
        prev = line


def load_cvs_log_acks(acks, args):
    repolist = cvsinfo.get_repository_list(args or [""])
    for info, paths in repolist:
        print >>sys.stderr, "Repository:", info.get_cvsroot()
        f = open_cvs_log(info, paths)
        find_acks(f, acks)
        f.close()


def load_tex_source_acks(acks, args):
    for path in args:
        path = path or os.curdir
        if os.path.isfile(path):
            read_acks_from_tex_file(acks, path)
        else:
            read_acks_from_tex_dir(acks, path)


def read_acks_from_tex_file(acks, path):
    f = open(path)
    while 1:
        line = f.readline()
        if not line:
            break
        if line.startswith(r"\sectionauthor{"):
            line = line[len(r"\sectionauthor"):]
            name, line = extract_tex_group(line)
            email, line = extract_tex_group(line)
            acks.add(email, name, path)


def read_acks_from_tex_dir(acks, path):
    stack = [path]
    while stack:
        p = stack.pop()
        for n in os.listdir(p):
            n = os.path.join(p, n)
            if os.path.isdir(n):
                stack.insert(0, n)
            elif os.path.normpath(n).endswith(".tex"):
                read_acks_from_tex_file(acks, n)


def extract_tex_group(s):
    c = 0
    for i in range(len(s)):
        if s[i] == '{':
            c += 1
        elif s[i] == '}':
            c -= 1
            if c == 0:
                return s[1:i], s[i+1:]


def print_acks(acks):
    first = 1
    for email, D in acks.items():
        if first:
            first = 0
        else:
            print
        L = D.items()
        L.sort()
        prefname = L[0][1]
        for file, name in L[1:]:
            if name != prefname:
                prefname = ""
                break
        if prefname:
            print prefname, "<%s>:" % email
        else:
            print email + ":"
        for file, name in L:
            if name == prefname:
                print "    " + file
            else:
                print "    %s (as %s)" % (file, name)


def print_ack_names(acks):
    names = []
    for email, D in acks.items():
        L = D.items()
        L.sort()
        prefname = L[0][1]
        for file, name in L[1:]:
            prefname = prefname or name
        names.append(prefname or email)
    def f(s1, s2):
        s1 = s1.lower()
        s2 = s2.lower()
        return cmp((s1.split()[-1], s1),
                   (s2.split()[-1], s2))
    names.sort(f)
    for name in names:
        print name


def main():
    args = sys.argv[1:]
    acks = Acknowledgements()
    load_cvs_log_acks(acks, args)
    load_tex_source_acks(acks, args)
    print_ack_names(acks)


if __name__ == "__main__":
    main()
