tex_log_parser 1.0.0.pre.10 → 1.0.0.pre.14

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.
@@ -1,101 +1,105 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Matches messages of these forms:
4
- #
5
- # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
6
- # !
7
- # ./plain.tex:5: fontspec error: "font-not-found"
8
- # !
9
- # ! The font "NoSuchFont" cannot be found.
10
- # !
11
- # ! See the fontspec documentation for further information.
12
- # !
13
- # ! For immediate help type H <return>.
14
- # !...............................................
15
- #
16
- # l.5 \setmainfont{NoSuchFont}
17
- #
18
- # and
19
- #
20
- # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
21
- # !
22
- # ! fontspec error: "font-not-found"
23
- # !
24
- # ! The font "NoSuchFont" cannot be found.
25
- # !
26
- # ! See the fontspec documentation for further information.
27
- # !
28
- # ! For immediate help type H <return>.
29
- # !...............................................
30
- #
31
- # l.5 \setmainfont{NoSuchFont}
32
- #
33
- # and
34
- #
35
- # .................................................
36
- # . LaTeX info: "xparse/define-command"
37
- # .
38
- # . Defining command \fontspec with sig. 'O{}mO{}' on line 472.
39
- # .................................................
40
- class HighlightedMessages
41
- include RegExpPattern
3
+ class TexLogParser
4
+ # Matches messages of these forms:
5
+ #
6
+ # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
7
+ # !
8
+ # ./plain.tex:5: fontspec error: "font-not-found"
9
+ # !
10
+ # ! The font "NoSuchFont" cannot be found.
11
+ # !
12
+ # ! See the fontspec documentation for further information.
13
+ # !
14
+ # ! For immediate help type H <return>.
15
+ # !...............................................
16
+ #
17
+ # l.5 \setmainfont{NoSuchFont}
18
+ #
19
+ # and
20
+ #
21
+ # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
22
+ # !
23
+ # ! fontspec error: "font-not-found"
24
+ # !
25
+ # ! The font "NoSuchFont" cannot be found.
26
+ # !
27
+ # ! See the fontspec documentation for further information.
28
+ # !
29
+ # ! For immediate help type H <return>.
30
+ # !...............................................
31
+ #
32
+ # l.5 \setmainfont{NoSuchFont}
33
+ #
34
+ # and
35
+ #
36
+ # .................................................
37
+ # . LaTeX info: "xparse/define-command"
38
+ # .
39
+ # . Defining command \fontspec with sig. 'O{}mO{}' on line 472.
40
+ # .................................................
41
+ class HighlightedMessages
42
+ include LogParser::RegExpPattern
42
43
 
43
- def initialize
44
- super(/^(\!{3,}|\.{3,})$/,
45
- { pattern: lambda { |m|
46
- if m[1][0] == '!'
47
- /^l\.(\d+)/
48
- else
49
- /^\.{3,}\s*$/
50
- end
51
- },
52
- until: :match,
53
- inclusive: true })
54
- end
44
+ # Creates a new instance.
45
+ def initialize
46
+ super(/^(!{3,}|\.{3,})$/,
47
+ { pattern: lambda { |m|
48
+ if m[1][0] == '!'
49
+ /^l\.(\d+)/
50
+ else
51
+ /^\.{3,}\s*$/
52
+ end
53
+ },
54
+ until: :match,
55
+ inclusive: true })
56
+ end
55
57
 
56
- def read(lines)
57
- # @type [LogMessage] msg
58
- msg, consumed = super(lines)
58
+ # (see LogParser::RegExpPattern#read)
59
+ def read(lines)
60
+ # @type [Message] msg
61
+ msg, consumed = super(lines)
59
62
 
60
- is_error = @start_match[1][0] == '!'
63
+ is_error = @start_match[1][0] == '!'
61
64
 
