#!/usr/bin/python
#
# lincredits -- Beautify the Linux CREDITS file in various formats
#   Written by Chris Lawrence <lawrencc@debian.org>
#   (C) 1999-2002 Chris Lawrence
#
# This program is freely distributable per the following license:
#
##  Permission to use, copy, modify, and distribute this software and its
##  documentation for any purpose and without fee is hereby granted,
##  provided that the above copyright notice appear in all copies and that
##  both that copyright notice and this permission notice appear in
##  supporting documentation.
##
##  I DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
##  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL I
##  BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
##  DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
##  WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
##  ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
##  SOFTWARE.
#
# Version 0.4: 12 January 1999
#
# TODO: Add support for the MAINTAINERS file format
#       [Unfortunately the S tag means different things in the two formats,
#        and each block starts with a line with no header]

import sys, string, getopt, re

def parseblock(lines):
    creditinfo = {}
    creditline = re.compile(r':\s+')
    for line in lines:
        dat = creditline.split(line, 1)
        # Skip improperly formatted headers
        if len(dat) < 2:
            continue
        (type, data) = dat
        if type == 'N':
            creditinfo['name'] = data
        elif type in ('E', 'M'):
            creditinfo['email'] = creditinfo.get('email', []) + [data]
        elif type == 'P':
            creditinfo['pgp'] = creditinfo.get('pgp', []) + [data]
        elif type == 'W':
            creditinfo['url'] = creditinfo.get('url', []) + [data]
        elif type == 'D':
            creditinfo['desc'] = creditinfo.get('desc', []) + [data]
        elif type == 'S':
            creditinfo['mail'] = creditinfo.get('mail', []) + [data]
        else:
            print 'Warning: Unknown CREDITS header:', type

    return creditinfo

def parse(file):
    lines = file.readlines()
    nameline = re.compile('N:')
    linebuff = []
    peoplelist = []
    people = {}
    for line in lines:
        if line[-1] == '\n':
            line = line[:-1]
        if nameline.match(line):
            linebuff = [line]
        elif linebuff and not line:
            info = parseblock(linebuff)
            if info.has_key('name'):
                people[info['name']] = info
                peoplelist.append(info['name'])
            else:
                print 'Warning: CREDITS entry without name:', info
            linebuff = []
        elif linebuff:
            linebuff.append(line)

    if linebuff: # Flush any remaining data
        info = parseblock(linebuff)
        if info.has_key('name'):
            people[info['name']] = info
            peoplelist.append(info['name'])
        else:
            print 'Warning: CREDITS entry without name:', info

    return peoplelist, people

def format_text(names, info, filename, outfile):
    outfile.write( 'CREDITS from %s\n\n' % filename )
    
    for name in names:
        outfile.write(name)
        if info[name].has_key('email'):
            outfile.write(' <'+info[name]['email'][0]+'>:\n')
            bits = info[name]['email'][1:]
            if bits:
                outfile.write('  Alternate addresses:\n')
                for bit in bits:
                    outfile.write('    <'+bit+'>\n')
        else:
            outfile.write(':\n')
        if info[name].has_key('url'):
            outfile.write('  Website:\n')
            bits = info[name]['url']
            for bit in bits:
                outfile.write('    '+bit+'\n')
        if info[name].has_key('pgp'):
            outfile.write('  PGP key fingerprint:\n')
            bits = info[name]['pgp']
            for bit in bits:
                outfile.write('    '+bit+'\n')
        if info[name].has_key('desc'):
            bits = info[name]['desc']
            outfile.write('  Contributions:\n')
            for bit in bits:
                outfile.write('    '+bit+'\n')
        if info[name].has_key('mail'):
            bits = info[name]['mail']
            outfile.write('  Physical address:\n')
            for bit in bits:
                outfile.write('    '+bit+'\n')
        outfile.write('\n')
    return

def webify(str):
    str = string.replace(str, '<', '&lt;')
    str = string.replace(str, '>', '&gt;')
    str = string.replace(str, '"', '&quot;')
    return str

def make_email_link(addr):
    return '&lt;<A HREF="mailto:%s">%s</A>&gt;' % (addr, webify(addr))

def make_url_link(addr):
    return '<A HREF="%s">%s</A>' % (addr, webify(addr))

