asciidoctor 2.0.10 → 2.0.11
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.
- 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))
|