def preprocess_include_directive raw_target, raw_attributes
if ((target = raw_target).include? ATTR_REF_HEAD) &&
(target = @document.sub_attributes raw_target, :attribute_missing => 'drop-line').empty?
shift
if @document.attributes.fetch('attribute-missing', Compliance.attribute_missing) == 'skip'
unshift %(Unresolved directive in #{@path} - include::#{raw_target}[#{raw_attributes}])
end
true
elsif include_processors? && (ext = @include_processor_extensions.find {|candidate| candidate.instance.handles? target })
shift
ext.process_method[@document, self, target, AttributeList.new(raw_attributes).parse]
true
elsif @document.safe >= SafeMode::SECURE
replace_next_line %(link:#{target}[])
true
elsif (abs_maxdepth = @maxdepth[:abs]) > 0
if @include_stack.size >= abs_maxdepth
warn %(asciidoctor: ERROR: #{line_info}: maximum include depth of #{@maxdepth[:rel]} exceeded)
return false
end
if ::RUBY_ENGINE_OPAL && ::JAVASCRIPT_IO_MODULE == 'xmlhttprequest'
target_type = :file
inc_path = relpath = @include_stack.empty? && ::Dir.pwd == @document.base_dir ? target : (::File.join @dir, target)
elsif Helpers.uriish? target
unless @document.attributes.key? 'allow-uri-read'
replace_next_line %(link:#{target}[])
return true
end
target_type = :uri
inc_path = relpath = target
if @document.attributes.key? 'cache-uri'
Helpers.require_library 'open-uri/cached', 'open-uri-cached' unless defined? ::OpenURI::Cache
elsif !::RUBY_ENGINE_OPAL
::OpenURI
end
else
target_type = :file
inc_path = @document.normalize_system_path target, @dir, nil, :target_name => 'include file'
unless ::File.file? inc_path
warn %(asciidoctor: WARNING: #{line_info}: include file not found: #{inc_path})
replace_next_line %(Unresolved directive in #{@path} - include::#{target}[#{raw_attributes}])
return true
end
relpath = (@path_resolver ||= PathResolver.new).relative_path inc_path, @document.base_dir
end
inc_linenos, inc_tags, attributes = nil, nil, {}
unless raw_attributes.empty?
attributes = AttributeList.new(raw_attributes).parse
if attributes.key? 'lines'
inc_linenos = []
attributes['lines'].split(DataDelimiterRx).each do |linedef|
if linedef.include?('..')
from, to = linedef.split('..', 2).map {|it| it.to_i }
if to == -1
inc_linenos << from
inc_linenos << 1.0/0.0
else
inc_linenos.concat ::Range.new(from, to).to_a
end
else
inc_linenos << linedef.to_i
end
end
inc_linenos = inc_linenos.empty? ? nil : inc_linenos.sort.uniq
elsif attributes.key? 'tag'
unless (tag = attributes['tag']).empty?
if tag.start_with? '!'
inc_tags = { (tag.slice 1, tag.length) => false } unless tag == '!'
else
inc_tags = { tag => true }
end
end
elsif attributes.key? 'tags'
inc_tags = {}
attributes['tags'].split(DataDelimiterRx).each do |tagdef|
if tagdef.start_with? '!'
inc_tags[tagdef.slice 1, tagdef.length] = false unless tagdef == '!'
else
inc_tags[tagdef] = true
end unless tagdef.empty?
end
inc_tags = nil if inc_tags.empty?
end
end
if inc_linenos
inc_lines, inc_offset, inc_lineno = [], nil, 0
begin
open(inc_path, 'r') do |f|
f.each_line do |l|
inc_lineno += 1
select = inc_linenos[0]
if ::Float === select && select.infinite?
inc_offset ||= inc_lineno
inc_lines << l
else
if inc_lineno == select
inc_offset ||= inc_lineno
inc_lines << l
inc_linenos.shift
end
break if inc_linenos.empty?
end
end
end
rescue
warn %(asciidoctor: WARNING: #{line_info}: include #{target_type} not readable: #{inc_path})
replace_next_line %(Unresolved directive in #{@path} - include::#{target}[#{raw_attributes}])
return true
end
shift
push_include inc_lines, inc_path, relpath, inc_offset, attributes if inc_offset
elsif inc_tags
inc_lines, inc_offset, inc_lineno, tag_stack, tags_used, active_tag = [], nil, 0, [], ::Set.new, nil
if inc_tags.key? '**'
if inc_tags.key? '*'
select = base_select = (inc_tags.delete '**')
wildcard = inc_tags.delete '*'
else
select = base_select = wildcard = (inc_tags.delete '**')
end
else
select = base_select = !(inc_tags.value? true)
wildcard = inc_tags.delete '*'
end
if (ext_idx = inc_path.rindex '.') && (circ_cmt = CIRCUMFIX_COMMENTS[inc_path.slice ext_idx, inc_path.length])
cmt_suffix_len = (tag_suffix = %([] #{circ_cmt[:suffix]})).length - 2
end
begin
open(inc_path, 'r') do |f|
f.each_line do |l|
inc_lineno += 1
l.force_encoding ::Encoding::UTF_8 if FORCE_ENCODING
if (((tl = l.chomp).end_with? '[]') ||
(tag_suffix && (tl.end_with? tag_suffix) && (tl = tl.slice 0, tl.length - cmt_suffix_len))) &&
TagDirectiveRx =~ tl
if $1
if (this_tag = $2) == active_tag
tag_stack.pop
active_tag, select = tag_stack.empty? ? [nil, base_select] : tag_stack[-1]
elsif inc_tags.key? this_tag
if (idx = tag_stack.rindex {|key, _| key == this_tag })
idx == 0 ? tag_stack.shift : (tag_stack.delete_at idx)
warn %(asciidoctor: WARNING: #{target}: line #{inc_lineno}: mismatched end tag in include: expected #{active_tag}, found #{this_tag})
else
warn %(asciidoctor: WARNING: #{target}: line #{inc_lineno}: unexpected end tag in include: #{this_tag})
end
end
elsif inc_tags.key?(this_tag = $2)
tags_used << this_tag
tag_stack << [(active_tag = this_tag), (select = inc_tags[this_tag])]
elsif !wildcard.nil?
select = active_tag && !select ? false : wildcard
tag_stack << [(active_tag = this_tag), select]
end
elsif select
inc_offset ||= inc_lineno
inc_lines << l
end
end
end
rescue
warn %(asciidoctor: WARNING: #{line_info}: include #{target_type} not readable: #{inc_path})
replace_next_line %(Unresolved directive in #{@path} - include::#{target}[#{raw_attributes}])
return true
end
unless (missing_tags = inc_tags.keys.to_a - tags_used.to_a).empty?
warn %(asciidoctor: WARNING: #{line_info}: tag#{missing_tags.size > 1 ? 's' : nil} '#{missing_tags * ','}' not found in include #{target_type}: #{inc_path})
end
shift
push_include inc_lines, inc_path, relpath, inc_offset, attributes if inc_offset
else
begin
inc_content = target_type == :file ? (::IO.read inc_path) : open(inc_path, 'r') {|f| f.read }
shift
push_include inc_content, inc_path, relpath, 1, attributes
rescue
warn %(asciidoctor: WARNING: #{line_info}: include #{target_type} not readable: #{inc_path})
replace_next_line %(Unresolved directive in #{@path} - include::#{target}[#{raw_attributes}])
return true
end
end
true
else
false
end
end