blob: 8ba88e46328c056f75c0c034a5a41a277e4bb234 [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 #
Guido van Rossum69c70a21998-03-25 16:25:26 +000047 def seek(self, pos, whence=0):
Guido van Rossum8ca84201998-03-26 20:56:10 +000048 here = self.tell()
49 if whence:
50 if whence == 1:
51 pos = pos + here
52 elif whence == 2:
53 if self.level > 0:
54 pos = pos + self.lastpos
55 else:
56 raise Error, "can't use whence=2 yet"
Guido van Rossum69c70a21998-03-25 16:25:26 +000057 if not 0 <= pos <= here or \
Guido van Rossum741c81a1992-07-13 14:40:45 +000058 self.level > 0 and pos > self.lastpos:
59 raise Error, 'bad MultiFile.seek() call'
60 self.fp.seek(pos + self.start)
61 self.level = 0
62 self.last = 0
63 #
64 def readline(self):
65 if self.level > 0: return ''
66 line = self.fp.readline()
67 if not line:
68 self.level = len(self.stack)
69 self.last = (self.level > 0)
70 if self.last:
71 err('*** Sudden EOF in MultiFile.readline()\n')
72 return ''
73 if line[:2] <> '--': return line
74 n = len(line)
75 k = n
76 while k > 0 and line[k-1] in string.whitespace: k = k-1
77 mark = line[2:k]
78 if mark[-2:] == '--': mark1 = mark[:-2]
79 else: mark1 = None
80 for i in range(len(self.stack)):
81 sep = self.stack[i]
82 if sep == mark:
83 self.last = 0
84 break
85 elif mark1 <> None and sep == mark1:
86 self.last = 1
87 break
88 else:
89 return line
90 # Get here after break out of loop
91 self.lastpos = self.tell() - len(line)
92 self.level = i+1
93 if self.level > 1:
94 err('*** Missing endmarker in MultiFile.readline()\n')
95 return ''
96 #
Guido van Rossumb6775db1994-08-01 11:34:53 +000097 def readlines(self):
98 list = []
99 while 1:
100 line = self.readline()
101 if not line: break
102 list.append(line)
103 return list
104 #
105 def read(self): # Note: no size argument -- read until EOF only!
106 return string.joinfields(self.readlines(), '')
107 #
Guido van Rossum741c81a1992-07-13 14:40:45 +0000108 def next(self):
109 while self.readline(): pass
110 if self.level > 1 or self.last:
111 return 0
112 self.level = 0
113 self.last = 0
114 self.start = self.fp.tell()
115 return 1
116 #
117 def push(self, sep):
118 if self.level > 0:
119 raise Error, 'bad MultiFile.push() call'
120 self.stack.insert(0, sep)
121 self.posstack.insert(0, self.start)
122 self.start = self.fp.tell()
123 #
124 def pop(self):
125 if self.stack == []:
126 raise Error, 'bad MultiFile.pop() call'
127 if self.level <= 1:
128 self.last = 0
129 else:
130 abslastpos = self.lastpos + self.start
131 self.level = max(0, self.level - 1)
132 del self.stack[0]
133 self.start = self.posstack[0]
134 del self.posstack[0]
135 if self.level > 0:
136 self.lastpos = abslastpos - self.start
137 #