# Author: Srinath Avadhanula
# This file is distributed as part of the vim-latex project
# http://vim-latex.sf.net
import re
class Bibliography(dict):
def __init__(self, txt, macros={}):
"""
txt:
a string which represents the entire bibtex entry. A typical
entry is of the form:
@ARTICLE{ellington:84:part3,
author = {Ellington, C P},
title = {The Aerodynamics of Hovering Insect Flight. III. Kinematics},
journal = {Philosophical Transactions of the Royal Society of London. Series B, Biological Sciences},
year = {1984},
volume = {305},
pages = {41-78},
number = {1122},
owner = {Srinath},
pdf = {C:\srinath\research\papers\Ellington-3-Kinematics.pdf},
timestamp = {2006.01.02},
}
"""
if macros:
for k, v in macros.iteritems():
txt = txt.replace(k, '{'+v+'}')
m = re.match(r'\s*@(\w+){((\S+),)?(.*)}\s*', txt, re.MULTILINE | re.DOTALL)
if not m:
return None
self['bibtype'] = m.group(1).capitalize()
self['key'] = m.group(3)
self['body'] = m.group(4)
body = self['body']
self['bodytext'] = ''
while 1:
m = re.search(r'(\S+?)\s*=\s*(.)', body)
if not m:
break
field = m.group(1)
body = body[(m.start(2)+1):]
if m.group(2) == '{':
# search for the next closing brace. This is not simply a
# matter of searching for the next closing brace since
# braces can be nested. The following code basically goes
# to the next } which has not already been closed by a
# following {.
mniter = re.finditer(r'{|}', body)
count = 1
while 1:
try:
mn = mniter.next()
except StopIteration:
return None
if mn.group(0) == '{':
count += 1
else:
count -= 1
if count == 0:
value = body[:(mn.start(0))]
break
elif m.group(2) == '"':
# search for the next unquoted double-quote. To be more
# precise, a double quote which is preceded by an even
# number of double quotes.
mn = re.search(r'(?!\\)(\\\\)*"', body)
if not mn:
return None
value = body[:(mn.start(0))]
else:
# $ always matches. So we do not need to do any
# error-checking.
mn = re.search(r',|$', body)
value = m.group(2) + body[:(mn.start(0))].rstrip()
self[field] = re.sub(r'\s+', ' ', value)
body = body[(mn.start(0)+1):]
self['bodytext'] += (' %s: %s\n' % (field, value))
if self['bibtype'].lower() == 'string':
self['macro'] = {field: value}
self['bodytext'] = self['bodytext'].rstrip()
def __getitem__(self, key):
try:
return dict.__getitem__(self, key)
except KeyError:
return ''
def __str__(self):
if self['bibtype'].lower() == 'string':
return 'String: %(macro)s' % self
elif self['bibtype'].lower() == 'article':
return ('Article [%(key)s]\n' +
'TI "%(title)s"\n' +
'AU %(author)s\n' +
'IN In %(journal)s, %(year)s') % self
elif self['bibtype'].lower() == 'conference':
return ('Conference [%(key)s]\n' +
'TI "%(title)s"\n' +
'AU %(author)s\n' +
'IN In %(booktitle)s, %(year)s') % self
elif self['bibtype'].lower() == 'mastersthesis':
return ('Masters [%(key)s]\n' +
'TI "%(title)s"\n' +
'AU %(author)s\n' +
'IN In %(school)s, %(year)s') % self
elif self['bibtype'].lower() == 'phdthesis':
return ('PhD [%(key)s]\n' +
'TI "%(title)s"\n' +
'AU %(author)s\n' +
'IN In %(school)s, %(year)s') % self
elif self['bibtype'].lower() == 'book':
return ('Book [%(key)s]\n' +
'TI "%(title)s"\n' +
'AU %(author)s\n' +
'IN %(publisher)s, %(year)s') % self
else:
s = '%(bibtype)s [%(key)s]\n' % self
if self['title']:
s += 'TI "%(title)s"\n' % self
if self['author']:
s += 'AU %(author)s\n' % self
for k, v in self.iteritems():
if k not in ['title', 'author', 'bibtype', 'key', 'id', 'file', 'body', 'bodytext']:
s += 'MI %s: %s\n' % (k, v)
return s.rstrip()
def satisfies(self, filters):
for field, regexp in filters:
if not re.search(regexp, self[field], re.I):
return False
return True
class BibFile:
def __init__(self, filelist=''):
self.bibentries = []
self.filters = []
self.macros = {}
self.sortfields = []
if filelist:
for f in filelist.splitlines():
self.addfile(f)
def addfile(self, file):
fields = open(file).read().split('@')
for f in fields:
if not (f and re.match('string', f, re.I)):
continue
b = Bibliography('@' + f)
self.macros.update(b['macro'])
for f in fields:
if not f or re.match('string', f, re.I):
continue
b = Bibliography('@' + f, self.macros)
if b:
b['file'] = file
b['id'] = len(self.bibentries)
self.bibentries += [b]
def addfilter(self, filterspec):
self.filters += [filterspec.split()]
def rmfilters(self):
self.filters = []
def __str__(self):
s = ''
for b in self.bibentries:
if b['key'] and b.satisfies(self.filters):
s += '%s\n\n' % b
return s
def addsortfield(self, field):
self.sortfields += [field]
def rmsortfields(self):
self.sortfields = []
def sort(self):
def cmpfun(b1, b2):
for f in self.sortfields:
c = cmp(b1[f], b2[f])
if c:
return c
return 0
self.bibentries.sort(cmp=cmpfun)
if __name__ == "__main__":
import sys
bf = BibFile(sys.argv[1])
print bf