rumbster 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,22 @@
1
+ #
2
+ # scanner.rb
3
+ #
4
+ # Copyright (c) 1998-2004 Minero Aoki
5
+ #
6
+ # This program is free software.
7
+ # You can distribute/modify this program under the terms of
8
+ # the GNU Lesser General Public License version 2.1.
9
+ #
10
+
11
+ require 'tmail/textutils'
12
+
13
+ module TMail
14
+ require 'tmail/scanner_r.rb'
15
+ begin
16
+ raise LoadError, 'Turn off Ruby extention by user choice' if ENV['NORUBYEXT']
17
+ require 'tmail/scanner_c.so'
18
+ Scanner = Scanner_C
19
+ rescue LoadError
20
+ Scanner = Scanner_R
21
+ end
22
+ end
@@ -0,0 +1,243 @@
1
+ #
2
+ # scanner_r.rb
3
+ #
4
+ # Copyright (c) 1998-2004 Minero Aoki
5
+ #
6
+ # This program is free software.
7
+ # You can distribute/modify this program under the terms of
8
+ # the GNU Lesser General Public License version 2.1.
9
+ #
10
+
11
+ require 'tmail/config'
12
+
13
+ module TMail
14
+
15
+ class Scanner_R
16
+
17
+ Version = '0.10.8'
18
+ Version.freeze
19
+
20
+ MIME_HEADERS = {
21
+ :CTYPE => true,
22
+ :CENCODING => true,
23
+ :CDISPOSITION => true
24
+ }
25
+
26
+ alnum = 'a-zA-Z0-9'
27
+ atomsyms = %q[ _#!$%&`'*+-{|}~^/=? ].strip
28
+ tokensyms = %q[ _#!$%&`'*+-{|}~^. ].strip
29
+
30
+ atomchars = alnum + Regexp.quote(atomsyms)
31
+ tokenchars = alnum + Regexp.quote(tokensyms)
32
+ iso2022str = '\e(?!\(B)..(?:[^\e]+|\e(?!\(B)..)*\e\(B'
33
+
34
+ eucstr = '(?:[\xa1-\xfe][\xa1-\xfe])+'
35
+ sjisstr = '(?:[\x81-\x9f\xe0-\xef][\x40-\x7e\x80-\xfc])+'
36
+ utf8str = '(?:[\xc0-\xdf][\x80-\xbf]|[\xe0-\xef][\x80-\xbf][\x80-\xbf])+'
37
+
38
+ quoted_with_iso2022 = /\A(?:[^\\\e"]+|#{iso2022str})+/n
39
+ domlit_with_iso2022 = /\A(?:[^\\\e\]]+|#{iso2022str})+/n
40
+ comment_with_iso2022 = /\A(?:[^\\\e()]+|#{iso2022str})+/n
41
+
42
+ quoted_without_iso2022 = /\A[^\\"]+/n
43
+ domlit_without_iso2022 = /\A[^\\\]]+/n
44
+ comment_without_iso2022 = /\A[^\\()]+/n
45
+
46
+ PATTERN_TABLE = {}
47
+ PATTERN_TABLE['EUC'] =
48
+ [
49
+ /\A(?:[#{atomchars}]+|#{iso2022str}|#{eucstr})+/n,
50
+ /\A(?:[#{tokenchars}]+|#{iso2022str}|#{eucstr})+/n,
51
+ quoted_with_iso2022,
52
+ domlit_with_iso2022,
53
+ comment_with_iso2022
54
+ ]
55
+ PATTERN_TABLE['SJIS'] =
56
+ [
57
+ /\A(?:[#{atomchars}]+|#{iso2022str}|#{sjisstr})+/n,
58
+ /\A(?:[#{tokenchars}]+|#{iso2022str}|#{sjisstr})+/n,
59
+ quoted_with_iso2022,
60
+ domlit_with_iso2022,
61
+ comment_with_iso2022
62
+ ]
63
+ PATTERN_TABLE['UTF8'] =
64
+ [
65
+ /\A(?:[#{atomchars}]+|#{utf8str})+/n,
66
+ /\A(?:[#{tokenchars}]+|#{utf8str})+/n,
67
+ quoted_without_iso2022,
68
+ domlit_without_iso2022,
69
+ comment_without_iso2022
70
+ ]
71
+ PATTERN_TABLE['NONE'] =
72
+ [
73
+ /\A[#{atomchars}]+/n,
74
+ /\A[#{tokenchars}]+/n,
75
+ quoted_without_iso2022,
76
+ domlit_without_iso2022,
77
+ comment_without_iso2022
78
+ ]
79
+
80
+
81
+ def initialize(str, scantype, comments)
82
+ init_scanner str
83
+ @comments = comments || []
84
+ @debug = false
85
+
86
+ # fix scanner mode
87
+ @received = (scantype == :RECEIVED)
88
+ @is_mime_header = MIME_HEADERS[scantype]
89
+
90
+ atom, token, @quoted_re, @domlit_re, @comment_re = PATTERN_TABLE[$KCODE]
91
+ @word_re = (MIME_HEADERS[scantype] ? token : atom)
92
+ end
93
+
94
+ attr_accessor :debug
95
+
96
+ def scan(&block)
97
+ if @debug
98
+ scan_main do |arr|
99
+ s, v = arr
100
+ printf "%7d %-10s %s\n",
101
+ rest_size(),
102
+ s.respond_to?(:id2name) ? s.id2name : s.inspect,
103
+ v.inspect
104
+ yield arr
105
+ end
106
+ else
107
+ scan_main(&block)
108
+ end
109
+ end
110
+
111
+ private
112
+
113
+ RECV_TOKEN = {
114
+ 'from' => :FROM,
115
+ 'by' => :BY,
116
+ 'via' => :VIA,
117
+ 'with' => :WITH,
118
+ 'id' => :ID,
119
+ 'for' => :FOR
120
+ }
121
+
122
+ def scan_main
123
+ until eof?
124
+ if skip(/\A[\n\r\t ]+/n) # LWSP
125
+ break if eof?
126
+ end
127
+
128
+ if s = readstr(@word_re)
129
+ if @is_mime_header
130
+ yield :TOKEN, s
131
+ else
132
+ # atom
133
+ if /\A\d+\z/ =~ s
134
+ yield :DIGIT, s
135
+ elsif @received
136
+ yield RECV_TOKEN[s.downcase] || :ATOM, s
137
+ else
138
+ yield :ATOM, s
139
+ end
140
+ end
141
+
142
+ elsif skip(/\A"/)
143
+ yield :QUOTED, scan_quoted_word()
144
+
145
+ elsif skip(/\A\[/)
146
+ yield :DOMLIT, scan_domain_literal()
147
+
148
+ elsif skip(/\A\(/)
149
+ @comments.push scan_comment()
150
+
151
+ else
152
+ c = readchar()
153
+ yield c, c
154
+ end
155
+ end
156
+
157
+ yield false, '$'
158
+ end
159
+
160
+ def scan_quoted_word
161
+ scan_qstr(@quoted_re, /\A"/, 'quoted-word')
162
+ end
163
+
164
+ def scan_domain_literal
165
+ '[' + scan_qstr(@domlit_re, /\A\]/, 'domain-literal') + ']'
166
+ end
167
+
168
+ def scan_qstr(pattern, terminal, type)
169
+ result = ''
170
+ until eof?
171
+ if s = readstr(pattern) then result << s
172
+ elsif skip(terminal) then return result
173
+ elsif skip(/\A\\/) then result << readchar()
174
+ else
175
+ raise "TMail FATAL: not match in #{type}"
176
+ end
177
+ end
178
+ scan_error! "found unterminated #{type}"
179
+ end
180
+
181
+ def scan_comment
182
+ result = ''
183
+ nest = 1
184
+ content = @comment_re
185
+
186
+ until eof?
187
+ if s = readstr(content) then result << s
188
+ elsif skip(/\A\)/) then nest -= 1
189
+ return result if nest == 0
190
+ result << ')'
191
+ elsif skip(/\A\(/) then nest += 1
192
+ result << '('
193
+ elsif skip(/\A\\/) then result << readchar()
194
+ else
195
+ raise 'TMail FATAL: not match in comment'
196
+ end
197
+ end
198
+ scan_error! 'found unterminated comment'
199
+ end
200
+
201
+ # string scanner
202
+
203
+ def init_scanner(str)
204
+ @src = str
205
+ end
206
+
207
+ def eof?
208
+ @src.empty?
209
+ end
210
+
211
+ def rest_size
212
+ @src.size
213
+ end
214
+
215
+ def readstr(re)
216
+ if m = re.match(@src)
217
+ @src = m.post_match
218
+ m[0]
219
+ else
220
+ nil
221
+ end
222
+ end
223
+
224
+ def readchar
225
+ readstr(/\A./)
226
+ end
227
+
228
+ def skip(re)
229
+ if m = re.match(@src)
230
+ @src = m.post_match
231
+ true
232
+ else
233
+ false
234
+ end
235
+ end
236
+
237
+ def scan_error!(msg)
238
+ raise SyntaxError, msg
239
+ end
240
+
241
+ end
242
+
243
+ end # module TMail
@@ -0,0 +1,256 @@
1
+ #
2
+ # stringio.rb
3
+ #
4
+ # Copyright (c) 1999-2004 Minero Aoki
5
+ #
6
+ # This program is free software.
7
+ # You can distribute/modify this program under the terms of
8
+ # the GNU Lesser General Public License version 2.1.
9
+ #
10
+ # $amstdId: stringio.rb,v 1.12 2004/02/20 00:31:14 aamine Exp $
11
+ #
12
+
13
+ class StringInput
14
+
15
+ include Enumerable
16
+
17
+ class << self
18
+ def new(str)
19
+ if block_given?
20
+ begin
21
+ f = super
22
+ yield f
23
+ ensure
24
+ f.close if f
25
+ end
26
+ else
27
+ super
28
+ end
29
+ end
30
+
31
+ alias open new
32
+ end
33
+
34
+ def initialize(str)
35
+ @src = str
36
+ @pos = 0
37
+ @closed = false
38
+ @lineno = 0
39
+ end
40
+
41
+ attr_reader :lineno
42
+
43
+ def string
44
+ @src
45
+ end
46
+
47
+ def inspect
48
+ "#<#{self.class}:#{@closed ? 'closed' : 'open'},src=#{@src[0,30].inspect}>"
49
+ end
50
+
51
+ def close
52
+ stream_check!
53
+ @pos = nil
54
+ @closed = true
55
+ end
56
+
57
+ def closed?
58
+ @closed
59
+ end
60
+
61
+ def pos
62
+ stream_check!
63
+ [@pos, @src.size].min
64
+ end
65
+
66
+ alias tell pos
67
+
68
+ def seek(offset, whence = IO::SEEK_SET)
69
+ stream_check!
70
+ case whence
71
+ when IO::SEEK_SET
72
+ @pos = offset
73
+ when IO::SEEK_CUR
74
+ @pos += offset
75
+ when IO::SEEK_END
76
+ @pos = @src.size - offset
77
+ else
78
+ raise ArgumentError, "unknown seek flag: #{whence}"
79
+ end
80
+ @pos = 0 if @pos < 0
81
+ @pos = [@pos, @src.size + 1].min
82
+ offset
83
+ end
84
+
85
+ def rewind
86
+ stream_check!
87
+ @pos = 0
88
+ end
89
+
90
+ def eof?
91
+ stream_check!
92
+ @pos > @src.size
93
+ end
94
+
95
+ def each(&block)
96
+ stream_check!
97
+ begin
98
+ @src.each(&block)
99
+ ensure
100
+ @pos = 0
101
+ end
102
+ end
103
+
104
+ def gets
105
+ stream_check!
106
+ if idx = @src.index(?\n, @pos)
107
+ idx += 1 # "\n".size
108
+ line = @src[ @pos ... idx ]
109
+ @pos = idx
110
+ @pos += 1 if @pos == @src.size
111
+ else
112
+ line = @src[ @pos .. -1 ]
113
+ @pos = @src.size + 1
114
+ end
115
+ @lineno += 1
116
+
117
+ line
118
+ end
119
+
120
+ def getc
121
+ stream_check!
122
+ ch = @src[@pos]
123
+ @pos += 1
124
+ @pos += 1 if @pos == @src.size
125
+ ch
126
+ end
127
+
128
+ def read(len = nil)
129
+ stream_check!
130
+ return read_all() unless len
131
+ str = @src[@pos, len]
132
+ @pos += len
133
+ @pos += 1 if @pos == @src.size
134
+ str
135
+ end
136
+
137
+ alias sysread read
138
+
139
+ def read_all
140
+ stream_check!
141
+ return nil if eof?
142
+ rest = @src[@pos ... @src.size]
143
+ @pos = @src.size + 1
144
+ rest
145
+ end
146
+
147
+ def stream_check!
148
+ @closed and raise IOError, 'closed stream'
149
+ end
150
+
151
+ end
152
+
153
+
154
+ class StringOutput
155
+
156
+ class << self
157
+ def new(str = '')
158
+ if block_given?
159
+ begin
160
+ f = super
161
+ yield f
162
+ ensure
163
+ f.close if f
164
+ end
165
+ else
166
+ super
167
+ end
168
+ end
169
+
170
+ alias open new
171
+ end
172
+
173
+ def initialize(str = '')
174
+ @dest = str
175
+ @closed = false
176
+ end
177
+
178
+ def close
179
+ @closed = true
180
+ end
181
+
182
+ def closed?
183
+ @closed
184
+ end
185
+
186
+ def string
187
+ @dest
188
+ end
189
+
190
+ alias value string
191
+ alias to_str string
192
+
193
+ def size
194
+ @dest.size
195
+ end
196
+
197
+ alias pos size
198
+
199
+ def inspect
200
+ "#<#{self.class}:#{@dest ? 'open' : 'closed'},#{id}>"
201
+ end
202
+
203
+ def print(*args)
204
+ stream_check!
205
+ raise ArgumentError, 'wrong # of argument (0 for >1)' if args.empty?
206
+ args.each do |s|
207
+ raise ArgumentError, 'nil not allowed' if s.nil?
208
+ @dest << s.to_s
209
+ end
210
+ nil
211
+ end
212
+
213
+ def puts(*args)
214
+ stream_check!
215
+ args.each do |str|
216
+ @dest << (s = str.to_s)
217
+ @dest << "\n" unless s[-1] == ?\n
218
+ end
219
+ @dest << "\n" if args.empty?
220
+ nil
221
+ end
222
+
223
+ def putc(ch)
224
+ stream_check!
225
+ @dest << ch.chr
226
+ nil
227
+ end
228
+
229
+ def printf(*args)
230
+ stream_check!
231
+ @dest << sprintf(*args)
232
+ nil
233
+ end
234
+
235
+ def write(str)
236
+ stream_check!
237
+ s = str.to_s
238
+ @dest << s
239
+ s.size
240
+ end
241
+
242
+ alias syswrite write
243
+
244
+ def <<(str)
245
+ stream_check!
246
+ @dest << str.to_s
247
+ self
248
+ end
249
+
250
+ private
251
+
252
+ def stream_check!
253
+ @closed and raise IOError, 'closed stream'
254
+ end
255
+
256
+ end