62
- if is_error
63
- file_line_match = %r{^(/?(?:.*?/)*[^/]+):(\d+):\s*}.match(msg.message)
64
- line = nil
65
- if !file_line_match.nil? # if file-line active
66
- msg.source_file = file_line_match[1]
67
- line = file_line_match[2].to_i
68
- msg.message.gsub!(file_line_match[0], '')
69
- elsif !@end_match[1].nil?
70
- # No file-line format, so use line number from end match
71
- line = @end_match[1].to_i
72
- end
73
- msg.source_lines = { from: line, to: line } unless line.nil?
65
+ if is_error
66
+ file_line_match = %r{^(/?(?:.*?/)*[^/]+):(\d+):\s*}.match(msg.message)
67
+ line = nil
68
+ if !file_line_match.nil? # if file-line active
69
+ msg.source_file = file_line_match[1]
70
+ line = file_line_match[2].to_i
71
+ msg.message.gsub!(file_line_match[0], '')
72
+ elsif !@end_match[1].nil?
73
+ # No file-line format, so use line number from end match
74
+ line = @end_match[1].to_i
75
+ end
76
+ msg.source_lines = { from: line, to: line } unless line.nil?
74
77
 
75
- msg.level = :error
78
+ msg.level = :error
76
79
 
77
- msg.message.gsub!(/^.*?For immediate help type.*$/, '')
78
- msg.message.gsub!(/^\!\.+\s*$/, '')
79
- msg.message.gsub!(/^l\.\d+\s+.*$/, '')
80
- else
81
- # BROKEN_BY_LINEBREAKS
82
- # TODO: may be split across lines --> remove whitespace before extracting
83
- line_match = /on line\s+(\d+)\.$/.match(msg.message)
84
- unless line_match.nil?
85
- line = line_match[1].to_i
86
- msg.source_lines = { from: line, to: line }
87
- end
80
+ msg.message.gsub!(/^.*?For immediate help type.*$/, '')
81
+ msg.message.gsub!(/^!\.+\s*$/, '')
82
+ msg.message.gsub!(/^l\.\d+\s+.*$/, '')
83
+ else
84
+ # BROKEN_BY_LINEBREAKS
85
+ # TODO: may be split across lines --> remove whitespace before extracting
86
+ line_match = /on line\s+(\d+)\.$/.match(msg.message)
87
+ unless line_match.nil?
88
+ line = line_match[1].to_i
89
+ msg.source_lines = { from: line, to: line }
90
+ end
88
91
 
89
- msg.level = :info
92
+ msg.level = :info
90
93
 
91
- msg.message.gsub!(@end_match[0], '')
92
- end
94
+ msg.message.gsub!(@end_match[0], '')
95
+ end
93
96
 
