def xml_from_sections(input)
def xml_from_sections(input)
unless ENV["KRAMDOWN_NO_SOURCE"]
require 'kramdown-rfc/gzip-clone'
require 'base64'
compressed_input = Gzip.compress_m0(input)
$source = Base64.encode64(compressed_input)
end
sections = input.scan(RE_SECTION)
# resulting in an array; each section is [section-label, nomarkdown-flag, section-text]
line = 1 # skip "---"
sections.each do |section|
section << line
line += 1 + section[2].lines.count
end
# warn "#{line-1} lines"
# the first section is a YAML with front matter parameters (don't put a label here)
# We put back the "---" plus gratuitous blank lines to hack the line number in errors
yaml_in = input[/---\s*/] << sections.shift[2]
ps = KramdownRFC::ParameterSet.new(yaml_load(yaml_in, [Date], [], true))
if v = ps[:v]
warn "*** unsupported RFCXML version #{v}" if v != 3
if $options.v2
warn "*** command line --v2 wins over document's 'v: #{v}'"
else
$options.v3 = true
$options.v = 3
ps.default!(:stand_alone, true)
ps.default!(:ipr, "trust200902")
ps.default!(:pi, {"toc" => true, "sortrefs" => true, "symrefs" => true})
end
end
if r = ENV["KRAMDOWN_RFC_DOCREV"]
warn "** building document revision -#{r}"
unless n = ps.has(:docname) and n.sub!(/-latest\z/, "-#{r}")
warn "** -d#{r}: docname #{n.inspect} doesn't have a '-latest' suffix"
end
end
if o = ps[:'autolink-iref-cleanup']
$options.autolink_iref_cleanup = o
end
if o = ps[:'svg-id-cleanup']
$options.svg_id_cleanup = o
end
coding_override = ps.has(:coding)
smart_quotes = ps[:smart_quotes] || ps[:"smart-quotes"]
typographic_symbols = ps[:typographic_symbols]
header_kramdown_options = ps[:kramdown_options]
kramdown_options = process_kramdown_options(coding_override,
smart_quotes, typographic_symbols,
header_kramdown_options)
# all the other sections are put in a Hash, possibly concatenated from parts there
sechash = Hash.new{ |h,k| h[k] = ""}
snames = [] # a stack of section names
sections.each do |sname, nmdflag, text, line|
# warn [:SNAME, sname, nmdflag, text[0..10]].inspect
nmdin, nmdout = {
"-" => ["", ""], # stay in nomarkdown
"" => NMDTAGS, # pop out temporarily
}[nmdflag || ""]
if sname
snames << sname # "--- label" -> push label (now current)
else
snames.pop # just "---" -> pop label (previous now current)
end
sechash[snames.last] << "#{nmdin}<?line #{line}?>\n#{text}#{nmdout}"
end
ref_replacements = { }
anchor_to_bibref = { }
displayref = {}
[:ref, :normative, :informative].each do |sn|
if refs = ps.has(sn)
warn "*** bad section #{sn}: #{refs.inspect}" unless refs.respond_to? :each
refs.each do |k, v|
if v.respond_to? :to_str
if bibtagsys(v) # enable "foo: RFC4711" as a custom anchor definition
anchor_to_bibref[k] = v.to_str
end
ref_replacements[v.to_str] = k
end
if Hash === v
if aliasname = v.delete("-")
ref_replacements[aliasname] = k
end
if bibref = v.delete("=")
anchor_to_bibref[k] = bibref
end
if dr = v.delete("display")
displayref[k.gsub("/", "_")] = dr
end
end
end
end
end
open_refs = ps[:ref] || { } # consumed
norm_ref = { }
# convenience replacement of {{-coap}} with {{I-D.ietf-core-coap}}
# collect normative/informative tagging {{!RFC2119}} {{?RFC4711}}
sechash.each do |k, v|
next if k == "fluff"
v.gsub!(/{{(#{
spacify_re(XSR_PREFIX)
})?([\w.\/_\-]+@)?(?:([?!])(-)?|(-))([\w._\-]+)(?:=([\w.\/_\-]+))?(#{
XREF_TXT_SUFFIX
})?(#{
spacify_re(XSR_SUFFIX)
})?}}/) do |match|
xsr_prefix = $1
subref = $2
norminform = $3
replacing = $4 || $5
word = $6
bibref = $7
xrt_suffix = $8
xsr_suffix = $9
if replacing
if new = ref_replacements[word]
word = new
else
warn "*** no alias replacement for {{-#{word}}}"
word = "-#{word}"
end
end # now, word is the anchor
if bibref
if old = anchor_to_bibref[word]
if bibref != old
warn "*** conflicting definitions for xref #{word}: #{old} != #{bibref}"
end
else
anchor_to_bibref[word] = bibref
end
end
# things can be normative in one place and informative in another -> normative
# collect norm/inform above and assign it by priority here
if norminform
norm_ref[word] ||= norminform == '!' # one normative ref is enough
end
"{{#{xsr_prefix}#{subref}#{word}#{xrt_suffix}#{xsr_suffix}}}"
end
end
[:normative, :informative].each do |k|
ps.rest[k.to_s] ||= { }
end
norm_ref.each do |k, v|
# could check bibtagsys here: needed if open_refs is nil or string
target = ps.has(v ? :normative : :informative)
warn "*** overwriting #{k}" if target.has_key?(k)
target[k] = open_refs[k] # add reference to normative/informative
end
# note that unused items from ref are considered OK, therefore no check for that here
# also should allow norm/inform check of other references
# {{?coap}} vs. {{!coap}} vs. {{-coap}} (undecided)
# or {{?-coap}} vs. {{!-coap}} vs. {{-coap}} (undecided)
# could require all references to be decided by a global flag
overlap = [:normative, :informative].map { |s| (ps.has(s) || { }).keys }.reduce(:&)
unless overlap.empty?
warn "*** #{overlap.join(', ')}: both normative and informative"
end
stand_alone = ps[:stand_alone]
[:normative, :informative].each do |sn|
if refs = ps[sn]
refs.each do |k, v|
href = ::Kramdown::Parser::RFC2629Kramdown.idref_cleanup(k)
kramdown_options[:link_defs][k] = ["##{href}", nil] # allow [RFC2119] in addition to {{RFC2119}}
bibref = anchor_to_bibref[k] || k
bts, url = bibtagsys(bibref, k, stand_alone)
ann = v.delete("annotation") || v.delete("ann") if Hash === v
if bts && (!v || v == {} || v.respond_to?(:to_str))
if stand_alone
a = %{{: anchor="#{k}"}}
a[-1...-1] = %{ ann="#{escape_html(ann, :attribute)}"} if ann
sechash[sn.to_s] << %{\n#{NMDTAGS[0]}\n#{a}\n#{NMDTAGS[1]}\n}
else
warn "*** please use standalone mode for adding annotations to references" if ann
bts.gsub!('/', '_')
(ps.rest["bibxml"] ||= []) << [bts, url]
sechash[sn.to_s] << %{&#{bts};\n} # ???
end
else
unless v && Hash === v
warn "*** don't know how to expand ref #{k}"
next
end
if bts && !v.delete("override")
warn "*** warning: explicit settings completely override canned bibxml in reference #{k}"
end
v["ann"] = ann if ann
sechash[sn.to_s] << KramdownRFC::ref_to_xml(href, v)
end
end
end
end
erbfilename = File.expand_path '../../../data/kramdown-rfc2629.erb', __FILE__
erbfile = File.read(erbfilename, coding: "UTF-8")
erb = ERB.trim_new(erbfile, '-')
# remove redundant nomarkdown pop outs/pop ins as they confuse kramdown
input = erb.result(binding).gsub(%r"{::nomarkdown}\s*{:/nomarkdown}"m, "")
ps.warn_if_leftovers
sechash.delete("fluff") # fluff is a "commented out" section
if !sechash.empty? # any sections unused by the ERb file?
warn "*** sections left #{sechash.keys.inspect}!"
end
[input, kramdown_options, coding_override]
end