asciidoctor 2.0.10 → 2.0.11
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.adoc +89 -5
- data/LICENSE +1 -1
- data/README-de.adoc +4 -4
- data/README-fr.adoc +4 -4
- data/README-jp.adoc +4 -4
- data/README-zh_CN.adoc +6 -6
- data/README.adoc +14 -10
- data/asciidoctor.gemspec +3 -3
- data/data/locale/attributes-ar.adoc +4 -3
- data/data/locale/attributes-bg.adoc +4 -3
- data/data/locale/attributes-ca.adoc +6 -5
- data/data/locale/attributes-cs.adoc +4 -3
- data/data/locale/attributes-da.adoc +6 -5
- data/data/locale/attributes-de.adoc +4 -4
- data/data/locale/attributes-en.adoc +4 -4
- data/data/locale/attributes-es.adoc +6 -5
- data/data/locale/attributes-fa.adoc +4 -3
- data/data/locale/attributes-fi.adoc +4 -3
- data/data/locale/attributes-fr.adoc +6 -5
- data/data/locale/attributes-hu.adoc +4 -3
- data/data/locale/attributes-id.adoc +4 -3
- data/data/locale/attributes-it.adoc +4 -3
- data/data/locale/attributes-ja.adoc +4 -3
- data/data/locale/{attributes-kr.adoc → attributes-ko.adoc} +4 -3
- data/data/locale/attributes-nb.adoc +4 -3
- data/data/locale/attributes-nl.adoc +4 -3
- data/data/locale/attributes-nn.adoc +4 -3
- data/data/locale/attributes-pl.adoc +8 -7
- data/data/locale/attributes-pt.adoc +6 -5
- data/data/locale/attributes-pt_BR.adoc +6 -5
- data/data/locale/attributes-ro.adoc +4 -3
- data/data/locale/attributes-ru.adoc +6 -5
- data/data/locale/attributes-sr.adoc +4 -4
- data/data/locale/attributes-sr_Latn.adoc +4 -4
- data/data/locale/attributes-sv.adoc +4 -4
- data/data/locale/attributes-tr.adoc +4 -3
- data/data/locale/attributes-uk.adoc +6 -5
- data/data/locale/attributes-zh_CN.adoc +4 -3
- data/data/locale/attributes-zh_TW.adoc +4 -3
- data/data/stylesheets/asciidoctor-default.css +22 -20
- data/lib/asciidoctor.rb +33 -7
- data/lib/asciidoctor/abstract_block.rb +9 -4
- data/lib/asciidoctor/abstract_node.rb +16 -6
- data/lib/asciidoctor/attribute_list.rb +59 -67
- data/lib/asciidoctor/cli/invoker.rb +2 -0
- data/lib/asciidoctor/cli/options.rb +3 -3
- data/lib/asciidoctor/convert.rb +167 -162
- data/lib/asciidoctor/converter.rb +13 -12
- data/lib/asciidoctor/converter/docbook5.rb +3 -7
- data/lib/asciidoctor/converter/html5.rb +59 -42
- data/lib/asciidoctor/converter/manpage.rb +12 -11
- data/lib/asciidoctor/converter/template.rb +3 -0
- data/lib/asciidoctor/document.rb +23 -38
- data/lib/asciidoctor/extensions.rb +2 -2
- data/lib/asciidoctor/helpers.rb +17 -9
- data/lib/asciidoctor/load.rb +101 -101
- data/lib/asciidoctor/parser.rb +26 -23
- data/lib/asciidoctor/path_resolver.rb +14 -12
- data/lib/asciidoctor/reader.rb +14 -7
- data/lib/asciidoctor/rx.rb +5 -4
- data/lib/asciidoctor/substitutors.rb +57 -38
- data/lib/asciidoctor/syntax_highlighter.rb +8 -5
- data/lib/asciidoctor/syntax_highlighter/coderay.rb +1 -1
- data/lib/asciidoctor/syntax_highlighter/highlightjs.rb +12 -4
- data/lib/asciidoctor/syntax_highlighter/prettify.rb +7 -4
- data/lib/asciidoctor/syntax_highlighter/pygments.rb +2 -3
- data/lib/asciidoctor/syntax_highlighter/rouge.rb +15 -7
- data/lib/asciidoctor/table.rb +49 -20
- data/lib/asciidoctor/version.rb +1 -1
- data/man/asciidoctor.1 +6 -6
- data/man/asciidoctor.adoc +3 -3
- metadata +8 -8
@@ -425,7 +425,7 @@ module Extensions
|
|
425
425
|
# TIP: Postprocessors can also be used to relocate assets needed by the published
|
426
426
|
# document.
|
427
427
|
#
|
428
|
-
# Postprocessor implementations must Postprocessor.
|
428
|
+
# Postprocessor implementations must extend Postprocessor.
|
429
429
|
class Postprocessor < Processor
|
430
430
|
def process document, output
|
431
431
|
raise ::NotImplementedError, %(#{Postprocessor} subclass #{self.class} must implement the ##{__method__} method)
|
@@ -610,7 +610,7 @@ module Extensions
|
|
610
610
|
#--
|
611
611
|
# TODO break this out into different pattern types
|
612
612
|
# for example, FullInlineMacro, ShortInlineMacro (no target) and other patterns
|
613
|
-
# FIXME for inline
|
613
|
+
# FIXME for inline macro, we need to have some way to specify the text as a passthrough
|
614
614
|
class InlineMacroProcessor < MacroProcessor
|
615
615
|
@@rx_cache = {}
|
616
616
|
|
data/lib/asciidoctor/helpers.rb
CHANGED
@@ -56,25 +56,27 @@ module Helpers
|
|
56
56
|
# If a BOM is found at the beginning of the data, a best attempt is made to
|
57
57
|
# encode it to UTF-8 from the specified source encoding.
|
58
58
|
#
|
59
|
-
# data
|
59
|
+
# data - the source data Array to prepare (no nil entries allowed)
|
60
|
+
# trim_end - whether to trim whitespace from the end of each line;
|
61
|
+
# (true cleans all whitespace; false only removes trailing newline) (default: true)
|
60
62
|
#
|
61
63
|
# returns a String Array of prepared lines
|
62
|
-
def prepare_source_array data
|
64
|
+
def prepare_source_array data, trim_end = true
|
63
65
|
return [] if data.empty?
|
64
66
|
if (leading_2_bytes = (leading_bytes = (first = data[0]).unpack 'C3').slice 0, 2) == BOM_BYTES_UTF_16LE
|
65
67
|
data[0] = first.byteslice 2, first.bytesize
|
66
68
|
# NOTE you can't split a UTF-16LE string using .lines when encoding is UTF-8; doing so will cause this line to fail
|
67
|
-
return data.map {|line| (line.encode UTF_8, ::Encoding::UTF_16LE).rstrip }
|
69
|
+
return trim_end ? data.map {|line| (line.encode UTF_8, ::Encoding::UTF_16LE).rstrip } : data.map {|line| (line.encode UTF_8, ::Encoding::UTF_16LE).chomp }
|
68
70
|
elsif leading_2_bytes == BOM_BYTES_UTF_16BE
|
69
71
|
data[0] = first.byteslice 2, first.bytesize
|
70
|
-
return data.map {|line| (line.encode UTF_8, ::Encoding::UTF_16BE).rstrip }
|
72
|
+
return trim_end ? data.map {|line| (line.encode UTF_8, ::Encoding::UTF_16BE).rstrip } : data.map {|line| (line.encode UTF_8, ::Encoding::UTF_16BE).chomp }
|
71
73
|
elsif leading_bytes == BOM_BYTES_UTF_8
|
72
74
|
data[0] = first.byteslice 3, first.bytesize
|
73
75
|
end
|
74
76
|
if first.encoding == UTF_8
|
75
|
-
data.map {|line| line.rstrip }
|
77
|
+
trim_end ? data.map {|line| line.rstrip } : data.map {|line| line.chomp }
|
76
78
|
else
|
77
|
-
data.map {|line| (line.encode UTF_8).rstrip }
|
79
|
+
trim_end ? data.map {|line| (line.encode UTF_8).rstrip } : data.map {|line| (line.encode UTF_8).chomp }
|
78
80
|
end
|
79
81
|
end
|
80
82
|
|
@@ -86,10 +88,12 @@ module Helpers
|
|
86
88
|
# If a BOM is found at the beginning of the data, a best attempt is made to
|
87
89
|
# encode it to UTF-8 from the specified source encoding.
|
88
90
|
#
|
89
|
-
# data
|
91
|
+
# data - the source data String to prepare
|
92
|
+
# trim_end - whether to trim whitespace from the end of each line;
|
93
|
+
# (true cleans all whitespace; false only removes trailing newline) (default: true)
|
90
94
|
#
|
91
95
|
# returns a String Array of prepared lines
|
92
|
-
def prepare_source_string data
|
96
|
+
def prepare_source_string data, trim_end = true
|
93
97
|
return [] if data.nil_or_empty?
|
94
98
|
if (leading_2_bytes = (leading_bytes = data.unpack 'C3').slice 0, 2) == BOM_BYTES_UTF_16LE
|
95
99
|
data = (data.byteslice 2, data.bytesize).encode UTF_8, ::Encoding::UTF_16LE
|
@@ -101,7 +105,11 @@ module Helpers
|
|
101
105
|
elsif data.encoding != UTF_8
|
102
106
|
data = data.encode UTF_8
|
103
107
|
end
|
104
|
-
|
108
|
+
if trim_end
|
109
|
+
[].tap {|lines| data.each_line {|line| lines << line.rstrip } }
|
110
|
+
else
|
111
|
+
[].tap {|lines| data.each_line {|line| lines << line.chomp } }
|
112
|
+
end
|
105
113
|
end
|
106
114
|
|
107
115
|
# Internal: Efficiently checks whether the specified String resembles a URI
|
data/lib/asciidoctor/load.rb
CHANGED
@@ -1,117 +1,117 @@
|
|
1
1
|
module Asciidoctor
|
2
|
-
|
2
|
+
class << self
|
3
|
+
# Public: Parse the AsciiDoc source input into a {Document}
|
4
|
+
#
|
5
|
+
# Accepts input as an IO (or StringIO), String or String Array object. If the
|
6
|
+
# input is a File, the object is expected to be opened for reading and is not
|
7
|
+
# closed afterwards by this method. Information about the file (filename,
|
8
|
+
# directory name, etc) gets assigned to attributes on the Document object.
|
9
|
+
#
|
10
|
+
# input - the AsciiDoc source as a IO, String or Array.
|
11
|
+
# options - a String, Array or Hash of options to control processing (default: {})
|
12
|
+
# String and Array values are converted into a Hash.
|
13
|
+
# See {Document#initialize} for details about these options.
|
14
|
+
#
|
15
|
+
# Returns the Document
|
16
|
+
def load input, options = {}
|
17
|
+
options = options.merge
|
3
18
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
# input is a File, the object is expected to be opened for reading and is not
|
8
|
-
# closed afterwards by this method. Information about the file (filename,
|
9
|
-
# directory name, etc) gets assigned to attributes on the Document object.
|
10
|
-
#
|
11
|
-
# input - the AsciiDoc source as a IO, String or Array.
|
12
|
-
# options - a String, Array or Hash of options to control processing (default: {})
|
13
|
-
# String and Array values are converted into a Hash.
|
14
|
-
# See {Document#initialize} for details about these options.
|
15
|
-
#
|
16
|
-
# Returns the Document
|
17
|
-
def load input, options = {}
|
18
|
-
options = options.merge
|
19
|
-
|
20
|
-
if (timings = options[:timings])
|
21
|
-
timings.start :read
|
22
|
-
end
|
19
|
+
if (timings = options[:timings])
|
20
|
+
timings.start :read
|
21
|
+
end
|
23
22
|
|
24
|
-
|
25
|
-
|
26
|
-
|
23
|
+
if (logger = options[:logger]) && logger != LoggerManager.logger
|
24
|
+
LoggerManager.logger = logger
|
25
|
+
end
|
27
26
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
27
|
+
if !(attrs = options[:attributes])
|
28
|
+
attrs = {}
|
29
|
+
elsif ::Hash === attrs
|
30
|
+
attrs = attrs.merge
|
31
|
+
elsif (defined? ::Java::JavaUtil::Map) && ::Java::JavaUtil::Map === attrs
|
32
|
+
attrs = attrs.dup
|
33
|
+
elsif ::Array === attrs
|
34
|
+
attrs = {}.tap do |accum|
|
35
|
+
attrs.each do |entry|
|
36
|
+
k, _, v = entry.partition '='
|
37
|
+
accum[k] = v
|
38
|
+
end
|
39
39
|
end
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
40
|
+
elsif ::String === attrs
|
41
|
+
# condense and convert non-escaped spaces to null, unescape escaped spaces, then split on null
|
42
|
+
attrs = {}.tap do |accum|
|
43
|
+
attrs.gsub(SpaceDelimiterRx, '\1' + NULL).gsub(EscapedSpaceRx, '\1').split(NULL).each do |entry|
|
44
|
+
k, _, v = entry.partition '='
|
45
|
+
accum[k] = v
|
46
|
+
end
|
47
47
|
end
|
48
|
+
elsif (attrs.respond_to? :keys) && (attrs.respond_to? :[])
|
49
|
+
# coerce attrs to a real Hash
|
50
|
+
attrs = {}.tap {|accum| attrs.keys.each {|k| accum[k] = attrs[k] } }
|
51
|
+
else
|
52
|
+
raise ::ArgumentError, %(illegal type for attributes option: #{attrs.class.ancestors.join ' < '})
|
48
53
|
end
|
49
|
-
elsif (attrs.respond_to? :keys) && (attrs.respond_to? :[])
|
50
|
-
# coerce attrs to a real Hash
|
51
|
-
attrs = {}.tap {|accum| attrs.keys.each {|k| accum[k] = attrs[k] } }
|
52
|
-
else
|
53
|
-
raise ::ArgumentError, %(illegal type for attributes option: #{attrs.class.ancestors.join ' < '})
|
54
|
-
end
|
55
54
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
55
|
+
if ::File === input
|
56
|
+
options[:input_mtime] = input.mtime
|
57
|
+
# NOTE defer setting infile and indir until we get a better sense of their purpose
|
58
|
+
# TODO cli checks if input path can be read and is file, but might want to add check to API too
|
59
|
+
attrs['docfile'] = input_path = ::File.absolute_path input.path
|
60
|
+
attrs['docdir'] = ::File.dirname input_path
|
61
|
+
attrs['docname'] = Helpers.basename input_path, (attrs['docfilesuffix'] = Helpers.extname input_path)
|
62
|
+
source = input.read
|
63
|
+
elsif input.respond_to? :read
|
64
|
+
# NOTE tty, pipes & sockets can't be rewound, but can't be sniffed easily either
|
65
|
+
# just fail the rewind operation silently to handle all cases
|
66
|
+
input.rewind rescue nil
|
67
|
+
source = input.read
|
68
|
+
elsif ::String === input
|
69
|
+
source = input
|
70
|
+
elsif ::Array === input
|
71
|
+
source = input.drop 0
|
72
|
+
elsif input
|
73
|
+
raise ::ArgumentError, %(unsupported input type: #{input.class})
|
74
|
+
end
|
76
75
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
76
|
+
if timings
|
77
|
+
timings.record :read
|
78
|
+
timings.start :parse
|
79
|
+
end
|
81
80
|
|
82
|
-
|
83
|
-
|
81
|
+
options[:attributes] = attrs
|
82
|
+
doc = options[:parse] == false ? (Document.new source, options) : (Document.new source, options).parse
|
84
83
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
84
|
+
timings.record :parse if timings
|
85
|
+
doc
|
86
|
+
rescue => ex
|
87
|
+
begin
|
88
|
+
context = %(asciidoctor: FAILED: #{attrs['docfile'] || '<stdin>'}: Failed to load AsciiDoc document)
|
89
|
+
if ex.respond_to? :exception
|
90
|
+
# The original message must be explicitly preserved when wrapping a Ruby exception
|
91
|
+
wrapped_ex = ex.exception %(#{context} - #{ex.message})
|
92
|
+
# JRuby automatically sets backtrace; MRI did not until 2.6
|
93
|
+
wrapped_ex.set_backtrace ex.backtrace
|
94
|
+
else
|
95
|
+
# Likely a Java exception class
|
96
|
+
wrapped_ex = ex.class.new context, ex
|
97
|
+
wrapped_ex.stack_trace = ex.stack_trace
|
98
|
+
end
|
99
|
+
rescue
|
100
|
+
wrapped_ex = ex
|
99
101
|
end
|
100
|
-
|
101
|
-
wrapped_ex = ex
|
102
|
+
raise wrapped_ex
|
102
103
|
end
|
103
|
-
raise wrapped_ex
|
104
|
-
end
|
105
104
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
105
|
+
# Public: Parse the contents of the AsciiDoc source file into an Asciidoctor::Document
|
106
|
+
#
|
107
|
+
# input - the String AsciiDoc source filename
|
108
|
+
# options - a String, Array or Hash of options to control processing (default: {})
|
109
|
+
# String and Array values are converted into a Hash.
|
110
|
+
# See Asciidoctor::Document#initialize for details about options.
|
111
|
+
#
|
112
|
+
# Returns the Asciidoctor::Document
|
113
|
+
def load_file filename, options = {}
|
114
|
+
::File.open(filename, FILE_READ_MODE) {|file| load file, options }
|
115
|
+
end
|
116
116
|
end
|
117
117
|
end
|
data/lib/asciidoctor/parser.rb
CHANGED
@@ -433,8 +433,10 @@ class Parser
|
|
433
433
|
# is treated like an untitled section
|
434
434
|
elsif preamble # implies parent == document
|
435
435
|
if preamble.blocks?
|
436
|
+
if book || document.blocks[1] || !Compliance.unwrap_standalone_preamble
|
437
|
+
preamble.source_location = preamble.blocks[0].source_location if document.sourcemap
|
436
438
|
# unwrap standalone preamble (i.e., document has no sections) except for books, if permissible
|
437
|
-
|
439
|
+
else
|
438
440
|
document.blocks.shift
|
439
441
|
while (child_block = preamble.blocks.shift)
|
440
442
|
document << child_block
|
@@ -858,6 +860,7 @@ class Parser
|
|
858
860
|
when :literal
|
859
861
|
block = build_block(block_context, :verbatim, terminator, parent, reader, attributes)
|
860
862
|
when :example
|
863
|
+
attributes['caption'] = '' if attributes['collapsible-option']
|
861
864
|
block = build_block(block_context, :compound, terminator, parent, reader, attributes)
|
862
865
|
when :quote, :verse
|
863
866
|
AttributeList.rekey(attributes, [nil, 'attribution', 'citetitle'])
|
@@ -899,7 +902,7 @@ class Parser
|
|
899
902
|
# FIXME title and caption should be assigned when block is constructed (though we need to handle all cases)
|
900
903
|
if attributes['title']
|
901
904
|
block.title = block_title = attributes.delete 'title'
|
902
|
-
if (caption_attr_name =
|
905
|
+
if (caption_attr_name = CAPTION_ATTRIBUTE_NAMES[block.context]) && document.attributes[caption_attr_name]
|
903
906
|
block.assign_caption (attributes.delete 'caption')
|
904
907
|
end
|
905
908
|
end
|
@@ -1148,14 +1151,16 @@ class Parser
|
|
1148
1151
|
def self.catalog_inline_anchors text, block, document, reader
|
1149
1152
|
text.scan InlineAnchorScanRx do
|
1150
1153
|
if (id = $1)
|
1151
|
-
if (reftext = $2)
|
1152
|
-
next if (reftext.include? ATTR_REF_HEAD) && (reftext = document.sub_attributes reftext).empty?
|
1153
|
-
end
|
1154
|
+
next if (reftext = $2) && (reftext.include? ATTR_REF_HEAD) && (reftext = document.sub_attributes reftext).empty?
|
1154
1155
|
else
|
1155
1156
|
id = $3
|
1156
1157
|
if (reftext = $4)
|
1157
|
-
|
1158
|
-
|
1158
|
+
if reftext.include? ']'
|
1159
|
+
reftext = reftext.gsub '\]', ']'
|
1160
|
+
reftext = document.sub_attributes reftext if reftext.include? ATTR_REF_HEAD
|
1161
|
+
elsif (reftext.include? ATTR_REF_HEAD) && (reftext = document.sub_attributes reftext).empty?
|
1162
|
+
next
|
1163
|
+
end
|
1159
1164
|
end
|
1160
1165
|
end
|
1161
1166
|
unless document.register :refs, [id, (Inline.new block, :anchor, reftext, type: :ref, id: id)]
|
@@ -2119,6 +2124,8 @@ class Parser
|
|
2119
2124
|
name = 'sectnums'
|
2120
2125
|
elsif name == 'hardbreaks'
|
2121
2126
|
name = 'hardbreaks-option'
|
2127
|
+
elsif name == 'showtitle'
|
2128
|
+
store_attribute 'notitle', (value ? nil : ''), doc, attrs
|
2122
2129
|
end
|
2123
2130
|
|
2124
2131
|
if doc
|
@@ -2273,9 +2280,14 @@ class Parser
|
|
2273
2280
|
end
|
2274
2281
|
|
2275
2282
|
skipped = table_reader.skip_blank_lines || 0
|
2283
|
+
if attributes['header-option']
|
2284
|
+
table.has_header_option = true
|
2285
|
+
elsif skipped == 0 && !attributes['noheader-option']
|
2286
|
+
# NOTE: assume table has header until we know otherwise; if it doesn't (nil), cells in first row get reprocessed
|
2287
|
+
table.has_header_option = implicit_header = true
|
2288
|
+
end
|
2276
2289
|
parser_ctx = Table::ParserContext.new table_reader, table, attributes
|
2277
2290
|
format, loop_idx, implicit_header_boundary = parser_ctx.format, -1, nil
|
2278
|
-
implicit_header = true unless skipped > 0 || attributes['header-option'] || attributes['noheader-option']
|
2279
2291
|
|
2280
2292
|
while (line = table_reader.read_line)
|
2281
2293
|
if (beyond_first = (loop_idx += 1) > 0) && line.empty?
|
@@ -2295,7 +2307,7 @@ class Parser
|
|
2295
2307
|
implicit_header_boundary = nil if implicit_header_boundary
|
2296
2308
|
# otherwise, the cell continues from previous line
|
2297
2309
|
elsif implicit_header_boundary && implicit_header_boundary == loop_idx
|
2298
|
-
implicit_header
|
2310
|
+
table.has_header_option = implicit_header = implicit_header_boundary = nil
|
2299
2311
|
end
|
2300
2312
|
end
|
2301
2313
|
end
|
@@ -2307,7 +2319,7 @@ class Parser
|
|
2307
2319
|
if table_reader.has_more_lines? && table_reader.peek_line.empty?
|
2308
2320
|
implicit_header_boundary = 1
|
2309
2321
|
else
|
2310
|
-
implicit_header =
|
2322
|
+
table.has_header_option = implicit_header = nil
|
2311
2323
|
end
|
2312
2324
|
end
|
2313
2325
|
end
|
@@ -2358,7 +2370,7 @@ class Parser
|
|
2358
2370
|
case format
|
2359
2371
|
when 'csv'
|
2360
2372
|
if parser_ctx.buffer_has_unclosed_quotes?
|
2361
|
-
implicit_header
|
2373
|
+
table.has_header_option = implicit_header = implicit_header_boundary = nil if implicit_header_boundary && loop_idx == 0
|
2362
2374
|
parser_ctx.keep_cell_open
|
2363
2375
|
else
|
2364
2376
|
parser_ctx.close_cell true
|
@@ -2380,15 +2392,8 @@ class Parser
|
|
2380
2392
|
end
|
2381
2393
|
end
|
2382
2394
|
|
2383
|
-
unless (table.attributes['colcount'] ||= table.columns.size) == 0 || explicit_colspecs
|
2384
|
-
|
2385
|
-
end
|
2386
|
-
|
2387
|
-
if implicit_header
|
2388
|
-
table.has_header_option = true
|
2389
|
-
attributes['header-option'] = ''
|
2390
|
-
end
|
2391
|
-
|
2395
|
+
table.assign_column_widths unless (table.attributes['colcount'] ||= table.columns.size) == 0 || explicit_colspecs
|
2396
|
+
attributes['header-option'] = '' if implicit_header
|
2392
2397
|
table.partition_header_footer attributes
|
2393
2398
|
|
2394
2399
|
table
|
@@ -2579,9 +2584,7 @@ class Parser
|
|
2579
2584
|
attributes['role'] = (existing_role = attributes['role']).nil_or_empty? ? (parsed_attrs[:role].join ' ') : %(#{existing_role} #{parsed_attrs[:role].join ' '})
|
2580
2585
|
end
|
2581
2586
|
|
2582
|
-
if parsed_attrs.key? :option
|
2583
|
-
(opts = parsed_attrs[:option]).each {|opt| attributes[%(#{opt}-option)] = '' }
|
2584
|
-
end
|
2587
|
+
parsed_attrs[:option].each {|opt| attributes[%(#{opt}-option)] = '' } if parsed_attrs.key? :option
|
2585
2588
|
|
2586
2589
|
parsed_style
|
2587
2590
|
else
|
@@ -331,11 +331,12 @@ class PathResolver
|
|
331
331
|
|
332
332
|
# Public: Securely resolve a system path
|
333
333
|
#
|
334
|
-
#
|
335
|
-
#
|
336
|
-
# that
|
337
|
-
# path
|
338
|
-
#
|
334
|
+
# Resolves the target to an absolute path on the current filesystem. The target is assumed to be
|
335
|
+
# relative to the start path, jail path, or working directory (specified in the constructor), in
|
336
|
+
# that order. If a jail path is specified, the resolved path is forced to descend from the jail
|
337
|
+
# path. If a jail path is not provided, the resolved path may be any location on the system. If
|
338
|
+
# the target is an absolute path, use it as is (unless it breaches the jail path). Expands all
|
339
|
+
# parent and self references in the resolved path.
|
339
340
|
#
|
340
341
|
# target - the String target path
|
341
342
|
# start - the String start path from which to resolve a relative target; falls back to jail, if
|
@@ -347,8 +348,9 @@ class PathResolver
|
|
347
348
|
# automatically recover when an illegal path is encountered
|
348
349
|
# * :target_name is used in messages to refer to the path being resolved
|
349
350
|
#
|
350
|
-
#
|
351
|
-
# if specified. The path is posixified and all parent and self references in the path
|
351
|
+
# Returns an absolute String path relative to the start path, if specified, and confined to the
|
352
|
+
# jail path, if specified. The path is posixified and all parent and self references in the path
|
353
|
+
# are expanded.
|
352
354
|
def system_path target, start = nil, jail = nil, opts = {}
|
353
355
|
if jail
|
354
356
|
raise ::SecurityError, %(Jail is not an absolute path: #{jail}) unless root? jail
|
@@ -362,7 +364,7 @@ class PathResolver
|
|
362
364
|
if jail && !(descends_from? target_path, jail)
|
363
365
|
if opts.fetch :recover, true
|
364
366
|
logger.warn %(#{opts[:target_name] || 'path'} is outside of jail; recovering automatically)
|
365
|
-
target_segments,
|
367
|
+
target_segments, = partition_path target_path
|
366
368
|
jail_segments, jail_root = partition_path jail
|
367
369
|
return join_path jail_segments + target_segments, jail_root
|
368
370
|
else
|
@@ -371,7 +373,7 @@ class PathResolver
|
|
371
373
|
end
|
372
374
|
return target_path
|
373
375
|
else
|
374
|
-
target_segments,
|
376
|
+
target_segments, = partition_path target
|
375
377
|
end
|
376
378
|
else
|
377
379
|
target_segments = []
|
@@ -387,7 +389,7 @@ class PathResolver
|
|
387
389
|
return expand_path start
|
388
390
|
end
|
389
391
|
else
|
390
|
-
target_segments,
|
392
|
+
target_segments, = partition_path start
|
391
393
|
start = jail || @working_dir
|
392
394
|
end
|
393
395
|
elsif start.nil_or_empty?
|
@@ -419,7 +421,7 @@ class PathResolver
|
|
419
421
|
if (resolved_segments = start_segments + target_segments).include? DOT_DOT
|
420
422
|
unresolved_segments, resolved_segments = resolved_segments, []
|
421
423
|
if jail
|
422
|
-
jail_segments,
|
424
|
+
jail_segments, = partition_path jail unless jail_segments
|
423
425
|
warned = false
|
424
426
|
unresolved_segments.each do |segment|
|
425
427
|
if segment == DOT_DOT
|
@@ -450,7 +452,7 @@ class PathResolver
|
|
450
452
|
target_path
|
451
453
|
elsif opts.fetch :recover, true
|
452
454
|
logger.warn %(#{opts[:target_name] || 'path'} is outside of jail; recovering automatically)
|
453
|
-
jail_segments,
|
455
|
+
jail_segments, = partition_path jail unless jail_segments
|
454
456
|
join_path jail_segments + target_segments, jail_root
|
455
457
|
else
|
456
458
|
raise ::SecurityError, %(#{opts[:target_name] || 'path'} #{target} is outside of jail: #{jail} (disallowed in safe mode))
|