Top Level Namespace

Includes:
Kramdown::Utils::Html

Defined Under Namespace

Modules: Gzip, Kramdown, KramdownRFC, REXML Classes: ERB, Object

Constant Summary collapse

ACCEPT_CITE_JSON =
{"Accept" => "application/citeproc+json"}
KDRFC_VERSION =

try to get this from gemspec.

Gem.loaded_specs["kramdown-rfc2629"].version rescue "unknown-version"
RE_NL =
/(?:\r\n|\n|\r)/
RE_SECTION =
/---(?: +(\w+)(-?))? *#{RE_NL}(.*?#{RE_NL})(?=---(?:\s+\w+-?)?\s*#{RE_NL}|\Z)/m
NMDTAGS =
["{:/nomarkdown}\n\n", "\n\n{::nomarkdown}\n"]
NORMINFORM =
{ "!" => :normative, "?" => :informative }
XREF_SECTIONS_RE =
::Kramdown::Parser::RFC2629Kramdown::SECTIONS_RE
XSR_PREFIX =
"#{XREF_SECTIONS_RE} of "
XSR_SUFFIX =
", (#{XREF_SECTIONS_RE})| \\((#{XREF_SECTIONS_RE})\\)"
XREF_TXT =
::Kramdown::Parser::RFC2629Kramdown::XREF_TXT
XREF_TXT_SUFFIX =
" \\(#{XREF_TXT}\\)"
XML_RESOURCE_ORG_PREFIX =
Kramdown::Converter::Rfc2629::XML_RESOURCE_ORG_PREFIX
FALLBACK =
read_encodings
FOLD_MSG =
"NOTE: '\\' line wrapping per RFC 8792".freeze
UNFOLD_RE =
/\A.*#{FOLD_MSG.sub("\\", "(\\\\\\\\\\\\\\\\?)")}.*\n\r?\n/
MIN_FOLD_COLUMNS =
FOLD_MSG.size
FOLD_COLUMNS =
69
RE_IDENT =
/\A[A-Za-z0-9_]\z/
SVG_NAMESPACES =
{"svg"=>"http://www.w3.org/2000/svg",
"xlink"=>"http://www.w3.org/1999/xlink"}

Instance Method Summary collapse

Instance Method Details

#add_quote(s) ⇒ Object



16
17
18
19
# File 'lib/kramdown-rfc/command.rb', line 16

def add_quote(s)
  l = s.lines
  l.map {|li| "> #{li}"}.join
end


3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# File 'lib/kramdown-rfc/autolink-iref-cleanup.rb', line 3

def autolink_iref_cleanup(d)

  d.root.get_elements("//section[@anchor]").each do |sec|
    anchor = sec['anchor']
    irefs = {}
    sec.get_elements(".//xref[@target='#{anchor}'][@format='none']").each do |xr|
      ne = xr.previous_element  # 9c87e84 iref now before xref
      if ne && ne.name == "iref" && (item = ne['item'])
        irefs[item] = ne['subitem'] # XXX one subitem only
        ne.remove
        chi = xr.children
        chi[1..-1].reverse.each do |ch|
          xr.parent.insert_after(xr, ch)
        end
        xr.replace_with(chi[0])
      end
    end
    irefs.each do |k, v|
      sec.insert_after(sec.get_elements("name").first, 
                       e = REXML::Element.new("iref", sec))
      e.attributes["item"] = k
      e.attributes["subitem"] = v
      e.attributes["primary"] = 'true'
    end
  end

end

#bibtagsys(bib, anchor = nil, stand_alone = true) ⇒ Object

return XML entity name, url, rewrite_anchor flag



493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
# File 'lib/kramdown-rfc/command.rb', line 493