def format_html(names, info, filename, outfile):
    outfile.write('<HTML><HEAD>\n')
    outfile.write('<TITLE>CREDITS from %s</TITLE>\n' % webify(filename) )
    outfile.write('</HEAD>\n\n<BODY><DL>\n')
    
    for name in names:
        outfile.write('<DT>'+webify(name))
        if info[name].has_key('email'):
            outfile.write(' '+make_email_link(info[name]['email'][0]))
            outfile.write(':\n<DD><DL>\n')
            bits = info[name]['email'][1:]
            if bits:
                outfile.write('<DT>Alternate addresses:\n<DD><UL>\n')
                for bit in bits:
                    outfile.write('<LI>'+make_email_link(bit)+'\n')
                outfile.write('</UL>\n')
        else:
            outfile.write(':\n<DD><DL>\n')
        if info[name].has_key('url'):
            outfile.write('<DT>Website:\n<DD><UL>\n')
            bits = info[name]['url']
            for bit in bits:
                outfile.write('<LI>'+make_url_link(bit)+'\n')
            outfile.write('</UL>\n')
        if info[name].has_key('pgp'):
            outfile.write('<DT>PGP key fingerprint:\n<DD><UL>\n')
            bits = info[name]['pgp']
            for bit in bits:
                outfile.write('<LI><TT>'+webify(bit)+'</TT>\n')
            outfile.write('</UL>\n')
        if info[name].has_key('desc'):
            bits = info[name]['desc']
            outfile.write('<DT>Contributions:\n<DD><UL>\n')
            for bit in bits:
                outfile.write('<LI>'+webify(bit)+'\n')
            outfile.write('</UL>\n')
        if info[name].has_key('mail'):
            bits = info[name]['mail']
            outfile.write('<DT>Physical address:\n<DD>\n')
            for bit in bits:
                outfile.write(webify(bit)+'<BR>\n')
        outfile.write('</DL>\n')

    outfile.write('</DL></BODY>\n</HTML>\n')
    return

# Swiped from running GNU recode -h
texfix = [
    "~", "!`", "", "\\pound{}",	# 160-163
    "", "", "", "\\S{}",        # 164-167
    "\\\"{}",			# 168
    "\\copyright{}",		# 169
    "",				# 170
    "``",			# 171
    "\\neg{}",			# 172
    "\\-",			# 173
    "",				# 174
    "",				# 175
    "\\mbox{$^\\circ$}",	# 176
    "\\mbox{$\\pm$}",		# 177
    "\\mbox{$^2$}",		# 178
    "\\mbox{$^3$}",		# 179
    "\\'{}",			# 180
    "\\mbox{$\\mu$}",		# 181
    "",				# 182
    "\\cdotp",			# 183
    "\\,{}",			# 184
    "\\mbox{$^1$}",		# 185
    "",				# 186
    "''",			# 187
    "\\frac1/4{}",		# 188
    "\\frac1/2{}",		# 189
    "\\frac3/4{}",		# 190
    "?`",			# 191
    "\\`A",			# 192
    "\\'A",			# 193
    "\\^A",			# 194
    "\\~A",			# 195
    "\\\"A",			# 196
    "\\AA{}",			# 197
    "\\AE{}",			# 198
    "\\c{C}",			# 199
    "\\`E",			# 200
    "\\'E",			# 201
    "\\^E",			# 202
    "\\\"E",			# 203
    "\\`I",			# 204
    "\\'I",			# 205
    "\\^I",			# 206
    "\\\"I",			# 207
    "",				# 208
    "\\~N",			# 209
    "\\`O",			# 210
    "\\'O",			# 211
    "\\^O",			# 212
    "\\~O",			# 213
    "\\\"O",			# 214
    "",				# 215
    "\\O{}",			# 216
    "\\`U",			# 217
    "\\'U",			# 218
    "\\^U",			# 219
    "\\\"U",			# 220
    "\\'Y",			# 221
    "",				# 222
    "\\ss{}",			# 223
    "\\`a",			# 224
    "\\'a",			# 225
    "\\^a",			# 226
    "\\~a",			# 227
    "\\\"a",			# 228
    "\\aa{}",			# 229
    "\\ae{}",			# 230
    "\\c{c}",			# 231
    "\\`e",			# 232
    "\\'e",			# 233
    "\\^e",			# 234
    "\\\"e",			# 235
    "\\`{\\i}",			# 236
    "\\'{\\i}",			# 237
    "\\^{\\i}",			# 238
    "\\\"{\\i}",		# 239
    "",				# 240
    "\\~n",			# 241
    "\\`o",			# 242
    "\\'o",			# 243
    "\\^o",			# 244
    "\\~o",			# 245
    "\\\"o",			# 246
    "",				# 247
    "\\o{}",			# 248
    "\\`u",			# 249
    "\\'u",			# 250
    "\\^u",			# 251
    "\\\"u",			# 252
    "\\'y",			# 253
    "",				# 254
    "\\\"y",			# 255
]

def texify(text, href=False):
    for ch in '#$%&_{}':
        text = text.replace(ch, '\\'+ch)
    if href:
        return text

    text = text.replace('<', '$<$')
    text = text.replace('>', '$>$')
    text = text.replace('~', '\~{}')
    for i in range(160, 255):
        text = text.replace(chr(i), texfix[i-160])
    return text

