class Object

Constants

FALLBACK
FOLD_COLUMNS
FOLD_MSG
KDRFC_VERSION

try to get this from gemspec.

MIN_FOLD_COLUMNS
NMDTAGS
NORMINFORM
RE_IDENT
RE_NL
RE_SECTION
XML_RESOURCE_ORG_PREFIX
XREF_SECTIONS_RE
XREF_TXT
XREF_TXT_SUFFIX
XSR_PREFIX
XSR_SUFFIX

Public Instance Methods

add_quote(s) click to toggle source
# File lib/kramdown-rfc/command.rb, line 23
def add_quote(s)
  l = s.lines
  l.map {|li| "> #{li}"}.join
end
bibtagsys(bib, anchor=nil, stand_alone=true) click to toggle source

return XML entity name, url, rewrite_anchor flag

# File lib/kramdown-rfc/command.rb, line 432
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) click to toggle source
# File lib/kramdown-rfc/command.rb, line 77
def boilerplate(key)
  case key.downcase
  when /\Abcp14(info)?(\+)?(-tagged)?\z/i
    ret = ''
    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
    ret
  else
    warn "** Unknwon boilerplate key: #{key}"
    "{::boilerplate #{key}}"
  end
end
deep_clone() click to toggle source
# File lib/kramdown-rfc2629.rb, line 25
def deep_clone
  Marshal.load(Marshal.dump(self))
end
do_the_tls_dance() click to toggle source
# File lib/kramdown-rfc/command.rb, line 125
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
expand_tabs(s, tab_stops = 8) click to toggle source
# File lib/kramdown-rfc/command.rb, line 471
def expand_tabs(s, tab_stops = 8)
  s.gsub(/([^\t\n]*)\t/) do
    $1 + " " * (tab_stops - ($1.size % tab_stops))
  end
end
fold8792_1(s, columns = FOLD_COLUMNS, left = false, dry = false) click to toggle source
# File lib/kramdown-rfc/rfc8792.rb, line 6
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
process_chunk(s, nested, dedent, fold, quote) click to toggle source
# File lib/kramdown-rfc/command.rb, line 28
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) click to toggle source
# File lib/kramdown-rfc/command.rb, line 36
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) click to toggle source
# File lib/kramdown-rfc/command.rb, line 163
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_encodings() click to toggle source
# File lib/kramdown-rfc/command.rb, line 460
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) click to toggle source

Note that this doesn't attempt to handle HT characters

# File lib/kramdown-rfc/command.rb, line 17
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) click to toggle source
# File lib/kramdown-rfc/command.rb, line 223
def spacify_re(s)
  s.gsub(' ', '[\u00A0\s]+')
end
xml_from_sections(input) click to toggle source
# File lib/kramdown-rfc/command.rb, line 227
def xml_from_sections(input)

  unless ENV["KRAMDOWN_NO_SOURCE"]
    require 'kramdown-rfc/gzip-clone'
    require 'base64'
    compressed_input = Gzip.compress(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]

  # 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

  coding_override = ps.has(:coding)
  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|
    # 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}#{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] = 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.\/_\-]+))?(#{
      XREF_TXT_SUFFIX
    })?(#{
      spacify_re(XSR_SUFFIX)
    })?}}/) do |match|
      xsr_prefix = $1
      norminform = $2
      replacing = $3 || $4
      word = $5
      bibref = $6
      xrt_suffix = $7
      xsr_suffix = $8
      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}#{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)
        if bts && (!v || v == {} || v.respond_to?(:to_str))
          if stand_alone
            a = %{{: anchor="#{k}"}}
            sechash[sn.to_s] << %{\n#{NMDTAGS[0]}\n![:include:](#{bts})#{a}\n#{NMDTAGS[1]}\n}
          else
            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
          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) click to toggle source
# File lib/kramdown-rfc/command.rb, line 151
def yaml_load(input, *args)
  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
end