94
- msg.preformatted = true
95
- msg.message.gsub!(@start, '')
96
- msg.message.gsub!(/^#{@start_match[1][0]}\s+/, '')
97
- msg.message.strip!
97
+ msg.preformatted = true
98
+ msg.message.gsub!(@start, '')
99
+ msg.message.gsub!(/^#{@start_match[1][0]}\s+/, '')
100
+ msg.message.strip!
98
101
 
99
- [msg, consumed]
102
+ [msg, consumed]
103
+ end
100
104
  end
101
- end
105
+ end
@@ -1,51 +1,55 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Matches messages of this form:
4
- #
5
- # Package tocbasic Info: omitting babel extension for `toc'
6
- # (tocbasic) because of feature `nobabel' available
7
- # (tocbasic) for `toc' on input line 132.
8
- #
9
- # Note: currently fails if lines get broken badly, e.g. in 000.log:634.
10
- class PrefixedMultiLinePattern
11
- include RegExpPattern
12
-
13
- def initialize
14
- super(/(Package|Class|\w+TeX)\s+(?:(\w+)\s+)?(Warning|Error|Info|Message)/,
15
- { pattern: ->(m) { /^\s*\(#{m[2]}\)/ }, # BROKEN_BY_LINEBREAKS
16
- until: :mismatch,
17
- inclusive: false })
18
- end
19
-
20
- def read(lines)
21
- # @type [LogMessage] msg
22
- # @type [Int] consumed
23
- msg, consumed = super(lines)
24
-
25
- case @start_match[3]
26
- when 'Error'
27
- msg.level = :error
28
- when 'Warning'
29
- msg.level = :warning
30
- when 'Info', 'Message'
31
- msg.level = :info
32
- else
33
- # TODO: abort?
34
- Logger.debug 'Unhandled message type!'
3
+ class TexLogParser
4
+ # Matches messages of this form:
5
+ #
6
+ # Package tocbasic Info: omitting babel extension for `toc'
7
+ # (tocbasic) because of feature `nobabel' available
8
+ # (tocbasic) for `toc' on input line 132.
9
+ #
10
+ # Note: currently fails if lines get broken badly, e.g. in 000.log:634.
11
+ class PrefixedMultiLinePattern
12
+ include LogParser::RegExpPattern
13
+
14
+ # Creates a new instance.
15
+ def initialize
16
+ super(/(Package|Class|\w+TeX)\s+(?:(\w+)\s+)?(Warning|Error|Info|Message)/,
17
+ { pattern: ->(m) { /^\s*\(#{m[2]}\)/ }, # BROKEN_BY_LINEBREAKS
18
+ until: :mismatch,
19
+ inclusive: false })
35
20
  end
36
21
 
37
- # source file from scope, parser does it
38
-
39
- # BROKEN_BY_LINEBREAKS
40
- # TODO: may be split across lines --> remove whitespace before extracting
41
- suffix_match = /on input line\s+(\d+)(?:\.\s*)?\z/.match(msg.message)
42
- unless suffix_match.nil?
43
- line = suffix_match[1].to_i
44
- msg.source_lines = { from: line, to: line }
22
+ # (see LogParser::RegExpPattern#read)
23
+ def read(lines)
24
+ # @type [Message] msg
25
+ # @type [Int] consumed
26
+ msg, consumed = super(lines)
27
+
28
+ case @start_match[3]
29
+ when 'Error'
30
+ msg.level = :error
31
+ when 'Warning'
32
+ msg.level = :warning
33
+ when 'Info', 'Message'
34
+ msg.level = :info
35
+ else
36
+ # TODO: abort?
37
+ Logger.debug 'Unhandled message type!'
38
+ end
39
+
40
+ # source file from scope, parser does it
41
+
42
+ # BROKEN_BY_LINEBREAKS
43
+ # TODO: may be split across lines --> remove whitespace before extracting
44
+ suffix_match = /on input line\s+(\d+)(?:\.\s*)?\z/.match(msg.message)
45
+ unless suffix_match.nil?
46
+ line = suffix_match[1].to_i
47
+ msg.source_lines = { from: line, to: line }
48
+ end
49
+
50
+ # TODO: message itself contains useless line prefixes --> remove, preformatted = false
51
+
52
+ [msg, consumed]
45
53
  end
46
-
47
- # TODO: message itself contains useless line prefixes --> remove, preformatted = false
48
-
49
- [msg, consumed]
50
54
  end
51
55
  end
@@ -1,24 +1,28 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Matches messages of this form:
4
- #
5
- # Runaway argument?
6
- # {Test. Also, it contains some \ref {warnings} and \ref {errors} for t\ETC.
7
- class RunawayParameterError
8
- include RegExpPattern
3
+ class TexLogParser
4
+ # Matches messages of this form:
5
+ #
6
+ # Runaway argument?
7
+ # {Test. Also, it contains some \ref {warnings} and \ref {errors} for t\ETC.
8
+ class RunawayParameterError
9
+ include LogParser::RegExpPattern
9
10
 
10
- def initialize
11
- super(/^Runaway argument\?/,
12
- { pattern: ->(_) { /./ }, until: :match, inclusive: true }
13
- )
14
- end
11
+ # Creates a new instance.
12
+ def initialize
13
+ super(/^Runaway argument\?/,
14
+ { pattern: ->(_) { /./ }, until: :match, inclusive: true }
15
+ )
16
+ end
15
17
 
16
- def read(lines)
17
- # @type [LogMessage] msg
18
- msg, consumed = super(lines)
18
+ # (see LogParser::RegExpPattern#read)
19
+ def read(lines)
20
+ # @type [Message] msg
21
+ msg, consumed = super(lines)
19
22
 
20
- msg.level = :error
23
+ msg.level = :error
21
24
 
22
- [msg, consumed]
25
+ [msg, consumed]
26
+ end
23
27
  end
24
- end
28
+ end
@@ -1,44 +1,48 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Matches messages of this form:
4
- #
5
- # ! File ended while scanning use of \@footnotetext.
6
- # <inserted text>
7
- # \par
8
- # <*> plain.tex
9
- #
10
- # and
11
- #
12
- # ! Font TU/NoSuchFont(0)/m/n/9=NoSuchFont at 9.0pt not loadable: Metric (TFM) fi
13
- # le or installed font not found.
14
- # <to be read again>
15
- # relax
16
- # l.40 \end{document}
17
- class StandardError
18
- include RegExpPattern
3
+ class TexLogParser
4
+ # Matches messages of this form:
5
+ #
6
+ # ! File ended while scanning use of \@footnotetext.
7
+ # <inserted text>
8
+ # \par
9
+ # <*> plain.tex
10
+ #
11
+ # and
12
+ #
13
+ # ! Font TU/NoSuchFont(0)/m/n/9=NoSuchFont at 9.0pt not loadable: Metric (TFM) fi
14
+ # le or installed font not found.
15
+ # <to be read again>
16
+ # relax
17
+ # l.40 \end{document}
18
+ class StandardError
19
+ include LogParser::RegExpPattern
19
20
 
20
- def initialize
21
- super(/^\! \w+/,
22
- { pattern: ->(_) { /^\s*<\*>\s+([^\s]+)|^l\.(\d+)\s+/ }, until: :match, inclusive: true }
23
- )
24
- end
21
+ # Creates a new instance.
22
+ def initialize
23
+ super(/^! \w+/,
24
+ { pattern: ->(_) { /^\s*<\*>\s+([^\s]+)|^l\.(\d+)\s+/ }, until: :match, inclusive: true }
25
+ )
26
+ end
25
27
 
26
- def read(lines)
27
- # @type [LogMessage] msg
28
- msg, consumed = super(lines)
28
+ # (see LogParser::RegExpPattern#read)
29
+ def read(lines)
30
+ # @type [Message] msg
31
+ msg, consumed = super(lines)
29
32
 
30
- msg.level = :error
31
- # Remove last line
32
- msg.message.gsub!(@ending[:pattern][nil], '')
33
- msg.message.rstrip!
33
+ msg.level = :error
34
+ # Remove last line
35
+ msg.message.gsub!(@ending[:pattern][nil], '')
36
+ msg.message.rstrip!
34
37
 
35
- file = @end_match[1]
36
- line = @end_match[2].to_i
38
+ file = @end_match[1]
39
+ line = @end_match[2].to_i
37
40
 
38
- msg.source_file = file unless file.nil?
39
- msg.source_lines = { from: line, to: line } unless line.nil? || line.zero?
40
- msg.preformatted = true
41
+ msg.source_file = file unless file.nil?
42
+ msg.source_lines = { from: line, to: line } unless line.nil? || line.zero?
43
+ msg.preformatted = true
41
44
 
42
- [msg, consumed]
45
+ [msg, consumed]
46
+ end
43
47
  end
44
- end
48
+ end
@@ -1,24 +1,30 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'logger'
4
- require 'tex_log_parser/log_buffer'
5
- require 'tex_log_parser/log_message'
6
- require 'tex_log_parser/log_parser'
7
- require 'tex_log_parser/log_pattern'
3
+ require 'log_parser/log_parser'
8
4
  Dir["#{File.expand_path(__dir__)}/tex_log_parser/patterns/*.rb"].each { |p| require p }
9
5
 
10
- # TODO: document
6
+ # Parses logs (and output) of LaTeX interpreters, e.g. `pdflatex`, `xelatex` and `lualatex`.
7
+ #
8
+ # *Note:* Due to shortcomings in the native format of those logs, please be
9
+ # aware of these recommendations:
10
+ # - Use `-file-line-error` if possible; it makes for more robust source file and line reporting.
11
+ # - Ask for log lines to be broken as rarely as possible; see e.g. [here](https://tex.stackexchange.com/q/52988/3213).
12
+ # Search the sources for `BROKEN_BY_LINEBREAKS` to find all the nastiness (and potential issues) you can avoid by that.
11
13
  class TexLogParser
12
14
  include LogParser
13
15
 
14
- def initialize(log, _options = {})
15
- super(log, _options)
16
+ # (see LogParser#initialize)
17
+ def initialize(log)
18
+ super(log)
16
19
 
17
20
  # BROKEN_BY_LINEBREAKS
18
21
  # I'd prefer to have this stateless, but well (see below).
19
22
  @pushed_dummy = false
20
23
  end
21
24
 
25
+ protected
26
+
27
+ # (see LogParser#patterns)
22
28
  def patterns
23
29
  [HighlightedMessages.new,
24
30
  FileLineError.new,
@@ -29,7 +35,22 @@ class TexLogParser
29
35
  BadHboxWarning.new]
30
36
  end
31
37
 
38
+ # (see LogParser#scope_changes)
39
+ #
40
+ # _Implementation note:_ The basic format in LaTeX logs is that
41
+ # * `(filename` marks the beginning of messages from that file, and
42
+ # * the matching `)` marks the end.
43
+ # Those nest, of course.
44
+ #
45
+ # This implementation is mainly concerned with hacking around problems of how this format is implemented in the logs.
46
+ # * Filenames may be broken across lines.
47
+ # * Opening "tags" may or may not appear on a dedicated line.
48
+ # * Closing "tags" may or may not appear on a dedicated line.
49
+ # * Badly line-broken message fragments with parentheses confuse matching heuristics for either.
50
+ # If inopportune line breaks can be avoided, this method is a lot more reliable.
32
51
  def scope_changes(line)
52
+ pushed_dummy = false
53
+
33
54
  result =
34
55
  case line
35
56
  when /^\s*\(([^()]*)\)\s*(.*)$/
@@ -55,7 +76,7 @@ class TexLogParser
55
76
  when /^[^()]*\)/
56
77
  # BROKEN_BY_LINEBREAKS
57
78
  # Badly broken lines may push ) prefixed by non-whitespace. Narf.
58
- [:pop] if @pushed_dummy ? [:pop] : []
79
+ @pushed_dummy ? [:pop] : []
59
80
  else
60
81
  []
61
82
  end
data/lib/version.rb ADDED
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @attr [String] VERSION
4
+ # The version of TexLogParser.
5
+ class TexLogParser
6
+ VERSION = '1.0.0.pre.14'
7
+ end
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tex_log_parser
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0.pre.10
4
+ version: 1.0.0.pre.14
5
5
  platform: ruby
6
6
  authors:
7
7
  - Raphael Reitzig
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-04-07 00:00:00.000000000 Z
11
+ date: 2018-04-08 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: github-markup
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.0'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: minitest
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -38,6 +52,20 @@ dependencies:
38
52
  - - "~>"
39
53
  - !ruby/object:Gem::Version
40
54
  version: '12.3'
55
+ - !ruby/object:Gem::Dependency
56
+ name: redcarpet
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.4'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3.4'
41
69
  - !ruby/object:Gem::Dependency
42
70
  name: yard
43
71
  requirement: !ruby/object:Gem::Requirement
@@ -76,12 +104,12 @@ files:
76
104
  - LICENSE
77
105
  - README.md
78
106
  - bin/texlogparser
79
- - lib/logger.rb
107
+ - lib/log_parser/buffer.rb
108
+ - lib/log_parser/log_parser.rb
109
+ - lib/log_parser/logger.rb
110
+ - lib/log_parser/message.rb
111
+ - lib/log_parser/pattern.rb
80
112
  - lib/tex_log_parser.rb
81
- - lib/tex_log_parser/log_buffer.rb
82
- - lib/tex_log_parser/log_message.rb
83
- - lib/tex_log_parser/log_parser.rb
84
- - lib/tex_log_parser/log_pattern.rb
85
113
  - lib/tex_log_parser/patterns/bad_hbox_warning.rb
86
114
  - lib/tex_log_parser/patterns/fatal_error_occurred.rb
87
115
  - lib/tex_log_parser/patterns/file_line_error.rb
@@ -89,7 +117,7 @@ files:
89
117
  - lib/tex_log_parser/patterns/prefixed_multi_line_pattern.rb
90
118
  - lib/tex_log_parser/patterns/runaway_parameter_error.rb
91
119
  - lib/tex_log_parser/patterns/standard_error.rb
92
- - lib/tex_log_parser/version.rb
120
+ - lib/version.rb
93
121
  homepage: http://github.com/reitzig/texlogparser
94
122
  licenses:
95
123
  - MIT
@@ -111,7 +139,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
111
139
  version: 1.3.1
112
140
  requirements: []
113
141
  rubyforge_project:
114
- rubygems_version: 2.5.2.1
142
+ rubygems_version: 2.7.6
115
143
  signing_key:
116
144
  specification_version: 4
117
145
  summary: Parses log files of (La)TeX engines
data/lib/logger.rb DELETED
@@ -1,21 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # A simple helper, probably to be replaced by a proper logging library
4
- # at some point.
5
- #
6
- # @attr [true,false] debug
7
- class Logger
8
- class << self
9
- attr_accessor :debugging
10
-
11
- def debug?
12
- debugging || !ENV['DEBUG'].nil?
13
- end
14
-
15
- # Logs the given message to STDOUT if `debug` is true.
16
- # @param [String] message
17
- def debug(message)
18
- puts message if debug?
19
- end
20
- end
21
- end
@@ -1,62 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class LogBuffer
4
- # @param [Array<String>,IO] log
5
- def initialize(log)
6
- @buffer = []
7
- @stream = nil
8
-
9
- @buffer = log if log.is_a?(Array)
10
- @stream = log if log.is_a?(IO) || log.is_a?(StringIO)
11
- end
12
-
13
- def empty?
14
- @buffer.empty? && stream_is_done?
15
- end
16
-
17
- def buffer_size
18
- @buffer.size
19
- end
20
-
21
- def first
22
- self[0]
23
- end
24
-
25
- def find_index(starting_from = 0)
26
- index = starting_from
27
- element = self[index]
28
-
29
- until element.nil?
30
- return index if yield(element)
31
- index += 1
32
- element = self[index]
33
- end
34
-
35
- nil
36
- end
37
-
38
- def [](offset, length = nil)
39
- base_length = length || 1
40
- while offset + base_length > @buffer.size
41
- return (length.nil? ? nil : @buffer[offset, @buffer.size]) if stream_is_done?
42
- @buffer.push(@stream.readline.rstrip)
43
- end
44
-
45
- length.nil? ? @buffer[offset] : @buffer[offset, length]
46
- end
47
-
48
- def forward(offset = 1)
49
- self[offset]
50
- @buffer.slice!(0, offset)
51
- end
52
-
53
- def close
54
- @stream.close unless @stream.nil? || @stream.closed?
55
- end
56
-
57
- private
58
-
59
- def stream_is_done?
60
- @stream.nil? || @stream.closed? || @stream.eof?
61
- end
62
- end