Rewrite parsesequence() to emulate MH without invoking pick.
Test it extensively by using pick.
diff --git a/Lib/mhlib.py b/Lib/mhlib.py
index 990cd9a..b7acf55 100644
--- a/Lib/mhlib.py
+++ b/Lib/mhlib.py
@@ -78,6 +78,7 @@
 import mimetools
 import multifile
 import shutil
+from bisect import bisect
 
 
 # Exported constants
@@ -332,49 +333,131 @@
 
 	# Return the current message.  Raise KeyError when there is none
 	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
 	def setcurrent(self, n):
 		updateline(self.getsequencesfilename(), 'cur', str(n), 0)
 
 	# 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):
-	    if seq == "all":
-		return self.listmessages()
-	    try:
-		n = string.atoi(seq, 10)
-	    except string.atoi_error:
-		n = 0
-	    if n > 0:
-		return [n]
-	    if regex.match("^last:", seq) >= 0:
+		# XXX Still not complete (see mh-format(5)).
+		# Missing are:
+		# - 'prev', 'next' as count
+		# - Sequence-Negation option
+		all = self.listmessages()
+		# Observed behavior: test for empty folder is done first
+		if not all:
+			raise Error, "no messages in %s" % self.name
+		# Common case first: all is frequently the default
+		if seq == 'all':
+			return all
+		# Test for X:Y before X-Y because 'seq:-n' matches both
+		i = string.find(seq, ':')
+		if i >= 0:
+			head, dir, tail = seq[:i], '', seq[i+1:]
+			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:
-		    n = string.atoi(seq[5:])
-		except string.atoi_error:
-		    n = 0
-		if n > 0:
-		    return self.listmessages()[-n:]
-	    if regex.match("^first:", seq) >= 0:
-		try:
-		    n = string.atoi(seq[6:])
-		except string.atoi_error:
-		    n = 0
-		if n > 0:
-		    return self.listmessages()[:n]
-	    dict = self.getsequences()
-	    if dict.has_key(seq):
-		return dict[seq]
-	    context = self.mh.getcontext()
-	    # Complex syntax -- use pick
-	    pipe = os.popen("pick +%s %s 2>/dev/null" % (self.name, seq))
-	    data = pipe.read()
-	    sts = pipe.close()
-	    self.mh.setcontext(context)
-	    if sts:
-		    raise Error, "unparseable sequence %s" % `seq`
-	    items = string.split(data)
-	    return map(string.atoi, items)
+			n = self._parseindex(seq, all)
+		except Error, msg:
+			seqs = self.getsequences()
+			if not seqs.has_key(seq):
+				if not msg:
+					msg = "bad message list %s" % seq
+				raise Error, msg
+			return seqs[seq]
+		else:
+			if n not in all:
+				if isnumeric(seq):
+					raise Error, \
+					      "message %d doesn't exist" % n
+				else:
+					raise Error, "no %s message" % seq
+			else:
+				return [n]
+
+	# 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
 	def openmessage(self, n):
@@ -704,8 +787,7 @@
 			alo, ahi = self.pairs[i-1]
 			blo, bhi = self.pairs[i]
 			if ahi >= blo-1:
-				self.pairs[i-1:i+1] = [
-				      (alo, max(ahi, bhi))]
+				self.pairs[i-1:i+1] = [(alo, max(ahi, bhi))]
 			else:
 				i = i+1
 
@@ -883,8 +965,20 @@
 	do('mh.getcontext()')
 	context = mh.getcontext()
 	f = mh.openfolder(context)
-	do('f.listmessages()')
 	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__':