hermeneutics 1.11 → 1.14
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README +11 -0
- data/bin/hermesmail +93 -58
- data/lib/hermeneutics/addrs.rb +6 -4
- data/lib/hermeneutics/boxes.rb +134 -119
- data/lib/hermeneutics/cgi.rb +16 -6
- data/lib/hermeneutics/cli/imap/commands.rb +283 -0
- data/lib/hermeneutics/cli/imap/parser.rb +245 -0
- data/lib/hermeneutics/cli/imap/utf7imap.rb +75 -0
- data/lib/hermeneutics/cli/imap.rb +240 -0
- data/lib/hermeneutics/cli/openssl.rb +11 -0
- data/lib/hermeneutics/cli/pop3.rb +257 -0
- data/lib/hermeneutics/cli/protocol.rb +141 -0
- data/lib/hermeneutics/cli/smtp.rb +218 -0
- data/lib/hermeneutics/color.rb +8 -8
- data/lib/hermeneutics/contents.rb +6 -0
- data/lib/hermeneutics/css.rb +10 -10
- data/lib/hermeneutics/escape.rb +30 -34
- data/lib/hermeneutics/html.rb +4 -4
- data/lib/hermeneutics/mail.rb +248 -63
- data/lib/hermeneutics/message.rb +238 -254
- data/lib/hermeneutics/types.rb +8 -7
- data/lib/hermeneutics/version.rb +3 -4
- metadata +26 -5
- data/lib/hermeneutics/cli/pop.rb +0 -102
- data/lib/hermeneutics/transports.rb +0 -230
@@ -0,0 +1,218 @@
|
|
1
|
+
#
|
2
|
+
# hermeneutics/cli/smtp.rb -- SMTP client
|
3
|
+
#
|
4
|
+
|
5
|
+
require "hermeneutics/cli/protocol"
|
6
|
+
|
7
|
+
module Hermeneutics
|
8
|
+
|
9
|
+
module Cli
|
10
|
+
|
11
|
+
class SMTP < Protocol
|
12
|
+
|
13
|
+
CRLF = true
|
14
|
+
|
15
|
+
PORT, PORT_SSL = 25, 465
|
16
|
+
|
17
|
+
class Error < StandardError ; end
|
18
|
+
class UnspecError < Error ; end
|
19
|
+
class ServerNotReady < Error ; end
|
20
|
+
class NotOk < Error ; end
|
21
|
+
class NotReadyForData < Error ; end
|
22
|
+
class Unused < Error ; end
|
23
|
+
class Uncaught < Error ; end
|
24
|
+
|
25
|
+
class <<self
|
26
|
+
private :new
|
27
|
+
def open host, port = nil, timeout: nil, ssl: false
|
28
|
+
port ||= ssl ? PORT_SSL : PORT
|
29
|
+
super host, port, timeout: timeout, ssl: ssl
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
attr_reader :domain, :greet
|
34
|
+
attr_reader :advertised
|
35
|
+
attr_reader :last_response
|
36
|
+
|
37
|
+
def initialize *args
|
38
|
+
super
|
39
|
+
get_response.ok? or raise ServerNotReady, @last_response.msg
|
40
|
+
end
|
41
|
+
|
42
|
+
def size
|
43
|
+
@advertised && @advertised[ :SIZE]
|
44
|
+
end
|
45
|
+
|
46
|
+
def auth
|
47
|
+
@advertised && @advertised[ :AUTH]
|
48
|
+
end
|
49
|
+
|
50
|
+
def has_auth? meth
|
51
|
+
a = auth
|
52
|
+
a and a.include? meth
|
53
|
+
end
|
54
|
+
|
55
|
+
|
56
|
+
def helo host = nil
|
57
|
+
cmd_hello "HELO", host
|
58
|
+
end
|
59
|
+
|
60
|
+
def ehlo host = nil
|
61
|
+
@advertised = {}
|
62
|
+
cmd_hello "EHLO", host do |code,msg|
|
63
|
+
unless @domain then
|
64
|
+
@domain, @greet = msg.split nil, 2
|
65
|
+
next
|
66
|
+
end
|
67
|
+
keyword, param = msg.split nil, 2
|
68
|
+
keyword.upcase!
|
69
|
+
keyword = keyword.to_sym
|
70
|
+
case keyword
|
71
|
+
when :SIZE then param = Integer param
|
72
|
+
when :AUTH then param = param.split.map { |p| p.upcase! ; p.to_sym }
|
73
|
+
end
|
74
|
+
@advertised[ keyword] = param || true
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def mail_from from
|
79
|
+
cmd "MAIL", "FROM:<#{from}>"
|
80
|
+
end
|
81
|
+
|
82
|
+
def rcpt_to to
|
83
|
+
cmd "RCPT", "TO:<#{to}>"
|
84
|
+
end
|
85
|
+
|
86
|
+
def data reader
|
87
|
+
write_cmd "DATA"
|
88
|
+
get_response.waiting? or raise NotReadyForData, @last_response.msg
|
89
|
+
reader.each_line { |l|
|
90
|
+
l =~ /\A\./ and l = ".#{l}"
|
91
|
+
writeline l
|
92
|
+
}
|
93
|
+
writeline "."
|
94
|
+
get_response_ok
|
95
|
+
end
|
96
|
+
|
97
|
+
def bdat data
|
98
|
+
data.each { |d|
|
99
|
+
write_cmd "BDAT", d.bytesize
|
100
|
+
write d
|
101
|
+
get_response_ok
|
102
|
+
}
|
103
|
+
write_cmd "BDAT", 0, "LAST"
|
104
|
+
get_response_ok do |code,msg|
|
105
|
+
yield msg if block_given?
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def rset
|
110
|
+
cmd "RSET"
|
111
|
+
end
|
112
|
+
|
113
|
+
def help str = nil, &block
|
114
|
+
cmd "HELP", str, &block
|
115
|
+
end
|
116
|
+
|
117
|
+
def noop str = nil
|
118
|
+
cmd "NOOP"
|
119
|
+
end
|
120
|
+
|
121
|
+
def quit
|
122
|
+
cmd "QUIT"
|
123
|
+
end
|
124
|
+
|
125
|
+
|
126
|
+
def plain user, password
|
127
|
+
write_cmd "AUTH", "PLAIN"
|
128
|
+
get_response.waiting? or raise NotReadyForData, @last_response.msg
|
129
|
+
l = ["\0#{user}\0#{password}"].pack "m0"
|
130
|
+
writeline l
|
131
|
+
get_response_ok
|
132
|
+
end
|
133
|
+
|
134
|
+
|
135
|
+
def login user, password
|
136
|
+
write_cmd "AUTH", "LOGIN"
|
137
|
+
get_response.waiting? or raise NotReadyForData, @last_response.msg
|
138
|
+
writeline [user].pack "m0"
|
139
|
+
get_response.waiting? or raise NotReadyForData, @last_response.msg
|
140
|
+
writeline [password].pack "m0"
|
141
|
+
get_response_ok
|
142
|
+
end
|
143
|
+
|
144
|
+
|
145
|
+
private
|
146
|
+
|
147
|
+
def cmd_hello name, host, &block
|
148
|
+
host ||= Socket.gethostname
|
149
|
+
write_cmd name, host
|
150
|
+
get_response_ok &block
|
151
|
+
unless @domain then
|
152
|
+
@domain, @greet = @last_response.msg.split nil, 2
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def cmd name, *args, &block
|
157
|
+
write_cmd name, *args
|
158
|
+
get_response_ok &block
|
159
|
+
end
|
160
|
+
|
161
|
+
def write_cmd name, *args
|
162
|
+
l = [ name, *args].join " "
|
163
|
+
writeline l
|
164
|
+
end
|
165
|
+
|
166
|
+
class Response
|
167
|
+
|
168
|
+
attr_reader :code, :msg
|
169
|
+
|
170
|
+
def initialize code, msg
|
171
|
+
@code, @msg = code, msg
|
172
|
+
end
|
173
|
+
|
174
|
+
def kat ; code / 100 ; end
|
175
|
+
|
176
|
+
def to_s ; "%03d %s" % [ @code, @msg] ; end
|
177
|
+
|
178
|
+
def prelim? ; kat == 1 ; end
|
179
|
+
def ok? ; kat == 2 ; end
|
180
|
+
def waiting? ; kat == 3 ; end
|
181
|
+
def error? ; kat == 4 ; end
|
182
|
+
def fatal? ; kat == 5 ; end
|
183
|
+
|
184
|
+
end
|
185
|
+
|
186
|
+
def get_response_ok &block
|
187
|
+
get_response &block
|
188
|
+
@last_response.ok? or raise NotOk, @last_response.msg
|
189
|
+
true
|
190
|
+
end
|
191
|
+
|
192
|
+
def get_response
|
193
|
+
loop do
|
194
|
+
r = readline
|
195
|
+
if r =~ /\A(\d\d\d) / then
|
196
|
+
@last_response = Response.new $1.to_i, $'
|
197
|
+
break
|
198
|
+
elsif r =~ /\A(\d\d\d)-/ then
|
199
|
+
block_given? or raise Uncaught, r
|
200
|
+
yield $1.to_i, $'
|
201
|
+
else
|
202
|
+
raise UnspecError, r
|
203
|
+
end
|
204
|
+
end
|
205
|
+
@last_response
|
206
|
+
ensure
|
207
|
+
unless done? then
|
208
|
+
r = readline
|
209
|
+
r and raise Unused, "Unexpected data: #{r.inspect}"
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
end
|
214
|
+
|
215
|
+
end
|
216
|
+
|
217
|
+
end
|
218
|
+
|
data/lib/hermeneutics/color.rb
CHANGED
@@ -8,7 +8,7 @@
|
|
8
8
|
|
9
9
|
Hermeneutics::Color handles 24-bit colors.
|
10
10
|
|
11
|
-
Hermeneutics::Colour is an alias for
|
11
|
+
Hermeneutics::Colour is an alias for +Hermeneutics::Color+.
|
12
12
|
|
13
13
|
=end
|
14
14
|
|
@@ -41,7 +41,7 @@ module Hermeneutics
|
|
41
41
|
# new( r, g, b) -> clr
|
42
42
|
#
|
43
43
|
# Create a color with red, green and blue values. They are in range
|
44
|
-
#
|
44
|
+
# +0..255+.
|
45
45
|
#
|
46
46
|
def initialize r, *gb
|
47
47
|
if gb.any? then
|
@@ -79,8 +79,8 @@ module Hermeneutics
|
|
79
79
|
# :call-seq:
|
80
80
|
# gray( num) -> clr
|
81
81
|
#
|
82
|
-
# Create a gray color (r=b=g).
|
83
|
-
#
|
82
|
+
# Create a gray color (r=b=g). +num+ is in range
|
83
|
+
# +0..255+.
|
84
84
|
#
|
85
85
|
def gray i
|
86
86
|
new i, i, i
|
@@ -96,7 +96,7 @@ module Hermeneutics
|
|
96
96
|
#
|
97
97
|
def to_s ; "#" + tuple.map { |x| "%02x" % x }.join ; end
|
98
98
|
|
99
|
-
def inspect ; "#<#{
|
99
|
+
def inspect ; "#<#{self.class}:#{'0x%08x' % (object_id << 1)} #{to_s}>" ; end
|
100
100
|
|
101
101
|
class <<self
|
102
102
|
|
@@ -224,8 +224,8 @@ module Hermeneutics
|
|
224
224
|
# :call-seq:
|
225
225
|
# edit_hsv() { |h,s,v| ... } -> clr
|
226
226
|
#
|
227
|
-
# Convert it to an HSV triple, yield that to the block and build a
|
228
|
-
#
|
227
|
+
# Convert it to an HSV triple, yield that to the block and build a new
|
228
|
+
# +Color+ from the blocks result.
|
229
229
|
#
|
230
230
|
def edit_hsv
|
231
231
|
hsv = yield *to_hsv
|
@@ -241,7 +241,7 @@ module Hermeneutics
|
|
241
241
|
|
242
242
|
end
|
243
243
|
|
244
|
-
# Alias for class
|
244
|
+
# Alias for class +Hermeneutics::Color+ in British English.
|
245
245
|
Colour = Color
|
246
246
|
|
247
247
|
end
|
@@ -115,6 +115,9 @@ module Hermeneutics
|
|
115
115
|
end
|
116
116
|
end
|
117
117
|
|
118
|
+
def empty? ; @hash.empty? ; end
|
119
|
+
def notempty? ; self if @hash.notempty? ; end
|
120
|
+
|
118
121
|
# :call-seq:
|
119
122
|
# []( key) -> str or nil
|
120
123
|
#
|
@@ -256,6 +259,9 @@ module Hermeneutics
|
|
256
259
|
super hash
|
257
260
|
end
|
258
261
|
|
262
|
+
def empty? ; @caption.empty? and super ; end
|
263
|
+
def notempty? ; self if @caption.notempty? or super ; end
|
264
|
+
|
259
265
|
def =~ re
|
260
266
|
@caption =~ re
|
261
267
|
end
|
data/lib/hermeneutics/css.rb
CHANGED
@@ -26,7 +26,7 @@ module Hermeneutics
|
|
26
26
|
# a ":visited", ATTR_COL2, ATTR_DECON
|
27
27
|
# a ":active", ATTR_COL1, ATTR_DECON
|
28
28
|
# a ":focus", ATTR_COL1, ATTR_DECOU
|
29
|
-
#
|
29
|
+
# skip
|
30
30
|
#
|
31
31
|
# body "#dummy" do
|
32
32
|
# properties background_color: "f7f7f7".to_rgb
|
@@ -145,22 +145,22 @@ module Hermeneutics
|
|
145
145
|
def comment str
|
146
146
|
@out << "/*"
|
147
147
|
str = mask_comment str
|
148
|
-
ml = str =~ %r(
|
148
|
+
ml = str =~ %r(\n)
|
149
149
|
if ml then
|
150
|
-
@out <<
|
150
|
+
@out << "\n"
|
151
151
|
str.each_line { |l|
|
152
152
|
l.chomp!
|
153
|
-
@out << " * " << l <<
|
153
|
+
@out << " * " << l << "\n"
|
154
154
|
}
|
155
155
|
else
|
156
156
|
@out << " " << str
|
157
157
|
end
|
158
158
|
@out << " */"
|
159
|
-
ml and @out <<
|
159
|
+
ml and @out << "\n"
|
160
160
|
end
|
161
161
|
|
162
|
-
def
|
163
|
-
@out <<
|
162
|
+
def skip
|
163
|
+
@out << "\n"
|
164
164
|
end
|
165
165
|
|
166
166
|
def tag *args
|
@@ -232,15 +232,15 @@ module Hermeneutics
|
|
232
232
|
args.each { |a| p.update a }
|
233
233
|
@out << sel << " {"
|
234
234
|
nl, ind = if p.size > 1 then
|
235
|
-
@out <<
|
236
|
-
[
|
235
|
+
@out << "\n"
|
236
|
+
[ "\n", INDENT]
|
237
237
|
else
|
238
238
|
[ " ", " "]
|
239
239
|
end
|
240
240
|
single p do |s|
|
241
241
|
@out << ind << s << nl
|
242
242
|
end
|
243
|
-
@out << "}" <<
|
243
|
+
@out << "}" << "\n"
|
244
244
|
end
|
245
245
|
|
246
246
|
def single hash
|
data/lib/hermeneutics/escape.rb
CHANGED
@@ -12,25 +12,25 @@ require "supplement"
|
|
12
12
|
:section: Classes definied here
|
13
13
|
|
14
14
|
Hermeneutics::Entities encodes to and decodes from HTML-Entities
|
15
|
-
(
|
15
|
+
(+&+ etc.)
|
16
16
|
|
17
17
|
Hermeneutics::URLText encodes to and decodes from URLs
|
18
|
-
(
|
18
|
+
(+%2d+ etc.)
|
19
19
|
|
20
20
|
Hermeneutics::HeaderExt encodes to and decodes from E-Mail Header fields
|
21
|
-
(
|
21
|
+
(+=?UTF-8?Q?=C3=B6?=+ etc.).
|
22
22
|
|
23
23
|
=end
|
24
24
|
|
25
25
|
module Hermeneutics
|
26
26
|
|
27
|
-
# Translate HTML and XML character entities:
|
28
|
-
#
|
27
|
+
# Translate HTML and XML character entities: +"&"+ to
|
28
|
+
# +"&"+ and vice versa.
|
29
29
|
#
|
30
30
|
# == What actually happens
|
31
31
|
#
|
32
|
-
# HTML pages usually come in with characters encoded
|
33
|
-
#
|
32
|
+
# HTML pages usually come in with characters encoded +<+ for +<+ and
|
33
|
+
# +€+ for +€+.
|
34
34
|
#
|
35
35
|
# Further, they may contain a meta tag in the header like this:
|
36
36
|
#
|
@@ -41,27 +41,25 @@ module Hermeneutics
|
|
41
41
|
#
|
42
42
|
# <?xml version="1.0" encoding="UTF-8" ?> (XHTML)
|
43
43
|
#
|
44
|
-
# When +charset+ is
|
45
|
-
#
|
46
|
-
# be displayed a character <code>"ä"</code>.
|
44
|
+
# When +charset+ is +utf-8+ and the file contains the byte sequence
|
45
|
+
# +"\303\244"+/+"\xc3\xa4"+ then there will be displayed a character +"ä"+.
|
47
46
|
#
|
48
|
-
# When +charset+ is
|
49
|
-
#
|
50
|
-
# displayed a character <code>"ä"</code>, too.
|
47
|
+
# When +charset+ is +iso8859-15+ and the file contains the byte sequence
|
48
|
+
# +"\344"+/+"\xe4"+ then there will be displayed a character +"ä"+, too.
|
51
49
|
#
|
52
|
-
# The sequence
|
50
|
+
# The sequence +"ä"+ will produce an +"ä"+ in any
|
53
51
|
# case.
|
54
52
|
#
|
55
53
|
# == What you should do
|
56
54
|
#
|
57
55
|
# Generating your own HTML pages you will always be safe when you only
|
58
|
-
# produce entity tags as
|
59
|
-
#
|
56
|
+
# produce entity tags as +ä+ and +€+ or +ä+ and +€+
|
57
|
+
# respectively.
|
60
58
|
#
|
61
59
|
# == What this module does
|
62
60
|
#
|
63
|
-
# This module translates strings to a HTML-masked version. The encoding
|
64
|
-
# not be changed and you may demand to keep 8-bit-characters.
|
61
|
+
# This module translates strings to a HTML-masked version. The encoding
|
62
|
+
# will not be changed and you may demand to keep 8-bit-characters.
|
65
63
|
#
|
66
64
|
# == Examples
|
67
65
|
#
|
@@ -118,7 +116,7 @@ module Hermeneutics
|
|
118
116
|
# :call-seq:
|
119
117
|
# new( keep_8bit: bool) -> ent
|
120
118
|
#
|
121
|
-
# Creates an
|
119
|
+
# Creates an +Entities+ converter.
|
122
120
|
#
|
123
121
|
# ent = Entities.new keep_8bit: true
|
124
122
|
#
|
@@ -184,9 +182,8 @@ module Hermeneutics
|
|
184
182
|
# Entities.decode "<" #=> "<"
|
185
183
|
# Entities.decode "äöü" #=> "äöü"
|
186
184
|
#
|
187
|
-
# Unmasked 8-bit-characters (
|
188
|
-
#
|
189
|
-
# a unique encoding.
|
185
|
+
# Unmasked 8-bit-characters (+"ä"+ instead of +"ä"+) will be kept
|
186
|
+
# but translated to a unique encoding.
|
190
187
|
#
|
191
188
|
# s = "ä ö ü"
|
192
189
|
# s.encode! "utf-8"
|
@@ -259,7 +256,7 @@ module Hermeneutics
|
|
259
256
|
# :call-seq:
|
260
257
|
# new( hash) -> urltext
|
261
258
|
#
|
262
|
-
# Creates a
|
259
|
+
# Creates a +URLText+ converter.
|
263
260
|
#
|
264
261
|
# The parameters may be given as values or as a hash.
|
265
262
|
#
|
@@ -276,7 +273,7 @@ module Hermeneutics
|
|
276
273
|
# :call-seq:
|
277
274
|
# encode( str) -> str
|
278
275
|
#
|
279
|
-
# Create a string that contains
|
276
|
+
# Create a string that contains +%XX+-encoded bytes.
|
280
277
|
#
|
281
278
|
# utx = URLText.new
|
282
279
|
# utx.encode "'Stop!' said Fred." #=> "%27Stop%21%27+said+Fred."
|
@@ -292,15 +289,14 @@ module Hermeneutics
|
|
292
289
|
# s = "< ä >".encode "ISO-8859-1"
|
293
290
|
# utx.encode s #=> "%3C+\xe4+%3E" in ISO-8859-1
|
294
291
|
#
|
295
|
-
# A space
|
296
|
-
#
|
292
|
+
# A space +" "+ will not be replaced by a plus +"\+"+ if +keep_space+ is
|
293
|
+
# set.
|
297
294
|
#
|
298
295
|
# utx = URLText.new keep_space: true
|
299
296
|
# s = "< x >"
|
300
297
|
# utx.encode s #=> "%3C x %3E"
|
301
298
|
#
|
302
|
-
# When +mask_space+ is set, then a space will be represented as
|
303
|
-
# <code>"%20"</code>,
|
299
|
+
# When +mask_space+ is set, then a space will be represented as +"%20"+,
|
304
300
|
#
|
305
301
|
def encode str
|
306
302
|
r = str.new_string
|
@@ -370,7 +366,7 @@ module Hermeneutics
|
|
370
366
|
# :call-seq:
|
371
367
|
# encode_hash( hash) -> str
|
372
368
|
#
|
373
|
-
# Encode a
|
369
|
+
# Encode a +Hash+ to a URL-style string.
|
374
370
|
#
|
375
371
|
# utx = URLText.new
|
376
372
|
#
|
@@ -470,8 +466,8 @@ module Hermeneutics
|
|
470
466
|
# decode_hash( str) -> hash
|
471
467
|
# decode_hash( str) { |key,val| ... } -> nil or int
|
472
468
|
#
|
473
|
-
# Decode a URL-style encoded string to a
|
474
|
-
#
|
469
|
+
# Decode a URL-style encoded string to a +Hash+. In case a block is
|
470
|
+
# given, the number of key-value pairs is returned.
|
475
471
|
#
|
476
472
|
# str = "a=%3B%3B%3B&x=%26auml%3B%26ouml%3B%26uuml%3B"
|
477
473
|
# URLText.decode_hash str do |k,v|
|
@@ -528,7 +524,7 @@ module Hermeneutics
|
|
528
524
|
# :call-seq:
|
529
525
|
# new( [ parameters] ) -> con
|
530
526
|
#
|
531
|
-
# Creates a
|
527
|
+
# Creates a +HeaderExt+ converter.
|
532
528
|
#
|
533
529
|
# See the +encode+ method for an explanation of the parameters.
|
534
530
|
#
|
@@ -572,8 +568,8 @@ module Hermeneutics
|
|
572
568
|
# The result will not contain any 8-bit characters. The encoding will
|
573
569
|
# be kept although it won't have a meaning.
|
574
570
|
#
|
575
|
-
# The parameter
|
576
|
-
#
|
571
|
+
# The parameter +:mask+ will have no influence on the masking itself but
|
572
|
+
# will guarantee characters to be masked.
|
577
573
|
#
|
578
574
|
# == Examples
|
579
575
|
#
|
data/lib/hermeneutics/html.rb
CHANGED
@@ -151,7 +151,7 @@ module Hermeneutics
|
|
151
151
|
@out << tag
|
152
152
|
mkattrs attrs
|
153
153
|
end
|
154
|
-
if nls
|
154
|
+
if nls>3 then
|
155
155
|
verbose_block yield
|
156
156
|
else
|
157
157
|
indent_if nls>2 do
|
@@ -213,9 +213,9 @@ module Hermeneutics
|
|
213
213
|
@out << "/* "
|
214
214
|
brace false do
|
215
215
|
@out << "![CDATA["
|
216
|
-
@out << "
|
216
|
+
@out << " */\n"
|
217
217
|
@out << str
|
218
|
-
@out <<
|
218
|
+
@out << "\n/* "
|
219
219
|
@out << "]]"
|
220
220
|
end
|
221
221
|
@out << " */"
|
@@ -227,7 +227,7 @@ module Hermeneutics
|
|
227
227
|
def brk
|
228
228
|
unless @nl then
|
229
229
|
@nl = true
|
230
|
-
@out <<
|
230
|
+
@out << "\n"
|
231
231
|
end
|
232
232
|
end
|
233
233
|
def out_brk str
|