kramdown-rfc2629-1.0.30/ 0000755 0000041 0000041 00000000000 12672245500 014543 5 ustar www-data www-data kramdown-rfc2629-1.0.30/data/ 0000755 0000041 0000041 00000000000 12672245500 015454 5 ustar www-data www-data kramdown-rfc2629-1.0.30/data/kramdown-rfc2629.erb 0000644 0000041 0000041 00000004321 12672245500 021063 0 ustar www-data www-data "?>
SYSTEM "<%= sys %>">
<% end -%>
<% ps.arr("entity", false) do |en, ev| -%>
"<%=ev%>">
<% end -%>
]>
<% ps.arr("pi", false) do |pi, val| -%>
="<%= {true => "yes", false => "no", nil => "yes"}[val] || val %>"?>
<% end -%>
>
<%= ps.ele("title", ps.attr("abbrev=titleabbrev")) %>
<% ps.arr("author") do |au|
aups = authorps_from_hash(au)
-%>
>
<%= aups.ele("organization=org", aups.attr("abbrev=orgabbrev"), "") %>
<% postal = %w{street city region code country}.select{|gi| aups.has(gi)}
if postal != [] -%>
<% postal.each do |gi| -%>
<%= aups.ele(gi) %>
<% end -%>
<% end -%>
<% %w{phone facsimile email uri}.select{|gi| aups.has(gi)}.each do |gi| -%>
<%= aups.ele(gi) %>
<% end -%>
<% aups.warn_if_leftovers -%>
<% end -%>
/>
<%= ps.ele("area") %>
<%= ps.ele("workgroup=wg") %>
<%= ps.ele("keyword=kw") %>
<%= sechash.delete("abstract") %>
<% sechash.keys.each do |k| -%>
<% if k =~ /\Anote_(.*)/ -%>
">
<%= sechash.delete(k) -%>
<% end -%>
<% end -%>
<%= sechash.delete("middle") %>
<% if sh = sechash.delete("normative") -%>
<%= sh %>
<% end -%>
<% if sh = sechash.delete("informative") -%>
<%= sh %>
<% end -%>
<%= sechash.delete("back") %>
kramdown-rfc2629-1.0.30/bin/ 0000755 0000041 0000041 00000000000 12672245500 015313 5 ustar www-data www-data kramdown-rfc2629-1.0.30/bin/kramdown-rfc2629 0000755 0000041 0000041 00000022573 12672245500 020167 0 ustar www-data www-data #!/usr/bin/env ruby2.1
# -*- coding: utf-8 -*-
require 'kramdown-rfc2629'
require 'yaml'
require 'erb'
require 'date'
Encoding.default_external = "UTF-8" # wake up, smell the coffee
RE_NL = /(?:\n|\r|\r\n)/
RE_SECTION = /---(?:\s+(\w+)(-?))?\s*#{RE_NL}(.*?#{RE_NL})(?=---(?:\s+\w+-?)?\s*#{RE_NL}|\Z)/m
NMDTAGS = ["{:/nomarkdown}\n\n", "\n\n{::nomarkdown}\n"]
NORMINFORM = { "!" => :normative, "?" => :informative }
def xml_from_sections(input)
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 = ParameterSet.new(YAML.load(yaml_in))
coding_override = (ps.has(:coding) =~ /ascii/i) ? :symbolic : :as_char
# 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|
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 = { }
[: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
ref_replacements[v.to_str] = k
end
if v.respond_to? :[]
if aliasname = v.delete("-")
ref_replacements[aliasname] = k
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!(/{{(?:([?!])(-)?|(-))([\w.\-]+)}}/) do |match|
norminform = $1
replacing = $2 || $3
word = $4
if replacing
if new = ref_replacements[word]
word = new
else
warn "*** no alias replacement for {{-#{word}}}"
word = "-#{word}"
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
"{{#{word}}}"
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]
link_defs = {}
[:normative, :informative].each do |sn|
if refs = ps[sn]
refs.each do |k, v|
href = k.gsub(/\A[0-9]/) { "_#{$&}" } # can't start an IDREF with a number
link_defs[k] = ["##{href}", nil] # allow [RFC2119] in addition to {{RFC2119}}
if bts = bibtagsys(k)
if v
warn "*** redundant in #{k}: #{v.inspect}" unless v.respond_to? :to_str
end
if stand_alone
sechash[sn.to_s] << %{\n#{NMDTAGS[0]}\n\n#{NMDTAGS[1]}\n}
else
(ps.rest["bibxml"] ||= []) << k
sechash[sn.to_s] << %{{bts[0]};\n}
end
else
unless v && Hash === v
warn "*** don't know how to expand ref #{k}"
next
end
vps = ParameterSet.new(v)
erb = ERB.new <<-REFERB, nil, '-'
>
<%= vps.ele("title") -%>
<% vps.arr("author", true, true) do |au|
aups = authorps_from_hash(au)
-%>
>
<%= aups.ele("organization=org", aups.attr("abbrev=orgabbrev"), "") %>
<% aups.warn_if_leftovers -%>
<% end -%>
/>
<% vps.arr("seriesinfo", false) do |k, v| -%>
<% end -%>
<% vps.arr("format", false) do |k, v| -%>
<% end -%>
<%= vps.ele("annotation=ann") -%>
REFERB
sechash[sn.to_s] << erb.result(binding)
vps.warn_if_leftovers
end
end
end
end
erbfilename = File.expand_path '../../data/kramdown-rfc2629.erb', __FILE__
erbfile = File.read(erbfilename, coding: "UTF-8")
erb = ERB.new(erbfile, nil, '-')
# 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, coding_override, link_defs]
end
class ParameterSet
include Kramdown::Utils::Html
attr_reader :f
def initialize(y)
raise "*** invalid parameter set #{y.inspect}" unless Hash === y
@f = y
end
def [](pn)
@f.delete(pn.to_s)
end
def has(pn)
@f[pn.to_s]
end
def van(pn) # pn is a parameter name, possibly with an =alias
an, pn = pn.to_s.split("=")
pn ||= an
[self[pn] || self[an], an]
end
def attr(pn)
val, an = van(pn)
%{#{an}="#{val}"} if val
end
def attrs(*pns)
pns.map{ |pn| attr(pn) }.compact.join(" ")
end
def ele(pn, attr=nil, defcontent=nil)
val, an = van(pn)
val ||= defcontent
Array(val).map do |val1|
%{<#{[an, *Array(attr).map(&:to_s)].join(" ").strip}>#{escape_html(val1.to_s.strip)}#{an}>}
end.join(" ")
end
def arr(an, converthash=true, must_have_one=false, &block)
arr = self[an] || []
arr = [arr] if Hash === arr && converthash
arr << { } if must_have_one && arr.empty?
Array(arr).each(&block)
end
def rest
@f
end
def warn_if_leftovers
if !@f.empty?
warn "*** attributes left #{@f.inspect}!"
end
end
end
XML_RESOURCE_ORG_PREFIX = Kramdown::Converter::Rfc2629::XML_RESOURCE_ORG_PREFIX
def bibtagsys(bib)
if bib =~ /\Arfc(\d+)/i
rfc4d = "%04d" % $1.to_i
[bib.upcase,
"#{XML_RESOURCE_ORG_PREFIX}/bibxml/reference.RFC.#{rfc4d}.xml"]
elsif bib =~ /\A([-A-Z0-9]+)\./ &&
dir = Kramdown::Converter::Rfc2629::XML_RESOURCE_ORG_MAP[$1]
bib1 = bib.gsub(/\A[0-9]/) { "_#{$&}" } # can't start an ID with a number
[bib1,
"#{XML_RESOURCE_ORG_PREFIX}/#{dir}/reference.#{bib}.xml"]
end
end
def authorps_from_hash(au)
aups = ParameterSet.new(au)
if ins = aups[:ins]
parts = ins.split('.').map(&:strip)
aups.rest["initials"] = parts[0..-2].join('.') << '.'
aups.rest["surname"] = parts[-1]
end
# hack ("heuristic for") initials and surname from name
# -- only works for people with exactly one last name and uncomplicated first names
if n = aups.rest["name"]
n = n.split
aups.rest["initials"] ||= n[0..-2].map(&:chr).join('.') << '.'
aups.rest["surname"] ||= n[-1]
end
aups
end
def dateattrs(date)
begin
case date
when Integer
%{year="#{"%04d" % date}"}
when String
Date.parse("#{date}-01").strftime(%{year="%Y" month="%B"})
when Date
date.strftime(%{year="%Y" month="%B" day="%d"})
when Array # this allows to explicitly give a string
%{year="#{date.join(" ")}"}
when nil
%{year="n.d."}
end
rescue ArgumentError
warn "*** Invalid date: #{date} -- use 2012, 2012-07, or 2012-07-28"
end
end
coding_override = :as_char
input = ARGF.read
if input[0] == "\uFEFF"
warn "*** There is a leading byte order mark. Ignored."
input[0..0] = ''
end
input.gsub!(/^\{::include\s+(.*?)\}/) {
File.read($1).chomp
} unless ENV["KRAMDOWN_SAFE"]
link_defs = {}
if input =~ /\A---/ # this is a sectionized file
input, coding_override, link_defs = xml_from_sections(input)
end
if input =~ /\A<\?xml/ # if this is a whole XML file, protect it
input = "{::nomarkdown}\n#{input}\n{:/nomarkdown}\n"
end
options = {input: 'RFC2629Kramdown', entity_output: coding_override, link_defs: link_defs}
case coding_override
when :symbolic
options[:smart_quotes] = ["'".ord, "'".ord, '"'.ord, '"'.ord]
end
# warn "options: #{options.inspect}"
doc = Kramdown::Document.new(input, options)
$stderr.puts doc.warnings.to_yaml unless doc.warnings.empty?
puts doc.to_rfc2629
kramdown-rfc2629-1.0.30/lib/ 0000755 0000041 0000041 00000000000 12672245500 015311 5 ustar www-data www-data kramdown-rfc2629-1.0.30/lib/kramdown-rfc2629.rb 0000644 0000041 0000041 00000052564 12672245500 020567 0 ustar www-data www-data # -*- coding: utf-8 -*-
#
#--
# Copyright (C) 2009-2010 Thomas Leitner
# Copyright (C) 2010-2014 Carsten Bormann
#
# This file was derived from a part of the kramdown gem which is licensed under the MIT license.
# This derived work is also licensed under the MIT license, see LICENSE.
#++
#
raise "sorry, 1.8 was last decade" unless RUBY_VERSION >= '1.9'
gem 'kramdown', '~> 1.10.0'
require 'kramdown'
my_span_elements = %w{list figure xref eref iref cref spanx vspace}
Kramdown::Parser::Html::Constants::HTML_SPAN_ELEMENTS.concat my_span_elements
require 'rexml/parsers/baseparser'
require 'open3' # for math
class Object
def deep_clone
Marshal.load(Marshal.dump(self))
end
end
module Kramdown
module Parser
class RFC2629Kramdown < Kramdown
def initialize(*doc)
super
@span_parsers.unshift(:xref)
@span_parsers.unshift(:iref)
end
XREF_START = /\{\{(.*?)\}\}/u
# Introduce new {{target}} syntax for empty xrefs, which would
# otherwise be an ugly  or 
# (I'd rather use [[target]], but that somehow clashes with links.)
def parse_xref
@src.pos += @src.matched_size
href = @src[1]
href = href.gsub(/\A[0-9]/) { "_#{$&}" } # can't start an IDREF with a number
el = Element.new(:xref, nil, {'target' => href})
@tree.children << el
end
define_parser(:xref, XREF_START, '{{')
IREF_START = /\(\(\((.*?)\)\)\)/u
# Introduce new (((target))) syntax for irefs
def parse_iref
@src.pos += @src.matched_size
href = @src[1]
el = Element.new(:iref, nil, {'target' => href}) # XXX
@tree.children << el
end
define_parser(:iref, IREF_START, '\(\(\(')
end
end
class Element
def rfc2629_fix
if a = attr
if anchor = a.delete('id')
a['anchor'] = anchor
end
if anchor = a.delete('href')
a['target'] = anchor
end
end
end
end
module Converter
# Converts a Kramdown::Document to HTML.
class Rfc2629 < Base
# we use these to do XML stuff, too
include ::Kramdown::Utils::Html
def el_html_attributes(el)
html_attributes(el.attr)
end
def el_html_attributes_with(el, defattr)
html_attributes(defattr.merge(el.attr))
end
# :stopdoc:
# Defines the amount of indentation used when nesting XML tags.
INDENTATION = 2
# Initialize the XML converter with the given Kramdown document +doc+.
def initialize(*doc)
super
@sec_level = 1
@in_dt = 0
@footnote_names_in_use = {}
end
def convert(el, indent = -INDENTATION, opts = {})
if el.children[-1].type == :raw
raw = convert1(el.children.pop, indent, opts)
end
"#{convert1(el, indent, opts)}#{end_sections(1, indent)}#{raw}"
end
def convert1(el, indent, opts = {})
el.rfc2629_fix
send("convert_#{el.type}", el, indent, opts)
end
def inner_a(el, indent, opts)
indent += INDENTATION
el.children.map do |inner_el|
inner_el.rfc2629_fix
send("convert_#{inner_el.type}", inner_el, indent, opts)
end
end
def inner(el, indent, opts)
inner_a(el, indent, opts).join('')
end
def convert_blank(el, indent, opts)
"\n"
end
def convert_text(el, indent, opts)
escape_html(el.value, :text)
end
def convert_p(el, indent, opts)
if (el.children.size == 1 && el.children[0].type == :img) || opts[:unpacked]
inner(el, indent, opts) # Part of the bad reference hack
else
"#{' '*indent}#{inner(el, indent, opts)}\n"
end
end
def saner_generate_id(value)
generate_id(value).gsub(/-+/, '-')
end
def convert_codeblock(el, indent, opts)
# el.attr['anchor'] ||= saner_generate_id(el.value) -- no longer in 1.0.6
result = el.value
blockclass = el.attr.delete('class')
if blockclass == 'language-tbreak'
result = result.lines.map {|line| [line.chomp, 0]}
spaceind = 0
result.each_with_index {|pair, index|
if pair[0] == ''
result[spaceind][1] += 1
pair[0] = nil unless index == spaceind
else
spaceind = index
end
}
# $stderr.puts(result.inspect)
result = result.map {|line, space|
"" if line
}.compact.join("\n")
"#{' '*indent}#{result}\n"
else
artwork_attr = {}
if blockclass
classes = blockclass.split(' ')
classes.each do |cl|
if md = cl.match(/\Alanguage-(.*)/)
artwork_attr["type"] = md[1] # XXX overwrite
else
$stderr.puts "*** Unimplemented block class: #{cl}"
end
end
end
# compensate for XML2RFC idiosyncracy by insisting on a blank line
unless el.attr.delete('tight')
result[0,0] = "\n" unless result[0,1] == "\n"
end
el.attr.each do |k, v|
if md = k.match(/\Aartwork-(.*)/)
el.attr.delete(k)
artwork_attr[md[1]] = v
end
end
"#{' '*indent}\n"
end
end
def convert_blockquote(el, indent, opts)
text = inner(el, indent, opts)
text = "" unless text =~ / # empty block quote
"#{' '*indent}\n#{text}#{' '*indent}\n"
end
def end_sections(to_level, indent)
if indent < 0
indent = 0
end
if @sec_level >= to_level
delta = (@sec_level - to_level)
@sec_level = to_level
"#{' '*indent}\n" * delta
else
$stderr.puts "Incorrect section nesting: Need to start with 1"
end
end
def clean_pcdata(parts) # hack, will become unnecessary with XML2RFCv3
clean = ''
irefs = ''
# warn "clean_parts: #{parts.inspect}"
parts.each do |p|
md = p.match(%r{([^<]*)(.*)})
clean << md[1]
irefs << md[2] # breaks for spanx... don't emphasize in headings!
end
[clean, irefs]
end
def convert_header(el, indent, opts)
# todo: handle appendix tags
el = el.deep_clone
options = @doc ? @doc.options : @options # XXX: 0.11 vs. 0.12
if options[:auto_ids] && !el.attr['anchor']
el.attr['anchor'] = saner_generate_id(el.options[:raw_text])
end
clean, irefs = clean_pcdata(inner_a(el, indent, opts))
el.attr['title'] = clean
"#{end_sections(el.options[:level], indent)}#{' '*indent}#{irefs}\n"
end
def convert_hr(el, indent, opts) # misuse for page break
"#{' '*indent}\n"
end
STYLES = {ul: 'symbols', ol: 'numbers', dl: 'hanging'}
def convert_ul(el, indent, opts)
opts = opts.merge(vspace: el.attr.delete('vspace'))
attrstring = el_html_attributes_with(el, {"style" => STYLES[el.type]})
if opts[:unpacked]
"#{' '*indent}\n#{inner(el, indent, opts)}#{' '*indent}\n"
else
"#{' '*indent}\n#{inner(el, indent, opts)}#{' '*indent}\n"
end
end
alias :convert_ol :convert_ul
alias :convert_dl :convert_ul
def convert_li(el, indent, opts)
res_a = inner_a(el, indent, opts)
if el.children.empty? || el.children.first.options[:category] == :span
res = res_a.join('')
else # merge multiple elements
res = res_a.select { |x|
x.strip != ''
}.map { |x|
x.sub(/\A\s*(.*)<\/t>\s*\Z/m) { $1}
}.join("#{' '*indent}\n").gsub(%r{()\s*}) { $1 }.gsub(%r{\s*(#{res}#{(res =~ /\n\Z/ ? ' '*indent : '')}\n"
end
def convert_dd(el, indent, opts)
output = ' '*indent
if @in_dt == 1
@in_dt = 0
else
output << ""
end
res = inner(el, indent+INDENTATION, opts.merge(unpacked: true))
# if el.children.empty? || el.children.first.options[:category] != :block
output << res << (res =~ /\n\Z/ ? ' '*indent : '')
# else FIXME: The latter case is needed for more complex cases
# output << "\n" << res << ' '*indent
# end
output << "\n"
end
def convert_dt(el, indent, opts) # SERIOUSLY BAD HACK:
close = "#{' '*indent}\n" * @in_dt
@in_dt = 1
vspace = opts[:vspace]
vspaceel = "" if vspace
ht = escape_html(inner(el, indent, opts), :attribute) # XXX this may leave gunk
"#{close}#{' '*indent}#{vspaceel}\n"
end
HTML_TAGS_WITH_BODY=['div', 'script']
def convert_html_element(el, indent, opts)
res = inner(el, indent, opts)
if el.options[:category] == :span
"<#{el.value}#{el_html_attributes(el)}" << (!res.empty? ? ">#{res}#{el.value}>" : " />")
else
output = ''
output << ' '*indent if !el.options[:parent_is_raw]
output << "<#{el.value}#{el_html_attributes(el)}"
if !res.empty? && el.options[:parse_type] != :block
output << ">#{res}#{el.value}>"
elsif !res.empty?
output << ">\n#{res}" << ' '*indent << "#{el.value}>"
elsif HTML_TAGS_WITH_BODY.include?(el.value)
output << ">#{el.value}>"
else
output << " />"
end
output << "\n" if el.options[:outer_element] || !el.options[:parent_is_raw]
output
end
end
def convert_xml_comment(el, indent, opts)
if el.options[:category] == :block && !el.options[:parent_is_raw]
' '*indent + el.value + "\n"
else
el.value
end
end
alias :convert_xml_pi :convert_xml_comment
alias :convert_html_doctype :convert_xml_comment
ALIGNMENTS = { default: :left, left: :left, right: :right, center: :center}
COLS_ALIGN = { "l" => :left, "c" => :center, "r" => :right}
def convert_table(el, indent, opts) # This only works for tables with headers
alignment = el.options[:alignment].map { |al| ALIGNMENTS[al]}
cols = (el.attr.delete("cols") || "").split(' ')
"#{' '*indent}\n#{inner(el, indent, opts.merge(table_alignment: alignment, table_cols: cols))}#{' '*indent}\n"
end
def convert_thead(el, indent, opts)
inner(el, indent, opts)
end
alias :convert_tbody :convert_thead
alias :convert_tfoot :convert_thead
alias :convert_tr :convert_thead
def convert_td(el, indent, opts)
if alignment = opts[:table_alignment]
alignment = alignment.shift
if cols = opts[:table_cols].shift
md = cols.match(/(\d*(|em|[%*]))([lrc])/)
if md[1].to_i != 0
widthval = md[1]
widthval << "em" if md[2].empty?
widthopt = "width='#{widthval}' "
end
alignment = COLS_ALIGN[md[3]] || :left
end
end
if alignment
res, irefs = clean_pcdata(inner_a(el, indent, opts))
warn "*** lost markup #{irefs} in table heading" unless irefs.empty?
"#{' '*indent}#{res.empty? ? " " : res}\n" # XXX need clean_pcdata
else
res = inner(el, indent, opts)
"#{' '*indent}#{res.empty? ? " " : res}\n"
end
end
alias :convert_th :convert_td
def convert_comment(el, indent, opts)
## Don't actually output all those comments into the XML:
# if el.options[:category] == :block
# "#{' '*indent}\n"
# else
# ""
# end
end
def convert_br(el, indent, opts)
""
end
def convert_a(el, indent, opts)
res = inner(el, indent, opts)
target = el.attr['target']
if target[0] == "#" # handle [](#foo) as xref as in RFC 7328
el.attr['target'] = target = target[1..-1]
if target.downcase == res.downcase
res = '' # get rid of raw anchors leaking through
end
"#{res}"
else
"#{res}"
end
end
def convert_xref(el, indent, opts)
target = el.attr['target']
if target[0] == "&"
"#{target};"
elsif target =~ %r{\A\w+:(?://|.*@)}
""
else
""
end
end
REFCACHEDIR = ".refcache"
def get_and_cache_resource(url, tn = Time.now, tvalid = 7200)
Dir.mkdir(REFCACHEDIR) unless Dir.exists?(REFCACHEDIR)
fn = "#{REFCACHEDIR}/#{File.basename(url)}"
f = File.stat(fn) rescue nil
if !f || tn - f.ctime >= tvalid
if f
message = "renewing (stale by #{"%.1f" % ((tn-f.ctime)/86400)} days)"
fetch_timeout = 10 # seconds, give up quickly if just renewing
else
message = "fetching"
fetch_timeout = 60 # seconds; long timeout needed for Travis
end
$stderr.puts "#{fn}: #{message}"
if ENV["HAVE_WGET"]
`cd #{REFCACHEDIR}; wget -t 3 -T #{fetch_timeout} -Nnv "#{url}"` # ignore errors if offline (hack)
begin
File.utime nil, nil, fn
rescue Errno::ENOENT
warn "Can't fetch #{url} -- is wget in path?"
end
else
require 'open-uri'
require 'timeout'
begin
Timeout::timeout(fetch_timeout) do
open(url) do |f|
s = f.read
if f.status[0] != "200"
warn "*** Status code #{status} while fetching #{url}"
else
File.write(fn, s)
end
end
end
rescue OpenURI::HTTPError, SocketError, Timeout::Error => e
warn "*** #{e} while fetching #{url}"
end
end
end
File.read(fn) # this blows up if no cache available after fetch attempt
end
XML_RESOURCE_ORG_MAP = {
"RFC" => "bibxml", "I-D" => "bibxml3", "W3C" => "bibxml4", "3GPP" => "bibxml5",
"ANSI" => "bibxml2",
"CCITT" => "bibxml2",
"FIPS" => "bibxml2",
"IANA" => "bibxml2",
"IEEE" => "bibxml2",
"ISO" => "bibxml2",
"ITU" => "bibxml2",
"NIST" => "bibxml2",
"OASIS" => "bibxml2",
"PKCS" => "bibxml2",
}
# XML_RESOURCE_ORG_HOST = ENV["XML_RESOURCE_ORG_HOST"] || "xml.resource.org"
XML_RESOURCE_ORG_HOST = ENV["XML_RESOURCE_ORG_HOST"] || "xml2rfc.ietf.org"
XML_RESOURCE_ORG_PREFIX = ENV["XML_RESOURCE_ORG_PREFIX"] ||
"http://#{XML_RESOURCE_ORG_HOST}/public/rfc"
def convert_img(el, indent, opts) # misuse the tag!
if a = el.attr
alt = a.delete('alt').strip
alt = '' if alt == '!' # work around re-wrap uglyness
if anchor = a.delete('src')
a['target'] = anchor
end
end
if alt == ":include:" # Really bad misuse of tag...
to_insert = ""
anchor.scan(/(W3C|3GPP|[A-Z-]+)[.]?([A-Za-z0-9.-]+)/) do |t, n|
fn = "reference.#{t}.#{n}.xml"
sub = XML_RESOURCE_ORG_MAP[t]
puts "Huh: ${fn}" unless sub
url = "#{XML_RESOURCE_ORG_PREFIX}/#{sub}/#{fn}"
to_insert = get_and_cache_resource(url)
to_insert.scrub! rescue nil # only do this for Ruby >= 2.1
end
to_insert.gsub(/<\?xml version=["']1.0["'] encoding=["']UTF-8["']\?>/, '').
gsub(/(anchor=["'])([0-9])/) { "#{$1}_#{$2}"} # can't start an ID with a number
else
"#{alt}"
end
end
def convert_codespan(el, indent, opts)
attrstring = el_html_attributes_with(el, {"style" => 'verb'})
"#{escape_html(el.value)}"
end
def convert_footnote(el, indent, opts) # XXX: footnotes into crefs???
# this would be more like xml2rfc v3:
# "\n#{' '*indent}\n#{inner(el.value, indent, opts).rstrip}\n#{' '*indent}"
content = inner(el.value, indent, opts).strip
content = escape_html(content.sub(/\A(.*)<\/t>\z/m) {$1}, :text) # text only...
name = el.options[:name].sub(/\A[0-9]/) {"_" << $&}
while @footnote_names_in_use[name] do
if name =~ /:\d+\z/
name.succ!
else
name << ":1"
end
end
@footnote_names_in_use[name] = true
attrstring = el_html_attributes_with(el, {"anchor" => name})
"\n#{' '*indent}#{content}"
end
def convert_raw(el, indent, opts)
end_sections(1, indent) +
el.value + (el.options[:category] == :block ? "\n" : '')
end
EMPH = { em: "emph", strong: "strong"}
def convert_em(el, indent, opts)
attrstring = el_html_attributes_with(el, {"style" => EMPH[el.type]})
span, irefs = clean_pcdata(inner_a(el, indent, opts))
"#{span}#{irefs}"
end
alias :convert_strong :convert_em
def convert_entity(el, indent, opts)
entity_to_str(el.value)
end
TYPOGRAPHIC_SYMS = {
:mdash => [::Kramdown::Utils::Entities.entity('mdash')],
:ndash => [::Kramdown::Utils::Entities.entity('ndash')],
:hellip => [::Kramdown::Utils::Entities.entity('hellip')],
:laquo_space => [::Kramdown::Utils::Entities.entity('laquo'), ::Kramdown::Utils::Entities.entity('nbsp')],
:raquo_space => [::Kramdown::Utils::Entities.entity('nbsp'), ::Kramdown::Utils::Entities.entity('raquo')],
:laquo => [::Kramdown::Utils::Entities.entity('laquo')],
:raquo => [::Kramdown::Utils::Entities.entity('raquo')]
}
def convert_typographic_sym(el, indent, opts)
TYPOGRAPHIC_SYMS[el.value].map {|e| entity_to_str(e)}.join('')
end
def convert_smart_quote(el, indent, opts)
entity_to_str(::Kramdown::Utils::Entities.entity(el.value.to_s))
end
def convert_math(el, indent, opts) # XXX: This is wrong
el = el.deep_clone
if el.options[:category] == :block
el.attr['artwork-type'] ||= ''
el.attr['artwork-type'] += (el.attr['artwork-type'].empty? ? '' : ' ') + 'math'
artwork_attr = {}
el.attr.each do |k, v|
if md = k.match(/\Aartwork-(.*)/)
el.attr.delete(k)
artwork_attr[md[1]] = v
end
end
result, _s = Open3.capture2("tex2mail -noindent -ragged -by_par -linelength=69", stdin_data: el.value);
# warn "*** tex2mail not in path?" unless s.success? -- doesn't have useful status
"#{' '*indent}\n"
else
warn "*** no support for inline math in XML2RFCv2"
type = 'spanx'
attrstring = el_html_attributes_with(el, {"style" => 'verb'})
"<#{type}#{attrstring}>#{escape_html(el.value, :text)}#{type}>"
end
end
ITEM_RE = '\s*(?:"([^"]*)"|([^,]*?))\s*'
IREF_RE = %r{\A(!\s*)?#{ITEM_RE}(?:,#{ITEM_RE})?\z}
def iref_attr(s)
md = s.match(IREF_RE)
attr = {
item: md[2] || md[3],
subitem: md[4] || md[5],
primary: md[1] && 'true',
}
""
end
def convert_iref(el, indent, opts)
iref_attr(el.attr['target'])
end
def convert_abbreviation(el, indent, opts) # XXX: This is wrong
title = @root.options[:abbrev_defs][el.value]
title = nil if title.empty?
value = el.value
if item = title
m = title.scan(Parser::RFC2629Kramdown::IREF_START)
if m.empty?
subitem = value
else
iref = m.map{|a,| iref_attr(a)}.join('')
end
else
item = value
end
iref ||= ""
"#{el.value}#{iref}"
end
def convert_root(el, indent, opts)
result = inner(el, indent, opts)
end
end
end
end
kramdown-rfc2629-1.0.30/kramdown-rfc2629.gemspec 0000644 0000041 0000041 00000001444 12672245500 021030 0 ustar www-data www-data spec = Gem::Specification.new do |s|
s.name = 'kramdown-rfc2629'
s.version = '1.0.30'
s.summary = "Kramdown extension for generating RFC 7749 XML."
s.description = %{An RFC7749 (XML2RFC) generating backend for Thomas Leitner's
"kramdown" markdown parser. Mostly useful for RFC writers.}
s.add_dependency('kramdown', '~> 1.10.0')
s.files = Dir['lib/**/*.rb'] + %w(README.md LICENSE kramdown-rfc2629.gemspec bin/kramdown-rfc2629 data/kramdown-rfc2629.erb)
s.require_path = 'lib'
s.executables = ['kramdown-rfc2629']
s.default_executable = 'kramdown-rfc2629'
s.required_ruby_version = '>= 1.9.2'
# s.requirements = 'wget'
# s.has_rdoc = true
s.author = "Carsten Bormann"
s.email = "cabo@tzi.org"
s.homepage = "http://github.com/cabo/kramdown-rfc2629"
s.license = 'MIT'
end
kramdown-rfc2629-1.0.30/LICENSE 0000644 0000041 0000041 00000002067 12672245500 015555 0 ustar www-data www-data Copyright (c) 2010-2015 Carsten Bormann
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
kramdown-rfc2629-1.0.30/README.md 0000644 0000041 0000041 00000037670 12672245500 016037 0 ustar www-data www-data # kramdown-rfc2629
[kramdown][] is a [markdown][] parser by Thomas Leitner, which has a
number of backends for generating HTML, Latex, and markdown again.
**kramdown-rfc2629** is an additional backend to that: It allows the
generation of [XML2RFC][] XML markup (originally known as [RFC 2629][]
compliant markup, now documented in [RFC 7749][]).
Who would care? Anybody who is writing Internet-Drafts and RFCs in
the [IETF][] and prefers (or has co-authors who prefer) to do part of
their work in markdown.
# Usage
Start by installing the kramdown-rfc2629 gem (this automatically
installs kramdown version 1.6.x as well):
gem install kramdown-rfc2629
(Add a `sudo` and a space in front of that command if you don't have
all the permissions needed.)
The guts of kramdown-rfc2629 are in one Ruby file,
`lib/kramdown-rfc2629.rb` --- this melds nicely into the extension
structure provided by kramdown. `bin/kramdown-rfc2629` started out as
a simple command-line program showing how to use this, but can now do
much more (see below).
To use kramdown-rfc2629, you'll need a Ruby 1.9 or 2.x, and maybe
[XML2RFC][] if you want to see the fruits of your work.
kramdown-rfc2629 mydraft.mkd >mydraft.xml
xml2rfc mydraft.xml
# Examples
For historical interest
`stupid.mkd` was an early markdown version of an actual Internet-Draft
(for a protocol called [STuPiD][] \[sic!]). This demonstrated some,
but not all features of kramdown-rfc2629. Since markdown/kramdown
does not cater for all the structure of an RFC 7749 style document,
some of the markup is in XML, and the example switches between XML and
markdown using kramdown's `{::nomarkdown}` and `{:/nomarkdown}` (this
is ugly, but works well enough). `stupid.xml` and `stupid.txt` show
what kramdown-rfc2629 and xml2rfc make out of this.
`stupid-s.mkd` is the same document in the new sectionized format
supported by kramdown-rfc2629. The document metadata are in a short
piece of YAML at the start, and from there, `abstract`, `middle`,
references (`normative` and `informative`) and `back` are sections
delimited in the markdown file. See the example for how this works.
The sections `normative` and `informative` can be populated right from
the metadata, so there is never a need to write XML any more.
Much less scary, and no `{:/nomarkdown}` etc. is needed any more.
Similarly, `stupid-s.xml` and `stupid-s.txt` show what
kramdown-rfc2629 and xml2rfc make out of this.
`draft-ietf-core-block-xx.mkd` is a real-world example of a current
Internet-Draft done this way. For RFC and Internet-Draft references,
it uses document prolog entities instead of caching the references in
the XML (i.e., not standalone mode, this is easier to handle when
collaborating with XML-only co-authors). See the `bibxml` metadata.
# The YAML header
Please consult the examples for the structure of the YAML header, this should be mostly
obvious. The `stand_alone` attribute controls whether the RFC/I-D
references are inserted into the document (yes) or entity-referenced
(no), the latter leads to increased build time, but may be more
palatable for a final XML conversion.
The author entry can be a single hash or a list, as in:
author:
ins: C. Bormann
name: Carsten Bormann
org: Universität Bremen TZI
abbrev: TZI
street: Bibliothekstr. 1
city: Bremen
code: D-28359
country: Germany
phone: +49-421-218-63921
email: cabo@tzi.org
or
author:
-
ins: C. Bormann
name: Carsten Bormann
org: Universität Bremen TZI
email: cabo@tzi.org
-
ins: Z. Shelby
name: Zach Shelby
org: Sensinode
role: editor
street: Kidekuja 2
city: Vuokatti
code: 88600
country: Finland
phone: "+358407796297"
email: zach@sensinode.com
-
role: editor
ins: P. Thubert
name: Pascal Thubert
org: Cisco Systems
abbrev: Cisco
street:
- Village d'Entreprises Green Side
- 400, Avenue de Roumanille
- Batiment T3
city: Biot - Sophia Antipolis
code: '06410'
country: FRANCE
phone: "+33 4 97 23 26 34"
email: pthubert@cisco.com
(the hash keys are the XML GIs from RFC 7749, with a flattened
structure. As RFC 7749 requires giving both the full name and
surname/initials, we use `ins` as an abbreviation for
"initials/surname". Yes, the toolchain is Unicode-capable, even if
the final RFC output is still in ASCII.)
Note that the YAML header needs to be syntactically valid YAML.
Where there is a potential for triggering some further YAML feature, a
string should be put in quotes (like the "+358407796297" above, which
might otherwise be interpreted as a number, losing the + sign).
## References
The references section is built from the references listed in the YAML
header and from references made inline to RFCs and I-Ds in the
markdown text. Since kramdown-rfc2629 cannot know whether a reference
is normative or informative, no entry is generated by default in the
references section. By indicating a normative reference as in
`{{!RFC2119}}` or an informative one as in `{{?RFC1925}}`, you can
completely automate the referencing, without the need to write
anything in the header. Alternatively, you can write something like:
informative:
RFC1925:
normative:
RFC2119:
and then just write `{{RFC2119}}` or `{{RFC1925}}`. (Yes, there is a
colon in the YAML, because this is a hash that could provide other
information.)
If your references are not in the [XML2RFC][] databases, you need to
spell it out like this:
informative:
RFC1925:
WEI:
title: "6LoWPAN: the Wireless Embedded Internet"
# see the quotes above? Needed because of the embedded colon.
author:
-
ins: Z. Shelby
name: Zach Shelby
-
ins: C. Bormann
name: Carsten Bormann
date: 2009
seriesinfo:
ISBN: 9780470747995
ann: This is a really good reference on 6LoWPAN.
ASN.1:
title: >
Information Technology — ASN.1 encoding rules:
Specification of Basic Encoding Rules (BER), Canonical Encoding
Rules (CER) and Distinguished Encoding Rules (DER)
# YAML's ">" syntax used above is a good way to write longer titles
author:
org: International Telecommunications Union
date: 1994
seriesinfo:
ITU-T: Recommendation X.690
REST:
target: http://www.ics.uci.edu/~fielding/pubs/dissertation/fielding_dissertation.pdf
title: Architectural Styles and the Design of Network-based Software Architectures
author:
ins: R. Fielding
name: Roy Thomas Fielding
org: University of California, Irvine
date: 2000
seriesinfo:
"Ph.D.": "Dissertation, University of California, Irvine"
format:
PDF: http://www.ics.uci.edu/~fielding/pubs/dissertation/fielding_dissertation.pdf
COAP:
title: "CoAP: An Application Protocol for Billions of Tiny Internet Nodes"
seriesinfo:
DOI: 10.1109/MIC.2012.29
date: 2012
author:
-
ins: C. Bormann
name: Carsten Bormann
-
ins: A. P. Castellani
name: Angelo P. Castellani
-
ins: Z. Shelby
name: Zach Shelby
normative:
ECMA262:
author:
org: European Computer Manufacturers Association
title: ECMAScript Language Specification 5.1 Edition
date: 2011-06
target: http://www.ecma-international.org/publications/files/ecma-st/ECMA-262.pdf
seriesinfo:
ECMA: Standard ECMA-262
RFC2119:
RFC6690:
(as in the author list, `ins` is an abbreviation for
"initials/surname"; note that the first title had to be put in double
quotes as it contains a colon which is special syntax in YAML.)
Then you can simply reference `{{ASN.1}}` and
`{{ECMA262}}` in the text. (Make sure the reference keys are valid XML
names, though.)
# Experimental features
Most of the [kramdown syntax][kdsyntax] is supported and does
something useful; with the exception of the math syntax (math has no
special support in XML2RFC), and HTML syntax of course.
A number of more esoteric features have recently been added.
(The minimum required version for each full feature is indicated.)
(1.0.30:)
kramdown-rfc now uses kramdown 1.10, which leads to two notable updates:
* Support for empty link texts in the standard markdown
reference syntax, as in `[](#RFC7744)`.
* Language names in fenced code blocks now support all characters
except whitespace, so you can go wild with `asn.1` and `C#`.
A heuristic generates missing initials/surname from the `name` entry
in author information. This should save a lot of redundant typing.
You'll need to continue using the `ins` entry as well if that
heuristic fails (e.g., for Spanish names).
Also, there is some rather experimental support for markdown display
math (blocks between `$$` pairs) if the `tex2mail` tool is available.
(1.0.23:)
Move up to kramdown 1.6.0. This inherits a number of fixes and one
nice feature:
Markdown footnote definitions that turn into `cref`s can have their
attributes in the footnote definition:
```markdown
{:cabo: source="cabo"}
(This section to be removed by the RFC editor.)[^1]
[^1]: here is my editorial comment: warble warble.
{:cabo}
Another questionable paragraph.[^2]
[^2]: so why not delete it?
{: source="observer"}
```
(1.0.23:)
As before, IAL attributes on a codeblock go to the figure element.
Language attributes on the code block now become the artwork type, and any
attribute with a name that starts "artwork-" is moved over to the artwork.
So this snippet now does the obvious things:
```markdown
~~~ abnf
a = b / %s"foo" / %x0D.0A
~~~
{: artwork-align="center" artwork-name="syntax"}
```
(1.0.22:)
Index entries can be created with `(((item)))` or
`(((item, subitem)))`; use quotes for weird entries: `(((",", comma)))`.
If the index entry is to be marked "primary", prefix an (unquoted) `!`
as in `(((!item)))`.
In addition, auto-indexing is supported by hijacking the kramdown
"abbrev" syntax:
*[IANA]:
*[MUST]: BCP14
*[CBOR]: (((Object Representation, Concise Binary))) (((CBOR)))
The word in square brackets (which must match exactly,
case-sensitively) is entered into the index automatically for each
place where it occurs. If no title is given, just the word is entered
(first example). If one is given, that becomes the main item (the
auto-indexed word becomes the subitem, second example). If full
control is desired (e.g., for multiple entries per occurrence), just
write down the full index entries instead (third example).
(1.0.20:)
As an alternative referencing syntax for references with text,
`{{ref}}` can be expressed as `[text](#ref)`. As a special case, a
simple `[ref]` is interpreted as `[](#ref)` (except that the latter
syntax is not actually allowed by kramdown). This syntax does not
allow for automatic entry of items as normative/informative.
(1.0.16:) Markdown footnotes are converted into `cref`s (XML2RFC formal
comments; note that these are only visible if the pi "comments" is set to yes).
The anchor is taken from the markdown footnote name. The source, if
needed, can be supplied by an IAD, as in (first example with
ALD):
```markdown
{:cabo: source="cabo"}
(This section to be removed by the RFC editor.)[^1]{:cabo}
[^1]: here is my editorial comment
Another questionable paragraph.[^2]{: source="observer"}
[^2]: so why not delete it
```
Note that XML2RFC v2 doesn't allow structure in crefs. If you put any,
you get the escaped verbatim XML...
(1.0.11:) Allow overriding "style" attribute (via IAL =
[inline attribute list][kdsyntax-ial]) in lists and spans
as in:
```markdown
{:req: counter="bar" style="format R(%d)"}
{: req}
* Foo
* Bar
* Bax
Text outside the list, so a new IAL is needed.
* Foof
* Barf
* Barx
{: req}
```
(1.0.5:) An IAL attribute "cols" can be added to tables to override
the column layout. For example, `cols="* 20 30c r"` sets the width attributes to
20 and 30 for the middle columns and sets the right two columns to
center and right alignment, respectively. The alignment from `cols`
overrides that from the kramdown table, if present.
(1.0.2:) An IAL attribute "vspace" can be added to a definition list
to break after the definition term:
```markdown
{: vspace="0"}
word:
: definition
anotherword:
: another definition
```
(0.x:) Files can be included with the syntax `{::include fn}` (needs
to be in column 1 since 1.0.22; can be suppressed for use in servers
by setting environment variable KRAMDOWN_SAFE since 1.0.22). A
typical example from a recent RFC, where the contents of a figure was
machine-generated:
```markdown
~~~~~~~~~~
{::include ../ghc/packets-new/p4.out}
~~~~~~~~~~
{: #example2 title="A longer RPL example"}
```
(0.x:) A page break can be forced by adding a horizontal rule (`----`,
note that this creates ugly blank space in some HTML converters).
# Risks and Side-Effects
The code is not very polished, but now quite stable; it has been successfully used for a
number of non-trivial Internet-Drafts and RFCs. You probably still need to
skim [RFC 7749][] if you want to write an Internet-Draft, but you
don't really need to understand XML very much. Knowing the basics of
YAML helps with the metadata (but you'll understand it from the
examples).
# Upconversion
If you have an old RFC and want to convert it to markdown, try just
using that RFC, it is 80 % there. It may be possible to automate the
remaining 20 % some more, but that hasn't been done.
If you have XML, there is an experimental upconverter that does 99 %
of the work. Please contact the author if you want to try it.
# Tools
Joe Hildebrand has a
[grunt][] plugin for kramdown-rfc2629 at:
https://github.com/hildjj/grunt-kramdown-rfc2629
.
Get started with it at:
https://github.com/hildjj/grunt-init-rfc
.
This provides a self-refreshing web page with the
kramdown-rfc2629/xml2rfc rendition of the draft you are editing.
[grunt]: http://gruntjs.com
Martin Thomson has an [I-D Template][] for github repositories that enable
collaboration on draft development.
This supports kramdown-rfc2629 out of the
box. Just name your draft like `draft-ietf-unicorn-protocol-latest.md` and
follow the installation instructions.
[I-D Template]: https://github.com/martinthomson/i-d-template
# Related Work
Moving from XML to Markdown for RFC writing apparently is a
no-brainer, so I'm not the only one who has written code for this.
[Miek Gieben][] has done a [similar thing][pandoc2rfc] employing
pandoc, now documented in [RFC 7328][]. He uses multiple input files instead of
kramdown-rfc2629's sectionized input format. He keeps the metadata in
a separate XML file, similar to the way the previous version of
kramdown-rfc2629 stored (and still can store) the metadata in XML in
the markdown document. He also uses a slightly different referencing
syntax, which is closer to what markdown does elsewhere but more
verbose (this syntax is now also supported in kramdown-rfc2629).
(Miek now also has a new thing going on with mostly different syntax,
see [mmark][].)
# License
Since kramdown version 1.0, kramdown itself is MIT licensed, which
made it possible to license kramdown-rfc2629 under the same license.
[kramdown]: http://kramdown.rubyforge.org/
[kdsyntax]: http://kramdown.gettalong.org/syntax.html
[kdsyntax-ial]: http://kramdown.gettalong.org/syntax.html#inline-attribute-lists
[stupid]: http://tools.ietf.org/id/draft-hartke-xmpp-stupid-00
[RFC 2629]: http://xml.resource.org/public/rfc/html/rfc2629.html
[RFC 7749]: http://tools.ietf.org/html/rfc7749
[markdown]: http://en.wikipedia.org/wiki/Markdown
[IETF]: http://www.ietf.org
[Miek Gieben]: http://www.miek.nl/
[pandoc2rfc]: https://github.com/miekg/pandoc2rfc/
[XML2RFC]: http://xml.resource.org
[RFC 7328]: http://tools.ietf.org/html/rfc7328
[mmark]: https://github.com/miekg/mmark
[YAML]: http://www.yaml.org/spec/1.2/spec.html