def format_latex(names, info, filename, outfile):
    outfile.write('\\documentclass{article}\n')
    print >> outfile, '\\usepackage{hyperref}'
    outfile.write('\\title{CREDITS from %s}\n' % texify(filename))
    outfile.write('\\author{Generated by \\texttt{lincredits}}\n')
    outfile.write('\\setlength{\parindent}{0in}\n')
    outfile.write('\\begin{document}\n\\maketitle\n')
    
    for name in names:
        if name == 'Jeff Garzik':
            print >> sys.stderr, repr(info[name])
        
        thisinfo = info[name]
        outfile.write('\\begin{minipage}{\\textwidth}\n')
        outfile.write(texify(name))
        keys = thisinfo.keys()
        madelist = False
        if 'email' in thisinfo:
            addr = thisinfo['email'][0]
            addr = texify(addr, href=True)
            print >> outfile, ' $<$\\href{mailto:%s}{%s}$>$:' % (addr, addr)
            bits = thisinfo['email'][1:]
            if bits:
                outfile.write('\\begin{list}{}{}\n')
                outfile.write('\\item Alternate addresses:\n')
                outfile.write('\\begin{itemize}\n')
                for bit in bits:
                    bit = texify(bit, href=True)
                    print >> outfile, '\\item $<$\\href{mailto:%s}{%s}$>$' % (bit, bit)
                outfile.write('\\end{itemize}\n')
                madelist=True
            elif len(keys) > 2:
                outfile.write('\\begin{list}{}{}\n')
                madelist=True
        elif len(keys) > 1:
            outfile.write(':\n\\begin{list}{}{}\n')
            madelist=True
            
        if thisinfo.has_key('url'):
            outfile.write('\\item Website:\n')
            outfile.write('\\begin{itemize}\n')
            bits = thisinfo['url']
            for bit in bits:
                bit = texify(bit, href=True)
                print >> outfile, '\\item \url{%s}' % (bit)
            outfile.write('\\end{itemize}\n')
        if thisinfo.has_key('pgp'):
            outfile.write('\\item PGP key fingerprint:\n')
            outfile.write('\\begin{itemize}\n')
            bits = thisinfo['pgp']
            for bit in bits:
                outfile.write('\\item \\texttt{'+texify(bit)+'}\n')
            outfile.write('\\end{itemize}\n')
        if thisinfo.has_key('desc'):
            bits = thisinfo['desc']
            outfile.write('\\item Contributions:\n')
            outfile.write('\\begin{itemize}\n')
            for bit in bits:
                outfile.write('\\item '+texify(bit)+'\n')
            outfile.write('\\end{itemize}\n')
        if thisinfo.has_key('mail'):
            bits = thisinfo['mail']
            outfile.write('\\item Physical address:\n')
            outfile.write('\\begin{list}{}{}\n')
            for bit in bits:
                outfile.write('\\item '+texify(bit)+'\n')
            outfile.write('\\end{list}\n')

        if madelist:
            outfile.write('\\end{list}\n')
        outfile.write('\\end{minipage}\n\n')
        outfile.write('\\vspace{\\baselineskip}\n')

    outfile.write('\\end{document}')
    return

USAGE = ('lincredits: Parse the Linux CREDITS file\n\n'
         'Usage: lincredits [options] <filename>\n'
         'Supported options:\n'
         '  --text:  Output as formatted text (default)\n'
         '  --html:  Output as HTML formatted text\n'
         '  --latex: Output as LaTeX source\n'
         '  --output <filename>: Output to a file instead of standard output\n'
         '  --help:  Show this help text\n'
         )

def main():
    try:
        (opts, args) = getopt.getopt(sys.argv[1:], 'ho:', ['help', 'text',
                                                         'output=',
                                                         'latex', 'html'])
    except getopt.error, msg:
        print msg
        sys.exit(1)

    format = format_text
    out = ""
    for option, arg in opts:
        if option == '-h' or option == '--help':
            print USAGE
            return
        elif option == '--html':
            format = format_html
        elif option == '--latex':
            format = format_latex
        elif option == '--text':
            format = format_text
        elif option == '-o' or option == '--output':
            out = arg

    if len(args) != 1:
        print 'You must specify the CREDITS file to read.'
        sys.exit(1)

    if out:
        outfile = open(out, 'w')
    else:
        outfile = sys.stdout
        
    infile = open(args[0])
    (names, info) = parse(infile)
    infile.close()

    format(names, info, args[0], outfile)
    if outfile != sys.stdout:
        outfile.close()
    return

if __name__ == '__main__':
    main()
