| # A class that makes each part of a multipart message "feel" like an |
| # ordinary file, as long as you use fp.readline(). Allows recursive |
| # use, for nested multipart messages. Probably best used together |
| # with module mimetools. |
| # |
| # Suggested use: |
| # |
| # real_fp = open(...) |
| # fp = MultiFile(real_fp) |
| # |
| # "read some lines from fp" |
| # fp.push(separator) |
| # while 1: |
| # "read lines from fp until it returns an empty string" (A) |
| # if not fp.next(): break |
| # fp.pop() |
| # "read remaining lines from fp until it returns an empty string" |
| # |
| # The latter sequence may be used recursively at (A). |
| # It is also allowed to use multiple push()...pop() sequences. |
| # Note that if a nested multipart message is terminated by a separator |
| # for an outer message, this is not reported, even though it is really |
| # illegal input. |
| |
| import sys |
| import string |
| |
| err = sys.stderr.write |
| |
| Error = 'multifile.Error' |
| |
| class MultiFile: |
| # |
| def __init__(self, fp): |
| self.fp = fp |
| self.stack = [] # Grows down |
| self.level = 0 |
| self.last = 0 |
| self.start = self.fp.tell() |
| self.posstack = [] # Grows down |
| # |
| def tell(self): |
| if self.level > 0: |
| return self.lastpos |
| return self.fp.tell() - self.start |
| # |
| def seek(self, pos): |
| if not 0 <= pos <= self.tell() or \ |
| self.level > 0 and pos > self.lastpos: |
| raise Error, 'bad MultiFile.seek() call' |
| self.fp.seek(pos + self.start) |
| self.level = 0 |
| self.last = 0 |
| # |
| def readline(self): |
| if self.level > 0: return '' |
| line = self.fp.readline() |
| if not line: |
| self.level = len(self.stack) |
| self.last = (self.level > 0) |
| if self.last: |
| err('*** Sudden EOF in MultiFile.readline()\n') |
| return '' |
| if line[:2] <> '--': return line |
| n = len(line) |
| k = n |
| while k > 0 and line[k-1] in string.whitespace: k = k-1 |
| mark = line[2:k] |
| if mark[-2:] == '--': mark1 = mark[:-2] |
| else: mark1 = None |
| for i in range(len(self.stack)): |
| sep = self.stack[i] |
| if sep == mark: |
| self.last = 0 |
| break |
| elif mark1 <> None and sep == mark1: |
| self.last = 1 |
| break |
| else: |
| return line |
| # Get here after break out of loop |
| self.lastpos = self.tell() - len(line) |
| self.level = i+1 |
| if self.level > 1: |
| err('*** Missing endmarker in MultiFile.readline()\n') |
| return '' |
| # |
| def readlines(self): |
| list = [] |
| while 1: |
| line = self.readline() |
| if not line: break |
| list.append(line) |
| return list |
| # |
| def read(self): # Note: no size argument -- read until EOF only! |
| return string.joinfields(self.readlines(), '') |
| # |
| def next(self): |
| while self.readline(): pass |
| if self.level > 1 or self.last: |
| return 0 |
| self.level = 0 |
| self.last = 0 |
| self.start = self.fp.tell() |
| return 1 |
| # |
| def push(self, sep): |
| if self.level > 0: |
| raise Error, 'bad MultiFile.push() call' |
| self.stack.insert(0, sep) |
| self.posstack.insert(0, self.start) |
| self.start = self.fp.tell() |
| # |
| def pop(self): |
| if self.stack == []: |
| raise Error, 'bad MultiFile.pop() call' |
| if self.level <= 1: |
| self.last = 0 |
| else: |
| abslastpos = self.lastpos + self.start |
| self.level = max(0, self.level - 1) |
| del self.stack[0] |
| self.start = self.posstack[0] |
| del self.posstack[0] |
| if self.level > 0: |
| self.lastpos = abslastpos - self.start |
| # |