Upgraded Rails and RSpec
[monkeycharger.git] / vendor / rails / actionmailer / lib / action_mailer / vendor / tmail-1.1.0 / tmail / scanner_r.rb
blobccf576c295110002d933c36d23665e8994207c53
2 # scanner_r.rb
4 #--
5 # Copyright (c) 1998-2003 Minero Aoki <aamine@loveruby.net>
7 # Permission is hereby granted, free of charge, to any person obtaining
8 # a copy of this software and associated documentation files (the
9 # "Software"), to deal in the Software without restriction, including
10 # without limitation the rights to use, copy, modify, merge, publish,
11 # distribute, sublicense, and/or sell copies of the Software, and to
12 # permit persons to whom the Software is furnished to do so, subject to
13 # the following conditions:
15 # The above copyright notice and this permission notice shall be
16 # included in all copies or substantial portions of the Software.
18 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19 # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26 # Note: Originally licensed under LGPL v2+. Using MIT license for Rails
27 # with permission of Minero Aoki.
28 #++
30 require 'tmail/config'
33 module TMail
35   class Scanner_R
37     Version = '0.10.7'
38     Version.freeze
40     MIME_HEADERS = {
41       :CTYPE        => true,
42       :CENCODING    => true,
43       :CDISPOSITION => true
44     }
46     alnum      = 'a-zA-Z0-9'
47     atomsyms   = %q[  _#!$%&`'*+-{|}~^@/=?  ].strip
48     tokensyms  = %q[  _#!$%&`'*+-{|}~^@.    ].strip
50     atomchars  = alnum + Regexp.quote(atomsyms)
51     tokenchars = alnum + Regexp.quote(tokensyms)
52     iso2022str = '\e(?!\(B)..(?:[^\e]+|\e(?!\(B)..)*\e\(B'
54     eucstr  = '(?:[\xa1-\xfe][\xa1-\xfe])+'
55     sjisstr = '(?:[\x81-\x9f\xe0-\xef][\x40-\x7e\x80-\xfc])+'
56     utf8str = '(?:[\xc0-\xdf][\x80-\xbf]|[\xe0-\xef][\x80-\xbf][\x80-\xbf])+'
58     quoted_with_iso2022  = /\A(?:[^\\\e"]+|#{iso2022str})+/n
59     domlit_with_iso2022  = /\A(?:[^\\\e\]]+|#{iso2022str})+/n
60     comment_with_iso2022 = /\A(?:[^\\\e()]+|#{iso2022str})+/n
62     quoted_without_iso2022  = /\A[^\\"]+/n
63     domlit_without_iso2022  = /\A[^\\\]]+/n
64     comment_without_iso2022 = /\A[^\\()]+/n
66     PATTERN_TABLE = {}
67     PATTERN_TABLE['EUC'] =
68       [
69         /\A(?:[#{atomchars}]+|#{iso2022str}|#{eucstr})+/n,
70         /\A(?:[#{tokenchars}]+|#{iso2022str}|#{eucstr})+/n,
71         quoted_with_iso2022,
72         domlit_with_iso2022,
73         comment_with_iso2022
74       ]
75     PATTERN_TABLE['SJIS'] =
76       [
77         /\A(?:[#{atomchars}]+|#{iso2022str}|#{sjisstr})+/n,
78         /\A(?:[#{tokenchars}]+|#{iso2022str}|#{sjisstr})+/n,
79         quoted_with_iso2022,
80         domlit_with_iso2022,
81         comment_with_iso2022
82       ]
83     PATTERN_TABLE['UTF8'] =
84       [
85         /\A(?:[#{atomchars}]+|#{utf8str})+/n,
86         /\A(?:[#{tokenchars}]+|#{utf8str})+/n,
87         quoted_without_iso2022,
88         domlit_without_iso2022,
89         comment_without_iso2022
90       ]
91     PATTERN_TABLE['NONE'] =
92       [
93         /\A[#{atomchars}]+/n,
94         /\A[#{tokenchars}]+/n,
95         quoted_without_iso2022,
96         domlit_without_iso2022,
97         comment_without_iso2022
98       ]
101     def initialize( str, scantype, comments )
102       init_scanner str
103       @comments = comments || []
104       @debug    = false
106       # fix scanner mode
107       @received  = (scantype == :RECEIVED)
108       @is_mime_header = MIME_HEADERS[scantype]
110       atom, token, @quoted_re, @domlit_re, @comment_re = PATTERN_TABLE[$KCODE]
111       @word_re = (MIME_HEADERS[scantype] ? token : atom)
112     end
114     attr_accessor :debug
116     def scan( &block )
117       if @debug
118         scan_main do |arr|
119           s, v = arr
120           printf "%7d %-10s %s\n",
121                  rest_size(),
122                  s.respond_to?(:id2name) ? s.id2name : s.inspect,
123                  v.inspect
124           yield arr
125         end
126       else
127         scan_main(&block)
128       end
129     end
131     private
133     RECV_TOKEN = {
134       'from' => :FROM,
135       'by'   => :BY,
136       'via'  => :VIA,
137       'with' => :WITH,
138       'id'   => :ID,
139       'for'  => :FOR
140     }
142     def scan_main
143       until eof?
144         if skip(/\A[\n\r\t ]+/n)   # LWSP
145           break if eof?
146         end
148         if s = readstr(@word_re)
149           if @is_mime_header
150             yield :TOKEN, s
151           else
152             # atom
153             if /\A\d+\z/ === s
154               yield :DIGIT, s
155             elsif @received
156               yield RECV_TOKEN[s.downcase] || :ATOM, s
157             else
158               yield :ATOM, s
159             end
160           end
162         elsif skip(/\A"/)
163           yield :QUOTED, scan_quoted_word()
165         elsif skip(/\A\[/)
166           yield :DOMLIT, scan_domain_literal()
168         elsif skip(/\A\(/)
169           @comments.push scan_comment()
171         else
172           c = readchar()
173           yield c, c
174         end
175       end
177       yield false, '$'
178     end
180     def scan_quoted_word
181       scan_qstr(@quoted_re, /\A"/, 'quoted-word')
182     end
184     def scan_domain_literal
185       '[' + scan_qstr(@domlit_re, /\A\]/, 'domain-literal') + ']'
186     end
188     def scan_qstr( pattern, terminal, type )
189       result = ''
190       until eof?
191         if    s = readstr(pattern) then result << s
192         elsif skip(terminal)       then return result
193         elsif skip(/\A\\/)         then result << readchar()
194         else
195           raise "TMail FATAL: not match in #{type}"
196         end
197       end
198       scan_error! "found unterminated #{type}"
199     end
201     def scan_comment
202       result = ''
203       nest = 1
204       content = @comment_re
206       until eof?
207         if s = readstr(content) then result << s
208         elsif skip(/\A\)/)      then nest -= 1
209                                      return result if nest == 0
210                                      result << ')'
211         elsif skip(/\A\(/)      then nest += 1
212                                      result << '('
213         elsif skip(/\A\\/)      then result << readchar()
214         else
215           raise 'TMail FATAL: not match in comment'
216         end
217       end
218       scan_error! 'found unterminated comment'
219     end
221     # string scanner
223     def init_scanner( str )
224       @src = str
225     end
227     def eof?
228       @src.empty?
229     end
231     def rest_size
232       @src.size
233     end
235     def readstr( re )
236       if m = re.match(@src)
237         @src = m.post_match
238         m[0]
239       else
240         nil
241       end
242     end
244     def readchar
245       readstr(/\A./)
246     end
248     def skip( re )
249       if m = re.match(@src)
250         @src = m.post_match
251         true
252       else
253         false
254       end
255     end
257     def scan_error!( msg )
258       raise SyntaxError, msg
259     end
261   end
263 end   # module TMail