Commit a728885b authored by Guido van Rossum's avatar Guido van Rossum

Rewrite parsesequence() to emulate MH without invoking pick.

Test it extensively by using pick.
parent f2954ee1
...@@ -78,6 +78,7 @@ import string ...@@ -78,6 +78,7 @@ import string
import mimetools import mimetools
import multifile import multifile
import shutil import shutil
from bisect import bisect
# Exported constants # Exported constants
...@@ -332,49 +333,131 @@ class Folder: ...@@ -332,49 +333,131 @@ class Folder:
# Return the current message. Raise KeyError when there is none # Return the current message. Raise KeyError when there is none
def getcurrent(self): def getcurrent(self):
return min(self.getsequences()['cur']) seqs = self.getsequences()
try:
return max(seqs['cur'])
except (ValueError, KeyError):
raise Error, "no cur message"
# Set the current message # Set the current message
def setcurrent(self, n): def setcurrent(self, n):
updateline(self.getsequencesfilename(), 'cur', str(n), 0) updateline(self.getsequencesfilename(), 'cur', str(n), 0)
# Parse an MH sequence specification into a message list. # Parse an MH sequence specification into a message list.
# Attempt to mimic mh-sequence(5) as close as possible.
# Also attempt to mimic observed behavior regarding which
# conditions cause which error messages
def parsesequence(self, seq): def parsesequence(self, seq):
if seq == "all": # XXX Still not complete (see mh-format(5)).
return self.listmessages() # Missing are:
try: # - 'prev', 'next' as count
n = string.atoi(seq, 10) # - Sequence-Negation option
except string.atoi_error: all = self.listmessages()
n = 0 # Observed behavior: test for empty folder is done first
if n > 0: if not all:
return [n] raise Error, "no messages in %s" % self.name
if regex.match("^last:", seq) >= 0: # Common case first: all is frequently the default
try: if seq == 'all':
n = string.atoi(seq[5:]) return all
except string.atoi_error: # Test for X:Y before X-Y because 'seq:-n' matches both
n = 0 i = string.find(seq, ':')
if n > 0: if i >= 0:
return self.listmessages()[-n:] head, dir, tail = seq[:i], '', seq[i+1:]
if regex.match("^first:", seq) >= 0: if tail[:1] in '-+':
dir, tail = tail[:1], tail[1:]
if not isnumeric(tail):
raise Error, "bad message list %s" % seq
try:
count = string.atoi(tail)
except (ValueError, OverflowError):
# Can't use sys.maxint because of i+count below
count = len(all)
try:
anchor = self._parseindex(head, all)
except Error, msg:
seqs = self.getsequences()
if not seqs.has_key(head):
if not msg:
msg = "bad message list %s" % seq
raise Error, msg, sys.exc_traceback
msgs = seqs[head]
if not msgs:
raise Error, "sequence %s empty" % head
if dir == '-':
return msgs[-count:]
else:
return msgs[:count]
else:
if not dir:
if head in ('prev', 'last'):
dir = '-'
if dir == '-':
i = bisect(all, anchor)
return all[max(0, i-count):i]
else:
i = bisect(all, anchor-1)
return all[i:i+count]
# Test for X-Y next
i = string.find(seq, '-')
if i >= 0:
begin = self._parseindex(seq[:i], all)
end = self._parseindex(seq[i+1:], all)
i = bisect(all, begin-1)
j = bisect(all, end)
r = all[i:j]
if not r:
raise Error, "bad message list %s" % seq
return r
# Neither X:Y nor X-Y; must be a number or a (pseudo-)sequence
try: try:
n = string.atoi(seq[6:]) n = self._parseindex(seq, all)
except string.atoi_error: except Error, msg:
n = 0 seqs = self.getsequences()
if n > 0: if not seqs.has_key(seq):
return self.listmessages()[:n] if not msg:
dict = self.getsequences() msg = "bad message list %s" % seq
if dict.has_key(seq): raise Error, msg
return dict[seq] return seqs[seq]
context = self.mh.getcontext() else:
# Complex syntax -- use pick if n not in all:
pipe = os.popen("pick +%s %s 2>/dev/null" % (self.name, seq)) if isnumeric(seq):
data = pipe.read() raise Error, \
sts = pipe.close() "message %d doesn't exist" % n
self.mh.setcontext(context) else:
if sts: raise Error, "no %s message" % seq
raise Error, "unparseable sequence %s" % `seq` else:
items = string.split(data) return [n]
return map(string.atoi, items)
# Internal: parse a message number (or cur, first, etc.)
def _parseindex(self, seq, all):
if isnumeric(seq):
try:
return string.atoi(seq)
except (OverflowError, ValueError):
return sys.maxint
if seq in ('cur', '.'):
return self.getcurrent()
if seq == 'first':
return all[0]
if seq == 'last':
return all[-1]
if seq == 'next':
n = self.getcurrent()
i = bisect(all, n)
try:
return all[i]
except IndexError:
raise Error, "no next message"
if seq == 'prev':
n = self.getcurrent()
i = bisect(all, n-1)
if i == 0:
raise Error, "no prev message"
try:
return all[i-1]
except IndexError:
raise Error, "no prev message"
raise Error, None
# Open a message -- returns a Message object # Open a message -- returns a Message object
def openmessage(self, n): def openmessage(self, n):
...@@ -704,8 +787,7 @@ class IntSet: ...@@ -704,8 +787,7 @@ class IntSet:
alo, ahi = self.pairs[i-1] alo, ahi = self.pairs[i-1]
blo, bhi = self.pairs[i] blo, bhi = self.pairs[i]
if ahi >= blo-1: if ahi >= blo-1:
self.pairs[i-1:i+1] = [ self.pairs[i-1:i+1] = [(alo, max(ahi, bhi))]
(alo, max(ahi, bhi))]
else: else:
i = i+1 i = i+1
...@@ -883,8 +965,20 @@ def test(): ...@@ -883,8 +965,20 @@ def test():
do('mh.getcontext()') do('mh.getcontext()')
context = mh.getcontext() context = mh.getcontext()
f = mh.openfolder(context) f = mh.openfolder(context)
do('f.listmessages()')
do('f.getcurrent()') do('f.getcurrent()')
for seq in ['first', 'last', 'cur', '.', 'prev', 'next',
'first:3', 'last:3', 'cur:3', 'cur:-3',
'prev:3', 'next:3',
'1:3', '1:-3', '100:3', '100:-3', '10000:3', '10000:-3',
'all']:
try:
do('f.parsesequence(%s)' % `seq`)
except Error, msg:
print "Error:", msg
stuff = os.popen("pick %s 2>/dev/null" % `seq`).read()
list = map(string.atoi, string.split(stuff))
print list, "<-- pick"
do('f.listmessages()')
if __name__ == '__main__': if __name__ == '__main__':
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment