blob: 71e0dd0d46843364a4d0e793f107a2627b37a5b4 [file] [log] [blame]
Guido van Rossum741c81a1992-07-13 14:40:45 +00001# A class that makes each part of a multipart message "feel" like an
2# ordinary file, as long as you use fp.readline(). Allows recursive
3# use, for nested multipart messages. Probably best used together
4# with module mimetools.
5#
6# Suggested use:
7#
8# real_fp = open(...)
Guido van Rossum7bc817d1993-12-17 15:25:27 +00009# fp = MultiFile(real_fp)
Guido van Rossum741c81a1992-07-13 14:40:45 +000010#
11# "read some lines from fp"
12# fp.push(separator)
13# while 1:
14# "read lines from fp until it returns an empty string" (A)
15# if not fp.next(): break
16# fp.pop()
17# "read remaining lines from fp until it returns an empty string"
18#
19# The latter sequence may be used recursively at (A).
20# It is also allowed to use multiple push()...pop() sequences.
21# Note that if a nested multipart message is terminated by a separator
22# for an outer message, this is not reported, even though it is really
23# illegal input.
24
25import sys
26import string
27
28err = sys.stderr.write
29
30Error = 'multifile.Error'
31
32class MultiFile:
33 #
Guido van Rossum7bc817d1993-12-17 15:25:27 +000034 def __init__(self, fp):
Guido van Rossum741c81a1992-07-13 14:40:45 +000035 self.fp = fp
36 self.stack = [] # Grows down
37 self.level = 0
38 self.last = 0
39 self.start = self.fp.tell()
40 self.posstack = [] # Grows down
Guido van Rossum741c81a1992-07-13 14:40:45 +000041 #
42 def tell(self):
43 if self.level > 0:
44 return self.lastpos
45 return self.fp.tell() - self.start
46 #
47 def seek(self, pos):
48 if not 0 <= pos <= self.tell() or \
49 self.level > 0 and pos > self.lastpos:
50 raise Error, 'bad MultiFile.seek() call'
51 self.fp.seek(pos + self.start)
52 self.level = 0
53 self.last = 0
54 #
55 def readline(self):
56 if self.level > 0: return ''
57 line = self.fp.readline()
58 if not line:
59 self.level = len(self.stack)
60 self.last = (self.level > 0)
61 if self.last:
62 err('*** Sudden EOF in MultiFile.readline()\n')
63 return ''
64 if line[:2] <> '--': return line
65 n = len(line)
66 k = n
67 while k > 0 and line[k-1] in string.whitespace: k = k-1
68 mark = line[2:k]
69 if mark[-2:] == '--': mark1 = mark[:-2]
70 else: mark1 = None
71 for i in range(len(self.stack)):
72 sep = self.stack[i]
73 if sep == mark:
74 self.last = 0
75 break
76 elif mark1 <> None and sep == mark1:
77 self.last = 1
78 break
79 else:
80 return line
81 # Get here after break out of loop
82 self.lastpos = self.tell() - len(line)
83 self.level = i+1
84 if self.level > 1:
85 err('*** Missing endmarker in MultiFile.readline()\n')
86 return ''
87 #
Guido van Rossumb6775db1994-08-01 11:34:53 +000088 def readlines(self):
89 list = []
90 while 1:
91 line = self.readline()
92 if not line: break
93 list.append(line)
94 return list
95 #
96 def read(self): # Note: no size argument -- read until EOF only!
97 return string.joinfields(self.readlines(), '')
98 #
Guido van Rossum741c81a1992-07-13 14:40:45 +000099 def next(self):
100 while self.readline(): pass
101 if self.level > 1 or self.last:
102 return 0
103 self.level = 0
104 self.last = 0
105 self.start = self.fp.tell()
106 return 1
107 #
108 def push(self, sep):
109 if self.level > 0:
110 raise Error, 'bad MultiFile.push() call'
111 self.stack.insert(0, sep)
112 self.posstack.insert(0, self.start)
113 self.start = self.fp.tell()
114 #
115 def pop(self):
116 if self.stack == []:
117 raise Error, 'bad MultiFile.pop() call'
118 if self.level <= 1:
119 self.last = 0
120 else:
121 abslastpos = self.lastpos + self.start
122 self.level = max(0, self.level - 1)
123 del self.stack[0]
124 self.start = self.posstack[0]
125 del self.posstack[0]
126 if self.level > 0:
127 self.lastpos = abslastpos - self.start
128 #