def bibtagsys(bib, anchor=nil, stand_alone=true)
  if bib =~ /\Arfc(\d+)/i
    rfc4d = "%04d" % $1.to_i
    [bib.upcase,
     "#{XML_RESOURCE_ORG_PREFIX}/bibxml/reference.RFC.#{rfc4d}.xml"]
  elsif $options.v3 && bib =~ /\A(bcp|std)(\d+)/i
    n4d = "%04d" % $2.to_i
    [bib.upcase,
     "#{XML_RESOURCE_ORG_PREFIX}/bibxml-rfcsubseries-new/reference.#{$1.upcase}.#{n4d}.xml"]
  elsif bib =~ /\A([-A-Z0-9]+)\./ &&
        (xro = Kramdown::Converter::Rfc2629::XML_RESOURCE_ORG_MAP[$1])
    dir, _ttl, rewrite_anchor = xro
    bib1 = ::Kramdown::Parser::RFC2629Kramdown.idref_cleanup(bib)
    if anchor && bib1 != anchor
      if rewrite_anchor
        a = %{?anchor=#{anchor}}
      else
        if !stand_alone
          warn "*** selecting a custom anchor '#{anchor}' for '#{bib1}' requires stand_alone mode"
          warn "    the output will need manual editing to correct this"
        end
      end
    end
    [bib1,
     "#{XML_RESOURCE_ORG_PREFIX}/#{dir}/reference.#{bib}.xml#{a}"]
  end
end

#boilerplate(key) ⇒ Object



70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
# File 'lib/kramdown-rfc/command.rb', line 70

def boilerplate(key)
  ret = ''
  case key.downcase
  when /\Abcp14(info)?(\+)?(-tagged)?(-bcp)?\z/i
    #            $1    $2     $3       $4
    if $1
      ret << <<RFC8174ise
Although this document is not an IETF Standards Track publication, it
adopts the conventions for normative language to provide clarity of
instructions to the implementer.
RFC8174ise
    end
    ret << <<RFC8174
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL
NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED",
"MAY", and "OPTIONAL" in this document are to be interpreted as
described in BCP 14 {{!RFC2119}} {{!RFC8174}} when, and only when, they
appear in all capitals, as shown here.
RFC8174
    if $2
      ret << <<PLUS
These words may also appear in this document in
lower case as plain English words, absent their normative meanings.
PLUS
    end
    if $3
      ($options.v3_used ||= []) << "** need --v3 to tag bcp14"
      ret << <<TAGGED

*[MUST]: <bcp14>
*[MUST NOT]: <bcp14>
*[REQUIRED]: <bcp14>
*[SHALL]: <bcp14>
*[SHALL NOT]: <bcp14>
*[SHOULD]: <bcp14>
*[SHOULD NOT]: <bcp14>
*[RECOMMENDED]: <bcp14>
*[NOT RECOMMENDED]: <bcp14>
*[MAY]: <bcp14>
*[OPTIONAL]: <bcp14>
TAGGED
    end
    if $4                       # experimental; idnits complains:
      #  ** The document seems to lack a both a reference to RFC 2119 and the
      #     recommended RFC 2119 boilerplate, even if it appears to use RFC 2119
      #     keywords -- however, there's a paragraph with a matching beginning.
      #     Boilerplate error?
      ret.sub!("{{!RFC2119}} {{!RFC8174}}", "{{!BCP14}}")
    end
    ret
  when /\Arfc\s*7942(info)?\z/i
    if $1
      ret << <<INFO
(Boilerplate as per {{Section 2.1 of RFC7942}}:)

INFO
    end
    ret << <<RFC7942
This section records the status of known implementations of the
protocol defined by this specification at the time of posting of
this Internet-Draft, and is based on a proposal described in
{{?RFC7942}}.  The description of implementations in this section is
intended to assist the IETF in its decision processes in
progressing drafts to RFCs.  Please note that the listing of any
individual implementation here does not imply endorsement by the
IETF.  Furthermore, no effort has been spent to verify the
information presented here that was supplied by IETF contributors.
This is not intended as, and must not be construed to be, a
catalog of available implementations or their features.  Readers
are advised to note that other implementations may exist.

According to {{?RFC7942}}, "this will allow reviewers and working
groups to assign due consideration to documents that have the
benefit of running code, which may serve as evidence of valuable
experimentation and feedback that have made the implemented
protocols more mature.  It is up to the individual working groups
to use this information as they see fit".
RFC7942
  else
    warn "** Unknown boilerplate key: #{key}"
    "{::boilerplate #{key}}"
  end
end

#do_the_tls_danceObject



154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
# File 'lib/kramdown-rfc/command.rb', line 154

def do_the_tls_dance
  begin
    require 'openssl'
    File.open(OpenSSL::X509::DEFAULT_CERT_FILE) do end
    # This guards against having an unreadable cert file (yes, that appears to happen a lot).
  rescue
    if Dir[File.join(OpenSSL::X509::DEFAULT_CERT_DIR, "*.pem")].empty?
      # This guards against having no certs at all, not against missing the right one for IETF.
      # Oh well.
      warn "** Configuration problem with OpenSSL certificate store."
      warn "**   You may want to examine #{OpenSSL::X509::DEFAULT_CERT_FILE}"
      warn "**    and #{OpenSSL::X509::DEFAULT_CERT_DIR}."
      warn "**   Activating suboptimal workaround."
      warn "**   Occasionally run `certified-update` to maintain that workaround."
      require 'certified'
    end
  end
end

#doi_citeproc_to_lit(cite, fuzzy) ⇒ Object



32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/kramdown-rfc/doi.rb', line 32

def doi_citeproc_to_lit(cite, fuzzy)
  lit = {}
  ser = lit["seriesinfo"] = {}
  refcontent = []
  lit["title"] = cite["title"]
  if (st = cite["subtitle"]) && Array === st # defensive
    st.delete('')
    if st != []
      lit["title"] << ": " << st.join("; ")
    end
  end
  if authors = cite["author"]
    lit["author"] = authors.map do |au|
      lau = {}
      if (f = au["family"])
        if (g = au["given"])
          lau["name"] = "#{g} #{f}"
          lau["ins"] =  "#{g[0]}. #{f}"
        else
          lau["name"] = "#{f}"
#          lau["ins"] =  "#{g[0]}. #{f}"
        end
      end
      if (f = au["affiliation"]) && Array === f
        names = f.map { |affn|
          if Hash === affn && (n = affn["name"]) && String === n
            n
          end
        }.compact
        if names.size > 0
          lau["org"] = names.join("; ")
        end
      end
      lau
    end
  end
  if iss = cite["issued"]
    if dp = iss["date-parts"]
      if Integer === (dp = dp[0])[0]
        lit["date"] = ["%04d" % dp[0], *dp[1..-1].map {|p| "%02d" % p}].join("-")
      end
    end
  end
  if !lit.key?("date") && fuzzy && (iss = cite["created"])
    if dp = iss["date-parts"]
      if Integer === (dp = dp[0])[0]
        lit["date"] = ["%04d" % dp[0], *dp[1..-1].map {|p| "%02d" % p}].join("-")
      end
    end
  end
  if (ct = cite["container-title"]) && ct != []
    info = []
    if v = cite["volume"]
      vi = "vol. #{v}"
      if (v = cite["journal-issue"]) && (issue = v["issue"])
        vi << ", no. #{issue}"
      end
      info << vi
    end
    if p = cite["page"]
      info << "pp. #{p}"
    end
    rhs = info.join(", ")
    if info != []
      ser[ct] = rhs
    else
      spl = ct.split(" ")
      ser[spl[0..-2].join(" ")] = spl[-1]
    end
  end
  if pub = cite["publisher"]
    refcontent << pub
    # info = []
    # if t = cite["type"]
    #   info << t
    # end
    # rhs = info.join(", ")
    # if info != []
    #   ser[pub] = rhs
    # else
    #   spl = pub.split(" ")
    #   ser[spl[0..-2].join(" ")] = spl[-1]
    # end
  end
  ["DOI", "ISBN"].each do |st|
    if a = cite[st]
      ser[st] = a
    end
  end
  if refcontent != []
    lit["refcontent"] = refcontent.join(", ")
  end
  lit
end

#doi_fetch_and_convert(doi, fuzzy: false, verbose: false, site: "https://dx.doi.org") ⇒ Object



7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# File 'lib/kramdown-rfc/doi.rb', line 7

def doi_fetch_and_convert(doi, fuzzy: false, verbose: false, site: "https://dx.doi.org")
  doipath = doi.sub(/^([0-9.]+)_/) {"#$1/"} # convert initial _ back to /
  # warn "** SUB #{doi} #{doipath}" if doi != doipath
  begin
    cite = JSON.parse(URI("#{site}/#{doipath}").open(ACCEPT_CITE_JSON).read)
    puts cite.to_yaml if verbose
    doi_citeproc_to_lit(cite, fuzzy)
  rescue OpenURI::HTTPError => e
    begin
      site = "https://dl.acm.org"
      percent_escaped = doipath.gsub("/", "%2F")
      path = "#{site}/action/exportCiteProcCitation?targetFile=custom-bibtex&format=bibTex&dois=#{percent_escaped}"
      op = URI(path).open       # first get a cookie, ignore result
      # warn [:META, op.meta].inspect
      cook = op.meta['set-cookie'].split('; ', 2)[0]
      cite = JSON.parse(URI(path).open("Cookie" => cook).read)
      cite = cite["items"].first[doipath]
      puts cite.to_yaml if verbose
      doi_citeproc_to_lit(cite, fuzzy)
    rescue
      raise e
    end
  end
end

#expand_tabs(s, tab_stops = 8) ⇒ Object



532
533
534
535
536
# File 'lib/kramdown-rfc/command.rb', line 532

def expand_tabs(s, tab_stops = 8)
  s.gsub(/([^\t\n]*)\t/) do
    $1 + " " * (tab_stops - ($1.size % tab_stops))
  end
end

#fix_unterminated_line(s) ⇒ Object



13
14
15
# File 'lib/kramdown-rfc/rfc8792.rb', line 13

def fix_unterminated_line(s)
  s.sub(/[^\n]\z/) { "#$&\n" } # XXX
end

#fold8792_1(s, columns = FOLD_COLUMNS, left = false, dry = false) ⇒ Object



47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/kramdown-rfc/rfc8792.rb', line 47

def fold8792_1(s, columns = FOLD_COLUMNS, left = false, dry = false)
  if s.index("\t")
    warn "*** HT (\"TAB\") in text to be folded. Giving up."
    return s
  end
  if columns < MIN_FOLD_COLUMNS
    columns =
      if columns == 0
        FOLD_COLUMNS
      else
        warn "*** folding to #{MIN_FOLD_COLUMNS}, not #{columns}"
        MIN_FOLD_COLUMNS
      end
  end

  lines = s.lines.map(&:chomp)
  did_fold = false
  ix = 0
  while li = lines[ix]
    col = columns
    if li[col].nil?
      if li[-1] == "\\"
        lines[ix..ix] = [li << "\\", ""]
        ix += 1
      end
      ix += 1
    else
      did_fold = true
      min_indent = left || 0
      col -= 1                  # space for "\\"
      while li[col] == " "      # can't start new line with " "
        col -= 1
      end
      if col <= min_indent
        warn "*** Cannot RFC8792-fold1 to #{columns} cols #{"with indent #{left}" if left}  |#{li.inspect}|"
      else
        if RE_IDENT === li[col] # Don't split IDs
          col2 = col
          while col2 > min_indent && RE_IDENT === li[col2-1]
            col2 -= 1
          end
          if col2 > min_indent
            col = col2
          end
        end
        rest = li[col..-1]
        indent = left || columns - rest.size
        if !left && li[-1] == "\\"
          indent -= 1           # leave space for next round
        end
        if indent > 0
         rest = " " * indent + rest
        end
        lines[ix..ix] = [li[0...col] << "\\", rest]
      end
      ix += 1
    end
  end

  if did_fold
    msg = FOLD_MSG.dup
    if !dry && columns >= msg.size + 4
      delta = columns - msg.size - 2 # 2 spaces
      half = delta/2
      msg = "#{"=" * half} #{msg} #{"=" * (delta - half)}"
    end
    lines[0...0] = [msg, ""]
    lines.map{|x| x << "\n"}.join
  else
    s
  end
end

#handle_artwork_sourcecode(s, unfold = true) ⇒ Object



17
18
19
20
21
# File 'lib/kramdown-rfc/rfc8792.rb', line 17

def handle_artwork_sourcecode(s, unfold = true)
  s = trim_empty_lines_around(s)
  s = unfold8792(s) if unfold
  fix_unterminated_line(s)
end

#process_chunk(s, nested, dedent, fold, quote) ⇒ Object



21
22
23
24
25
26
27
# File 'lib/kramdown-rfc/command.rb', line 21

def process_chunk(s, nested, dedent, fold, quote)
  process_includes(s) if nested
  s = remove_indentation(s) if dedent
  s = fold8792_1(s, *fold) if fold
  s = add_quote(s) if quote
  s
end

#process_includes(input) ⇒ Object



29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/kramdown-rfc/command.rb', line 29

def process_includes(input)
 input.gsub!(/^\{::include((?:-[a-z0-9]+)*)\s+(.*?)\}/) {
  include_flags = $1
  fn = [$2]
  chunks = false
  nested = false
  dedent = false
  fold = false
  quote = false
  include_flags.split("-") do |flag|
    case flag
    when ""
    when "nested"
      nested = true
    when "quote"
      quote = true
    when "dedent"
      dedent = true
    when /\Afold(\d*)(left(\d*))?(dry)?\z/
      fold = [$1.to_i,            # col 0 for ''
              ($3.to_i if $2),    # left 0 for '', nil if no "left"
              $4]                 # dry
    when "all", "last"
      fn = fn.flat_map{|n| Dir[n]}
      fn = [fn.last] if flag == "last"
      chunks = fn.map{ |f|
        ret = process_chunk(File.read(f), nested, dedent, fold, quote)
        nested = false; dedent = false; fold = false; quote = false
        ret
      }
    else
      warn "** unknown include flag #{flag}"
    end
  end
  chunks = fn.map{|f| File.read(f)} unless chunks # no all/last
  chunks = chunks.map {|ch| process_chunk(ch, nested, dedent, fold, quote)}
  chunks.join.chomp
 }
end

#process_kramdown_options(coding_override = nil, smart_quotes = nil, typographic_symbols = nil, header_kramdown_options = nil) ⇒ Object



197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
# File 'lib/kramdown-rfc/command.rb', line 197

def process_kramdown_options(coding_override = nil,
                             smart_quotes = nil, typographic_symbols = nil,
                             header_kramdown_options = nil)

  ascii_target = coding_override && coding_override =~ /ascii/
  suppress_typography = ascii_target || $options.v3
  entity_output = ascii_target ? :numeric : :as_char;

  options = {input: 'RFC2629Kramdown', entity_output: entity_output, link_defs: {}}

  if smart_quotes.nil? && suppress_typography
    smart_quotes = false
  end
  if smart_quotes == false
    smart_quotes = ["'".ord, "'".ord, '"'.ord, '"'.ord]
  end
  case smart_quotes
  when Array
    options[:smart_quotes] = smart_quotes
  when nil, true
    # nothin
  else
    warn "*** Can't deal with smart_quotes value #{smart_quotes.inspect}"
  end

  if typographic_symbols.nil? && suppress_typography
    typographic_symbols = false
  end
  if typographic_symbols == false
    typographic_symbols = Hash[::Kramdown::Parser::Kramdown::TYPOGRAPHIC_SYMS.map { |k, v|
                                 if Symbol === v
                                   [v.intern, k]
                                 end
                               }.compact]
  end
  # warn [:TYPOGRAPHIC_SYMBOLS, typographic_symbols].to_yaml
  case typographic_symbols
  when Hash
    options[:typographic_symbols] = typographic_symbols
  when nil, true
    # nothin
  else
    warn "*** Can't deal with typographic_symbols value #{typographic_symbols.inspect}"
  end

  if header_kramdown_options
    options.merge! header_kramdown_options
  end

  $global_markdown_options = options   # For nested calls in bibref annotation processing and xref text

  options
end

#read_encodingsObject



521
522
523
524
525
526
527
528
# File 'lib/kramdown-rfc/command.rb', line 521

def read_encodings
  encfilename = File.expand_path '../../../data/encoding-fallbacks.txt', __FILE__
  encfile = File.read(encfilename, coding: "UTF-8")
  Hash[encfile.lines.map{|l|
         l.chomp!;
         x, s = l.split(" ", 2)
         [x.hex.chr(Encoding::UTF_8), s || " "]}]
end

#remove_indentation(s) ⇒ Object

Note that this doesn’t attempt to handle HT characters



3
4
5
6
7
# File 'lib/kramdown-rfc/rfc8792.rb', line 3

def remove_indentation(s)
  l = s.lines
  indent = l.grep(/\S/).map {|l| l[/^\s*/].size}.min
  l.map {|li| li.sub(/^ {0,#{indent}}/, "")}.join
end

#spacify_re(s) ⇒ Object



257
258
259
# File 'lib/kramdown-rfc/command.rb', line 257

def spacify_re(s)
  s.gsub(' ', '[\u00A0\s]+')
end

#svg_id_cleanup(d) ⇒ Object



6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/kramdown-rfc/svg-id-cleanup.rb', line 6

def svg_id_cleanup(d)
  gensym = "gensym000"

  REXML::XPath.each(d.root, "//svg:svg", SVG_NAMESPACES) do |x|
    gensym = gensym.succ
    # warn "*** SVG"
    # warn "*** SVG: #{x.to_s.size}"
    found_as_id = Set[]
    found_as_href = Set[]
    REXML::XPath.each(x, ".//*[@id]", SVG_NAMESPACES) do |y|
      # warn "*** ID: #{y}"
      name = y.attributes["id"]
      if found_as_id === name
        warn "*** duplicate ID #{name}"
      end
      found_as_id.add(name)
      y.attributes["id"] = "#{name}-#{gensym}"
    end
    REXML::XPath.each(x, ".//*[@xlink:href]", SVG_NAMESPACES) do |y|
      # warn "*** HREF: #{y}"
      name = y.attributes["href"]
      name1 = name[1..-1]
      if !found_as_id === name1
        warn "*** unknown HREF #{name}"
      end
      found_as_href.add(name1)
      y.attributes["xlink:href"] = "#{name}-#{gensym}"
    end
    found_as_id -= found_as_href
    warn "*** warning: unused ID: #{found_as_id.to_a.join(", ")}" unless found_as_id.empty?
  end
rescue => detail
  warn "*** Can't clean SVG: #{detail}"
end

#trim_empty_lines_around(s) ⇒ Object

this deletes the trailing newline, which may need to be reconstructed



9
10
11
# File 'lib/kramdown-rfc/rfc8792.rb', line 9

def trim_empty_lines_around(s)  # this deletes the trailing newline, which may need to be reconstructed
  s.sub(/\A(\r?\n)*/, '').sub(/(\r?\n)*\z/, '')
end

#unfold8792(s) ⇒ Object



26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# File 'lib/kramdown-rfc/rfc8792.rb', line 26

def unfold8792(s)
  if s =~ UNFOLD_RE
    indicator = $1
    s = $'
    sub = case indicator
          when "\\"
            s.gsub!(/\\\n[ \t]*/, '')
          when "\\\\"
            s.gsub!(/\\\n[ \t]*\\/, '')
          else
            fail "indicator"    # Cannot happen
          end
    warn "** encountered RFC 8792 header without folded lines" unless sub
  end
  s
end

#xml_from_sections(input) ⇒ Object



263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
# File 'lib/kramdown-rfc/command.rb', line 263

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![:include:](#{bts})#{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

#yaml_load(input, *args) ⇒ Object



180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
# File 'lib/kramdown-rfc/command.rb', line 180

def yaml_load(input, *args)
 begin
  if YAML.respond_to?(:safe_load)
    begin
      YAML.safe_load(input, *args)
    rescue ArgumentError
      YAML.safe_load(input, permitted_classes: args[0], permitted_symbols: args[1], aliases: args[2])
    end
  else
    YAML.load(input)
  end
 rescue Psych::SyntaxError => e
   warn "*** YAML syntax error: #{e}"
   exit 65 # EX_DATAERR
 end
end