kramdown-rfc2629 1.5.26 → 1.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +13 -0
- data/bin/kdrfc +17 -0
- data/bin/kramdown-rfc +9 -0
- data/bin/kramdown-rfc2629 +1 -536
- data/kramdown-rfc2629.gemspec +3 -3
- data/lib/kramdown-rfc/command.rb +572 -0
- data/lib/kramdown-rfc/kdrfc-processor.rb +3 -1
- data/lib/kramdown-rfc/parameterset.rb +9 -1
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e14f146c7079e4611aa6ddf79f706ef0fddb168e1bb510c278ead6c93595b99a
|
4
|
+
data.tar.gz: efa3c83941f0eaa68e82d9d2bbe554826f70bee1469498e870506f006806bcf8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 54b21302c63267ecf9eaf845d67927c8f7f7dc78b4608c545fb6487274a507980695512d21397fa54c7700c3659600229c6dc0cab4964e2c8e9a471da9b51e00
|
7
|
+
data.tar.gz: '093aaa4efee63f5a103fba961072413884170bbcf13bb402b3ea40f36d1af1843d0b170be57d257dbf94f7b05bb6dc1e38df6a76abee06a5c475517fdd843d62'
|
data/README.md
CHANGED
@@ -48,6 +48,19 @@ is provided by `kdrfc`:
|
|
48
48
|
|
49
49
|
kdrfc -r mydraft.mkd
|
50
50
|
|
51
|
+
# Versions of RFCXML
|
52
|
+
|
53
|
+
Since RFC 8650, RFCs are using an updated grammar as defined in RFC
|
54
|
+
7991 to 7998 and further updated informally since, colloquially called "[v3][]".
|
55
|
+
As RFC 2629 is no longer the governing standard, kramdown-rfc2629 is
|
56
|
+
now called kramdown-rfc. The latter command defaults to v3 processing
|
57
|
+
rules; from 2022-02-22T22:02:22 on, kramdown-rfc2629 does as well (1.6.1).
|
58
|
+
(-3/--v3 and -2/--v2 select v3 and v2 explicitly; the latter should
|
59
|
+
only be needed if there is a reason to to make a document look
|
60
|
+
like it's 2016.)
|
61
|
+
|
62
|
+
[v3]: https://xml2rfc.tools.ietf.org/xml2rfc-doc.html
|
63
|
+
|
51
64
|
# Examples
|
52
65
|
|
53
66
|
For historical interest
|
data/bin/kdrfc
CHANGED
@@ -51,9 +51,26 @@ BANNER
|
|
51
51
|
opts.on("-3", "--[no-]v3", "Use RFCXML v3 processing rules") do |v|
|
52
52
|
kdrfc.options.v3 = v
|
53
53
|
end
|
54
|
+
opts.on("-2", "--[no-]v2", "Use RFCXML v2 processing rules") do |v|
|
55
|
+
kdrfc.options.v2 = v
|
56
|
+
end
|
54
57
|
end
|
55
58
|
op.parse!
|
56
59
|
|
60
|
+
|
61
|
+
if kdrfc.options.v2 && kdrfc.options.v3
|
62
|
+
warn "*** can't have v2 and eat v3 cake"
|
63
|
+
kdrfc.options.v2 = false
|
64
|
+
end
|
65
|
+
|
66
|
+
if kdrfc.options.v3.nil? && !kdrfc.options.v2
|
67
|
+
if Time.now.to_i >= 1645567342 # Time.parse("2022-02-22T22:02:22Z").to_i
|
68
|
+
kdrfc.options.v3 = true # new default from the above date
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
warn "*** v2 #{kdrfc.options.v2.inspect} v3 #{kdrfc.options.v3.inspect}" if kdrfc.options.verbose
|
73
|
+
|
57
74
|
case ARGV.size
|
58
75
|
when 1
|
59
76
|
fn = ARGV[0]
|
data/bin/kramdown-rfc
ADDED
data/bin/kramdown-rfc2629
CHANGED
@@ -1,539 +1,4 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
# -*- coding: utf-8 -*-
|
3
|
-
require 'kramdown-
|
4
|
-
require 'kramdown-rfc/parameterset'
|
5
|
-
require 'kramdown-rfc/refxml'
|
6
|
-
require 'kramdown-rfc/rfc8792'
|
7
|
-
require 'yaml'
|
8
|
-
require 'kramdown-rfc/erb'
|
9
|
-
require 'date'
|
3
|
+
require 'kramdown-rfc/command'
|
10
4
|
|
11
|
-
# try to get this from gemspec.
|
12
|
-
KDRFC_VERSION=Gem.loaded_specs["kramdown-rfc2629"].version rescue "unknown-version"
|
13
|
-
|
14
|
-
Encoding.default_external = "UTF-8" # wake up, smell the coffee
|
15
|
-
|
16
|
-
# Note that this doesn't attempt to handle HT characters
|
17
|
-
def remove_indentation(s)
|
18
|
-
l = s.lines
|
19
|
-
indent = l.grep(/\S/).map {|l| l[/^\s*/].size}.min
|
20
|
-
l.map {|li| li.sub(/^ {0,#{indent}}/, "")}.join
|
21
|
-
end
|
22
|
-
|
23
|
-
def add_quote(s)
|
24
|
-
l = s.lines
|
25
|
-
l.map {|li| "> #{li}"}.join
|
26
|
-
end
|
27
|
-
|
28
|
-
def process_chunk(s, nested, dedent, fold, quote)
|
29
|
-
process_includes(s) if nested
|
30
|
-
s = remove_indentation(s) if dedent
|
31
|
-
s = fold8792_1(s, *fold) if fold
|
32
|
-
s = add_quote(s) if quote
|
33
|
-
s
|
34
|
-
end
|
35
|
-
|
36
|
-
def process_includes(input)
|
37
|
-
input.gsub!(/^\{::include((?:-[a-z0-9]+)*)\s+(.*?)\}/) {
|
38
|
-
include_flags = $1
|
39
|
-
fn = [$2]
|
40
|
-
chunks = false
|
41
|
-
nested = false
|
42
|
-
dedent = false
|
43
|
-
fold = false
|
44
|
-
quote = false
|
45
|
-
include_flags.split("-") do |flag|
|
46
|
-
case flag
|
47
|
-
when ""
|
48
|
-
when "nested"
|
49
|
-
nested = true
|
50
|
-
when "quote"
|
51
|
-
quote = true
|
52
|
-
when "dedent"
|
53
|
-
dedent = true
|
54
|
-
when /\Afold(\d*)(left(\d*))?(dry)?\z/
|
55
|
-
fold = [$1.to_i, # col 0 for ''
|
56
|
-
($3.to_i if $2), # left 0 for '', nil if no "left"
|
57
|
-
$4] # dry
|
58
|
-
when "all", "last"
|
59
|
-
fn = fn.flat_map{|n| Dir[n]}
|
60
|
-
fn = [fn.last] if flag == "last"
|
61
|
-
chunks = fn.map{ |f|
|
62
|
-
ret = process_chunk(File.read(f), nested, dedent, fold, quote)
|
63
|
-
nested = false; dedent = false; fold = false; quote = false
|
64
|
-
ret
|
65
|
-
}
|
66
|
-
else
|
67
|
-
warn "** unknown include flag #{flag}"
|
68
|
-
end
|
69
|
-
end
|
70
|
-
chunks = fn.map{|f| File.read(f)} unless chunks # no all/last
|
71
|
-
chunks = chunks.map {|ch| process_chunk(ch, nested, dedent, fold, quote)}
|
72
|
-
chunks.join.chomp
|
73
|
-
}
|
74
|
-
end
|
75
|
-
|
76
|
-
|
77
|
-
def boilerplate(key)
|
78
|
-
case key.downcase
|
79
|
-
when /\Abcp14(info)?(\+)?(-tagged)?\z/i
|
80
|
-
ret = ''
|
81
|
-
if $1
|
82
|
-
ret << <<RFC8174ise
|
83
|
-
Although this document is not an IETF Standards Track publication, it
|
84
|
-
adopts the conventions for normative language to provide clarity of
|
85
|
-
instructions to the implementer.
|
86
|
-
RFC8174ise
|
87
|
-
end
|
88
|
-
ret << <<RFC8174
|
89
|
-
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL
|
90
|
-
NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED",
|
91
|
-
"MAY", and "OPTIONAL" in this document are to be interpreted as
|
92
|
-
described in BCP 14 {{!RFC2119}} {{!RFC8174}} when, and only when, they
|
93
|
-
appear in all capitals, as shown here.
|
94
|
-
RFC8174
|
95
|
-
if $2
|
96
|
-
ret << <<PLUS
|
97
|
-
These words may also appear in this document in
|
98
|
-
lower case as plain English words, absent their normative meanings.
|
99
|
-
PLUS
|
100
|
-
end
|
101
|
-
if $3
|
102
|
-
if $options.v3
|
103
|
-
ret << <<TAGGED
|
104
|
-
|
105
|
-
*[MUST]: <bcp14>
|
106
|
-
*[MUST NOT]: <bcp14>
|
107
|
-
*[REQUIRED]: <bcp14>
|
108
|
-
*[SHALL]: <bcp14>
|
109
|
-
*[SHALL NOT]: <bcp14>
|
110
|
-
*[SHOULD]: <bcp14>
|
111
|
-
*[SHOULD NOT]: <bcp14>
|
112
|
-
*[RECOMMENDED]: <bcp14>
|
113
|
-
*[NOT RECOMMENDED]: <bcp14>
|
114
|
-
*[MAY]: <bcp14>
|
115
|
-
*[OPTIONAL]: <bcp14>
|
116
|
-
TAGGED
|
117
|
-
else
|
118
|
-
warn "** need --v3 to tag bcp14"
|
119
|
-
end
|
120
|
-
end
|
121
|
-
ret
|
122
|
-
else
|
123
|
-
warn "** Unknwon boilerplate key: #{key}"
|
124
|
-
"{::boilerplate #{key}}"
|
125
|
-
end
|
126
|
-
end
|
127
|
-
|
128
|
-
def do_the_tls_dance
|
129
|
-
begin
|
130
|
-
require 'openssl'
|
131
|
-
File.open(OpenSSL::X509::DEFAULT_CERT_FILE) do end
|
132
|
-
# This guards against having an unreadable cert file (yes, that appears to happen a lot).
|
133
|
-
rescue
|
134
|
-
if Dir[File.join(OpenSSL::X509::DEFAULT_CERT_DIR, "*.pem")].empty?
|
135
|
-
# This guards against having no certs at all, not against missing the right one for IETF.
|
136
|
-
# Oh well.
|
137
|
-
warn "** Configuration problem with OpenSSL certificate store."
|
138
|
-
warn "** You may want to examine #{OpenSSL::X509::DEFAULT_CERT_FILE}"
|
139
|
-
warn "** and #{OpenSSL::X509::DEFAULT_CERT_DIR}."
|
140
|
-
warn "** Activating suboptimal workaround."
|
141
|
-
warn "** Occasionally run `certified-update` to maintain that workaround."
|
142
|
-
require 'certified'
|
143
|
-
end
|
144
|
-
end
|
145
|
-
end
|
146
|
-
|
147
|
-
RE_NL = /(?:\n|\r|\r\n)/
|
148
|
-
RE_SECTION = /---(?: +(\w+)(-?))?\s*#{RE_NL}(.*?#{RE_NL})(?=---(?:\s+\w+-?)?\s*#{RE_NL}|\Z)/m
|
149
|
-
|
150
|
-
NMDTAGS = ["{:/nomarkdown}\n\n", "\n\n{::nomarkdown}\n"]
|
151
|
-
|
152
|
-
NORMINFORM = { "!" => :normative, "?" => :informative }
|
153
|
-
|
154
|
-
def yaml_load(input, *args)
|
155
|
-
if YAML.respond_to?(:safe_load)
|
156
|
-
begin
|
157
|
-
YAML.safe_load(input, *args)
|
158
|
-
rescue ArgumentError
|
159
|
-
YAML.safe_load(input, permitted_classes: args[0], permitted_symbols: args[1], aliases: args[2])
|
160
|
-
end
|
161
|
-
else
|
162
|
-
YAML.load(input)
|
163
|
-
end
|
164
|
-
end
|
165
|
-
|
166
|
-
def process_kramdown_options(coding_override = nil,
|
167
|
-
smart_quotes = nil, typographic_symbols = nil,
|
168
|
-
header_kramdown_options = nil)
|
169
|
-
|
170
|
-
ascii_target = coding_override && coding_override =~ /ascii/
|
171
|
-
suppress_typography = ascii_target || $options.v3
|
172
|
-
entity_output = ascii_target ? :numeric : :as_char;
|
173
|
-
|
174
|
-
options = {input: 'RFC2629Kramdown', entity_output: entity_output, link_defs: {}}
|
175
|
-
|
176
|
-
if smart_quotes.nil? && suppress_typography
|
177
|
-
smart_quotes = false
|
178
|
-
end
|
179
|
-
if smart_quotes == false
|
180
|
-
smart_quotes = ["'".ord, "'".ord, '"'.ord, '"'.ord]
|
181
|
-
end
|
182
|
-
case smart_quotes
|
183
|
-
when Array
|
184
|
-
options[:smart_quotes] = smart_quotes
|
185
|
-
when nil, true
|
186
|
-
# nothin
|
187
|
-
else
|
188
|
-
warn "*** Can't deal with smart_quotes value #{smart_quotes.inspect}"
|
189
|
-
end
|
190
|
-
|
191
|
-
if typographic_symbols.nil? && suppress_typography
|
192
|
-
typographic_symbols = false
|
193
|
-
end
|
194
|
-
if typographic_symbols == false
|
195
|
-
typographic_symbols = Hash[::Kramdown::Parser::Kramdown::TYPOGRAPHIC_SYMS.map { |k, v|
|
196
|
-
if Symbol === v
|
197
|
-
[v.intern, k]
|
198
|
-
end
|
199
|
-
}.compact]
|
200
|
-
end
|
201
|
-
# warn [:TYPOGRAPHIC_SYMBOLS, typographic_symbols].to_yaml
|
202
|
-
case typographic_symbols
|
203
|
-
when Hash
|
204
|
-
options[:typographic_symbols] = typographic_symbols
|
205
|
-
when nil, true
|
206
|
-
# nothin
|
207
|
-
else
|
208
|
-
warn "*** Can't deal with typographic_symbols value #{typographic_symbols.inspect}"
|
209
|
-
end
|
210
|
-
|
211
|
-
if header_kramdown_options
|
212
|
-
options.merge! header_kramdown_options
|
213
|
-
end
|
214
|
-
|
215
|
-
$global_markdown_options = options # For nested calls in bibref annotation processing and xref text
|
216
|
-
|
217
|
-
options
|
218
|
-
end
|
219
|
-
|
220
|
-
XREF_SECTIONS_RE = ::Kramdown::Parser::RFC2629Kramdown::SECTIONS_RE
|
221
|
-
XSR_PREFIX = "#{XREF_SECTIONS_RE} of "
|
222
|
-
XSR_SUFFIX = ", (#{XREF_SECTIONS_RE})| \\((#{XREF_SECTIONS_RE})\\)"
|
223
|
-
XREF_TXT = ::Kramdown::Parser::RFC2629Kramdown::XREF_TXT
|
224
|
-
XREF_TXT_SUFFIX = " \\(#{XREF_TXT}\\)"
|
225
|
-
|
226
|
-
def spacify_re(s)
|
227
|
-
s.gsub(' ', '[\u00A0\s]+')
|
228
|
-
end
|
229
|
-
|
230
|
-
def xml_from_sections(input)
|
231
|
-
|
232
|
-
unless ENV["KRAMDOWN_NO_SOURCE"]
|
233
|
-
require 'kramdown-rfc/gzip-clone'
|
234
|
-
require 'base64'
|
235
|
-
compressed_input = Gzip.compress(input)
|
236
|
-
$source = Base64.encode64(compressed_input)
|
237
|
-
end
|
238
|
-
|
239
|
-
sections = input.scan(RE_SECTION)
|
240
|
-
# resulting in an array; each section is [section-label, nomarkdown-flag, section-text]
|
241
|
-
|
242
|
-
# the first section is a YAML with front matter parameters (don't put a label here)
|
243
|
-
# We put back the "---" plus gratuitous blank lines to hack the line number in errors
|
244
|
-
yaml_in = input[/---\s*/] << sections.shift[2]
|
245
|
-
ps = KramdownRFC::ParameterSet.new(yaml_load(yaml_in, [Date], [], true))
|
246
|
-
|
247
|
-
coding_override = ps.has(:coding)
|
248
|
-
smart_quotes = ps[:smart_quotes]
|
249
|
-
typographic_symbols = ps[:typographic_symbols]
|
250
|
-
header_kramdown_options = ps[:kramdown_options]
|
251
|
-
|
252
|
-
kramdown_options = process_kramdown_options(coding_override,
|
253
|
-
smart_quotes, typographic_symbols,
|
254
|
-
header_kramdown_options)
|
255
|
-
|
256
|
-
# all the other sections are put in a Hash, possibly concatenated from parts there
|
257
|
-
sechash = Hash.new{ |h,k| h[k] = ""}
|
258
|
-
snames = [] # a stack of section names
|
259
|
-
sections.each do |sname, nmdflag, text|
|
260
|
-
# warn [:SNAME, sname, nmdflag, text[0..10]].inspect
|
261
|
-
nmdin, nmdout = {
|
262
|
-
"-" => ["", ""], # stay in nomarkdown
|
263
|
-
"" => NMDTAGS, # pop out temporarily
|
264
|
-
}[nmdflag || ""]
|
265
|
-
if sname
|
266
|
-
snames << sname # "--- label" -> push label (now current)
|
267
|
-
else
|
268
|
-
snames.pop # just "---" -> pop label (previous now current)
|
269
|
-
end
|
270
|
-
sechash[snames.last] << "#{nmdin}#{text}#{nmdout}"
|
271
|
-
end
|
272
|
-
|
273
|
-
ref_replacements = { }
|
274
|
-
anchor_to_bibref = { }
|
275
|
-
|
276
|
-
[:ref, :normative, :informative].each do |sn|
|
277
|
-
if refs = ps.has(sn)
|
278
|
-
warn "*** bad section #{sn}: #{refs.inspect}" unless refs.respond_to? :each
|
279
|
-
refs.each do |k, v|
|
280
|
-
if v.respond_to? :to_str
|
281
|
-
if bibtagsys(v) # enable "foo: RFC4711" as a custom anchor definition
|
282
|
-
anchor_to_bibref[k] = v.to_str
|
283
|
-
end
|
284
|
-
ref_replacements[v.to_str] = k
|
285
|
-
end
|
286
|
-
if Hash === v
|
287
|
-
if aliasname = v.delete("-")
|
288
|
-
ref_replacements[aliasname] = k
|
289
|
-
end
|
290
|
-
if bibref = v.delete("=")
|
291
|
-
anchor_to_bibref[k] = bibref
|
292
|
-
end
|
293
|
-
end
|
294
|
-
end
|
295
|
-
end
|
296
|
-
end
|
297
|
-
open_refs = ps[:ref] || { } # consumed
|
298
|
-
|
299
|
-
norm_ref = { }
|
300
|
-
|
301
|
-
# convenience replacement of {{-coap}} with {{I-D.ietf-core-coap}}
|
302
|
-
# collect normative/informative tagging {{!RFC2119}} {{?RFC4711}}
|
303
|
-
sechash.each do |k, v|
|
304
|
-
next if k == "fluff"
|
305
|
-
v.gsub!(/{{(#{
|
306
|
-
spacify_re(XSR_PREFIX)
|
307
|
-
})?(?:([?!])(-)?|(-))([\w._\-]+)(?:=([\w.\/_\-]+))?(#{
|
308
|
-
XREF_TXT_SUFFIX
|
309
|
-
})?(#{
|
310
|
-
spacify_re(XSR_SUFFIX)
|
311
|
-
})?}}/) do |match|
|
312
|
-
xsr_prefix = $1
|
313
|
-
norminform = $2
|
314
|
-
replacing = $3 || $4
|
315
|
-
word = $5
|
316
|
-
bibref = $6
|
317
|
-
xrt_suffix = $7
|
318
|
-
xsr_suffix = $8
|
319
|
-
if replacing
|
320
|
-
if new = ref_replacements[word]
|
321
|
-
word = new
|
322
|
-
else
|
323
|
-
warn "*** no alias replacement for {{-#{word}}}"
|
324
|
-
word = "-#{word}"
|
325
|
-
end
|
326
|
-
end # now, word is the anchor
|
327
|
-
if bibref
|
328
|
-
if old = anchor_to_bibref[word]
|
329
|
-
if bibref != old
|
330
|
-
warn "*** conflicting definitions for xref #{word}: #{old} != #{bibref}"
|
331
|
-
end
|
332
|
-
else
|
333
|
-
anchor_to_bibref[word] = bibref
|
334
|
-
end
|
335
|
-
end
|
336
|
-
|
337
|
-
# things can be normative in one place and informative in another -> normative
|
338
|
-
# collect norm/inform above and assign it by priority here
|
339
|
-
if norminform
|
340
|
-
norm_ref[word] ||= norminform == '!' # one normative ref is enough
|
341
|
-
end
|
342
|
-
"{{#{xsr_prefix}#{word}#{xrt_suffix}#{xsr_suffix}}}"
|
343
|
-
end
|
344
|
-
end
|
345
|
-
|
346
|
-
[:normative, :informative].each do |k|
|
347
|
-
ps.rest[k.to_s] ||= { }
|
348
|
-
end
|
349
|
-
|
350
|
-
norm_ref.each do |k, v|
|
351
|
-
# could check bibtagsys here: needed if open_refs is nil or string
|
352
|
-
target = ps.has(v ? :normative : :informative)
|
353
|
-
warn "*** overwriting #{k}" if target.has_key?(k)
|
354
|
-
target[k] = open_refs[k] # add reference to normative/informative
|
355
|
-
end
|
356
|
-
# note that unused items from ref are considered OK, therefore no check for that here
|
357
|
-
|
358
|
-
# also should allow norm/inform check of other references
|
359
|
-
# {{?coap}} vs. {{!coap}} vs. {{-coap}} (undecided)
|
360
|
-
# or {{?-coap}} vs. {{!-coap}} vs. {{-coap}} (undecided)
|
361
|
-
# could require all references to be decided by a global flag
|
362
|
-
overlap = [:normative, :informative].map { |s| (ps.has(s) || { }).keys }.reduce(:&)
|
363
|
-
unless overlap.empty?
|
364
|
-
warn "*** #{overlap.join(', ')}: both normative and informative"
|
365
|
-
end
|
366
|
-
|
367
|
-
stand_alone = ps[:stand_alone]
|
368
|
-
|
369
|
-
[:normative, :informative].each do |sn|
|
370
|
-
if refs = ps[sn]
|
371
|
-
refs.each do |k, v|
|
372
|
-
href = ::Kramdown::Parser::RFC2629Kramdown.idref_cleanup(k)
|
373
|
-
kramdown_options[:link_defs][k] = ["##{href}", nil] # allow [RFC2119] in addition to {{RFC2119}}
|
374
|
-
|
375
|
-
bibref = anchor_to_bibref[k] || k
|
376
|
-
bts, url = bibtagsys(bibref, k, stand_alone)
|
377
|
-
if bts && (!v || v == {} || v.respond_to?(:to_str))
|
378
|
-
if stand_alone
|
379
|
-
a = %{{: anchor="#{k}"}}
|
380
|
-
sechash[sn.to_s] << %{\n#{NMDTAGS[0]}\n#{a}\n#{NMDTAGS[1]}\n}
|
381
|
-
else
|
382
|
-
bts.gsub!('/', '_')
|
383
|
-
(ps.rest["bibxml"] ||= []) << [bts, url]
|
384
|
-
sechash[sn.to_s] << %{&#{bts};\n} # ???
|
385
|
-
end
|
386
|
-
else
|
387
|
-
unless v && Hash === v
|
388
|
-
warn "*** don't know how to expand ref #{k}"
|
389
|
-
next
|
390
|
-
end
|
391
|
-
if bts && !v.delete("override")
|
392
|
-
warn "*** warning: explicit settings completely override canned bibxml in reference #{k}"
|
393
|
-
end
|
394
|
-
sechash[sn.to_s] << KramdownRFC::ref_to_xml(href, v)
|
395
|
-
end
|
396
|
-
end
|
397
|
-
end
|
398
|
-
end
|
399
|
-
|
400
|
-
erbfilename = File.expand_path '../../data/kramdown-rfc2629.erb', __FILE__
|
401
|
-
erbfile = File.read(erbfilename, coding: "UTF-8")
|
402
|
-
erb = ERB.trim_new(erbfile, '-')
|
403
|
-
# remove redundant nomarkdown pop outs/pop ins as they confuse kramdown
|
404
|
-
input = erb.result(binding).gsub(%r"{::nomarkdown}\s*{:/nomarkdown}"m, "")
|
405
|
-
ps.warn_if_leftovers
|
406
|
-
sechash.delete("fluff") # fluff is a "commented out" section
|
407
|
-
if !sechash.empty? # any sections unused by the ERb file?
|
408
|
-
warn "*** sections left #{sechash.keys.inspect}!"
|
409
|
-
end
|
410
|
-
|
411
|
-
[input, kramdown_options, coding_override]
|
412
|
-
end
|
413
|
-
|
414
|
-
XML_RESOURCE_ORG_PREFIX = Kramdown::Converter::Rfc2629::XML_RESOURCE_ORG_PREFIX
|
415
|
-
|
416
|
-
# return XML entity name, url, rewrite_anchor flag
|
417
|
-
def bibtagsys(bib, anchor=nil, stand_alone=true)
|
418
|
-
if bib =~ /\Arfc(\d+)/i
|
419
|
-
rfc4d = "%04d" % $1.to_i
|
420
|
-
[bib.upcase,
|
421
|
-
"#{XML_RESOURCE_ORG_PREFIX}/bibxml/reference.RFC.#{rfc4d}.xml"]
|
422
|
-
elsif $options.v3 && bib =~ /\A(bcp|std)(\d+)/i
|
423
|
-
n4d = "%04d" % $2.to_i
|
424
|
-
[bib.upcase,
|
425
|
-
"#{XML_RESOURCE_ORG_PREFIX}/bibxml-rfcsubseries-new/reference.#{$1.upcase}.#{n4d}.xml"]
|
426
|
-
elsif bib =~ /\A([-A-Z0-9]+)\./ &&
|
427
|
-
(xro = Kramdown::Converter::Rfc2629::XML_RESOURCE_ORG_MAP[$1])
|
428
|
-
dir, _ttl, rewrite_anchor = xro
|
429
|
-
bib1 = ::Kramdown::Parser::RFC2629Kramdown.idref_cleanup(bib)
|
430
|
-
if anchor && bib1 != anchor
|
431
|
-
if rewrite_anchor
|
432
|
-
a = %{?anchor=#{anchor}}
|
433
|
-
else
|
434
|
-
if !stand_alone
|
435
|
-
warn "*** selecting a custom anchor '#{anchor}' for '#{bib1}' requires stand_alone mode"
|
436
|
-
warn " the output will need manual editing to correct this"
|
437
|
-
end
|
438
|
-
end
|
439
|
-
end
|
440
|
-
[bib1,
|
441
|
-
"#{XML_RESOURCE_ORG_PREFIX}/#{dir}/reference.#{bib}.xml#{a}"]
|
442
|
-
end
|
443
|
-
end
|
444
|
-
|
445
|
-
def read_encodings
|
446
|
-
encfilename = File.expand_path '../../data/encoding-fallbacks.txt', __FILE__
|
447
|
-
encfile = File.read(encfilename, coding: "UTF-8")
|
448
|
-
Hash[encfile.lines.map{|l|
|
449
|
-
l.chomp!;
|
450
|
-
x, s = l.split(" ", 2)
|
451
|
-
[x.hex.chr(Encoding::UTF_8), s || " "]}]
|
452
|
-
end
|
453
|
-
|
454
|
-
FALLBACK = read_encodings
|
455
|
-
|
456
|
-
def expand_tabs(s, tab_stops = 8)
|
457
|
-
s.gsub(/([^\t\n]*)\t/) do
|
458
|
-
$1 + " " * (tab_stops - ($1.size % tab_stops))
|
459
|
-
end
|
460
|
-
end
|
461
|
-
|
462
|
-
|
463
|
-
require 'optparse'
|
464
|
-
require 'ostruct'
|
465
|
-
|
466
|
-
$options = OpenStruct.new
|
467
|
-
op = OptionParser.new do |opts|
|
468
|
-
opts.banner = <<BANNER
|
469
|
-
Usage: kramdown-rfc2629 [options] file.md|file.mkd > file.xml
|
470
|
-
Version: #{KDRFC_VERSION}
|
471
|
-
BANNER
|
472
|
-
opts.on("-V", "--version", "Show version and exit") do |v|
|
473
|
-
puts "kramdown-rfc2629 #{KDRFC_VERSION}"
|
474
|
-
exit
|
475
|
-
end
|
476
|
-
opts.on("-H", "--help", "Show option summary and exit") do |v|
|
477
|
-
puts opts
|
478
|
-
exit
|
479
|
-
end
|
480
|
-
opts.on("-v", "--[no-]verbose", "Run verbosely") do |v|
|
481
|
-
$options.verbose = v
|
482
|
-
end
|
483
|
-
opts.on("-3", "--[no-]v3", "Use RFCXML v3 processing rules") do |v|
|
484
|
-
$options.v3 = v
|
485
|
-
end
|
486
|
-
end
|
487
|
-
op.parse!
|
488
|
-
|
489
|
-
if $options.verbose && $options.v3
|
490
|
-
warn "*** not much RFCXMLv3 stuff implemented yet"
|
491
|
-
end
|
492
|
-
|
493
|
-
input = ARGF.read
|
494
|
-
if input[0] == "\uFEFF"
|
495
|
-
warn "*** There is a leading byte order mark. Ignored."
|
496
|
-
input[0..0] = ''
|
497
|
-
end
|
498
|
-
if input[-1] != "\n"
|
499
|
-
# warn "*** added missing newline at end"
|
500
|
-
input << "\n" # fix #26
|
501
|
-
end
|
502
|
-
process_includes(input) unless ENV["KRAMDOWN_SAFE"]
|
503
|
-
input.gsub!(/^\{::boilerplate\s+(.*?)\}/) {
|
504
|
-
boilerplate($1)
|
505
|
-
}
|
506
|
-
if input =~ /[\t]/
|
507
|
-
warn "*** Input contains HT (\"tab\") characters. Undefined behavior will ensue."
|
508
|
-
input = expand_tabs(input)
|
509
|
-
end
|
510
|
-
|
511
|
-
if input =~ /\A---/ # this is a sectionized file
|
512
|
-
do_the_tls_dance unless ENV["KRAMDOWN_DONT_VERIFY_HTTPS"]
|
513
|
-
input, options, coding_override = xml_from_sections(input)
|
514
|
-
else
|
515
|
-
options = process_kramdown_options # all default
|
516
|
-
end
|
517
|
-
if input =~ /\A<\?xml/ # if this is a whole XML file, protect it
|
518
|
-
input = "{::nomarkdown}\n#{input}\n{:/nomarkdown}\n"
|
519
|
-
end
|
520
|
-
|
521
|
-
if coding_override
|
522
|
-
input = input.encode(Encoding.find(coding_override), fallback: FALLBACK)
|
523
|
-
end
|
524
|
-
|
525
|
-
# 1.4.17: because of UTF-8 bibxml files, kramdown always needs to see UTF-8 (!)
|
526
|
-
if input.encoding != Encoding::UTF_8
|
527
|
-
input = input.encode(Encoding::UTF_8)
|
528
|
-
end
|
529
|
-
|
530
|
-
# warn "options: #{options.inspect}"
|
531
|
-
doc = Kramdown::Document.new(input, options)
|
532
|
-
$stderr.puts doc.warnings.to_yaml unless doc.warnings.empty?
|
533
|
-
output = doc.to_rfc2629
|
534
|
-
|
535
|
-
if coding_override
|
536
|
-
output = output.encode(Encoding.find(coding_override), fallback: FALLBACK)
|
537
|
-
end
|
538
|
-
|
539
|
-
puts output
|
data/kramdown-rfc2629.gemspec
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
spec = Gem::Specification.new do |s|
|
2
2
|
s.name = 'kramdown-rfc2629'
|
3
|
-
s.version = '1.
|
3
|
+
s.version = '1.6.1'
|
4
4
|
s.summary = "Kramdown extension for generating RFC 7749 XML."
|
5
5
|
s.description = %{An RFC7749 (XML2RFC) generating backend for Thomas Leitner's
|
6
6
|
"kramdown" markdown parser. Mostly useful for RFC writers.}
|
@@ -8,9 +8,9 @@ spec = Gem::Specification.new do |s|
|
|
8
8
|
s.add_dependency('kramdown-parser-gfm', '~> 1.1')
|
9
9
|
s.add_dependency('certified', '~> 1.0')
|
10
10
|
s.add_dependency('json_pure', '~> 2.0')
|
11
|
-
s.files = Dir['lib/**/*.rb'] + %w(README.md LICENSE kramdown-rfc2629.gemspec bin/kdrfc bin/kramdown-rfc2629 bin/doilit bin/kramdown-rfc-extract-markdown data/kramdown-rfc2629.erb data/encoding-fallbacks.txt data/math.json bin/kramdown-rfc-cache-subseries-bibxml bin/de-gfm)
|
11
|
+
s.files = Dir['lib/**/*.rb'] + %w(README.md LICENSE kramdown-rfc2629.gemspec bin/kdrfc bin/kramdown-rfc bin/kramdown-rfc2629 bin/doilit bin/kramdown-rfc-extract-markdown data/kramdown-rfc2629.erb data/encoding-fallbacks.txt data/math.json bin/kramdown-rfc-cache-subseries-bibxml bin/de-gfm)
|
12
12
|
s.require_path = 'lib'
|
13
|
-
s.executables = ['kramdown-rfc2629', 'doilit', 'kramdown-rfc-extract-markdown',
|
13
|
+
s.executables = ['kramdown-rfc', 'kramdown-rfc2629', 'doilit', 'kramdown-rfc-extract-markdown',
|
14
14
|
'kdrfc', 'kramdown-rfc-cache-i-d-bibxml',
|
15
15
|
'kramdown-rfc-cache-subseries-bibxml', 'de-gfm']
|
16
16
|
s.required_ruby_version = '>= 2.3.0'
|
@@ -0,0 +1,572 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# -*- coding: utf-8 -*-
|
3
|
+
require 'kramdown-rfc2629'
|
4
|
+
require 'kramdown-rfc/parameterset'
|
5
|
+
require 'kramdown-rfc/refxml'
|
6
|
+
require 'kramdown-rfc/rfc8792'
|
7
|
+
require 'yaml'
|
8
|
+
require 'kramdown-rfc/erb'
|
9
|
+
require 'date'
|
10
|
+
|
11
|
+
# try to get this from gemspec.
|
12
|
+
KDRFC_VERSION=Gem.loaded_specs["kramdown-rfc2629"].version rescue "unknown-version"
|
13
|
+
|
14
|
+
Encoding.default_external = "UTF-8" # wake up, smell the coffee
|
15
|
+
|
16
|
+
# Note that this doesn't attempt to handle HT characters
|
17
|
+
def remove_indentation(s)
|
18
|
+
l = s.lines
|
19
|
+
indent = l.grep(/\S/).map {|l| l[/^\s*/].size}.min
|
20
|
+
l.map {|li| li.sub(/^ {0,#{indent}}/, "")}.join
|
21
|
+
end
|
22
|
+
|
23
|
+
def add_quote(s)
|
24
|
+
l = s.lines
|
25
|
+
l.map {|li| "> #{li}"}.join
|
26
|
+
end
|
27
|
+
|
28
|
+
def process_chunk(s, nested, dedent, fold, quote)
|
29
|
+
process_includes(s) if nested
|
30
|
+
s = remove_indentation(s) if dedent
|
31
|
+
s = fold8792_1(s, *fold) if fold
|
32
|
+
s = add_quote(s) if quote
|
33
|
+
s
|
34
|
+
end
|
35
|
+
|
36
|
+
def process_includes(input)
|
37
|
+
input.gsub!(/^\{::include((?:-[a-z0-9]+)*)\s+(.*?)\}/) {
|
38
|
+
include_flags = $1
|
39
|
+
fn = [$2]
|
40
|
+
chunks = false
|
41
|
+
nested = false
|
42
|
+
dedent = false
|
43
|
+
fold = false
|
44
|
+
quote = false
|
45
|
+
include_flags.split("-") do |flag|
|
46
|
+
case flag
|
47
|
+
when ""
|
48
|
+
when "nested"
|
49
|
+
nested = true
|
50
|
+
when "quote"
|
51
|
+
quote = true
|
52
|
+
when "dedent"
|
53
|
+
dedent = true
|
54
|
+
when /\Afold(\d*)(left(\d*))?(dry)?\z/
|
55
|
+
fold = [$1.to_i, # col 0 for ''
|
56
|
+
($3.to_i if $2), # left 0 for '', nil if no "left"
|
57
|
+
$4] # dry
|
58
|
+
when "all", "last"
|
59
|
+
fn = fn.flat_map{|n| Dir[n]}
|
60
|
+
fn = [fn.last] if flag == "last"
|
61
|
+
chunks = fn.map{ |f|
|
62
|
+
ret = process_chunk(File.read(f), nested, dedent, fold, quote)
|
63
|
+
nested = false; dedent = false; fold = false; quote = false
|
64
|
+
ret
|
65
|
+
}
|
66
|
+
else
|
67
|
+
warn "** unknown include flag #{flag}"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
chunks = fn.map{|f| File.read(f)} unless chunks # no all/last
|
71
|
+
chunks = chunks.map {|ch| process_chunk(ch, nested, dedent, fold, quote)}
|
72
|
+
chunks.join.chomp
|
73
|
+
}
|
74
|
+
end
|
75
|
+
|
76
|
+
|
77
|
+
def boilerplate(key)
|
78
|
+
case key.downcase
|
79
|
+
when /\Abcp14(info)?(\+)?(-tagged)?\z/i
|
80
|
+
ret = ''
|
81
|
+
if $1
|
82
|
+
ret << <<RFC8174ise
|
83
|
+
Although this document is not an IETF Standards Track publication, it
|
84
|
+
adopts the conventions for normative language to provide clarity of
|
85
|
+
instructions to the implementer.
|
86
|
+
RFC8174ise
|
87
|
+
end
|
88
|
+
ret << <<RFC8174
|
89
|
+
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL
|
90
|
+
NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED",
|
91
|
+
"MAY", and "OPTIONAL" in this document are to be interpreted as
|
92
|
+
described in BCP 14 {{!RFC2119}} {{!RFC8174}} when, and only when, they
|
93
|
+
appear in all capitals, as shown here.
|
94
|
+
RFC8174
|
95
|
+
if $2
|
96
|
+
ret << <<PLUS
|
97
|
+
These words may also appear in this document in
|
98
|
+
lower case as plain English words, absent their normative meanings.
|
99
|
+
PLUS
|
100
|
+
end
|
101
|
+
if $3
|
102
|
+
($options.v3_used ||= []) << "** need --v3 to tag bcp14"
|
103
|
+
ret << <<TAGGED
|
104
|
+
|
105
|
+
*[MUST]: <bcp14>
|
106
|
+
*[MUST NOT]: <bcp14>
|
107
|
+
*[REQUIRED]: <bcp14>
|
108
|
+
*[SHALL]: <bcp14>
|
109
|
+
*[SHALL NOT]: <bcp14>
|
110
|
+
*[SHOULD]: <bcp14>
|
111
|
+
*[SHOULD NOT]: <bcp14>
|
112
|
+
*[RECOMMENDED]: <bcp14>
|
113
|
+
*[NOT RECOMMENDED]: <bcp14>
|
114
|
+
*[MAY]: <bcp14>
|
115
|
+
*[OPTIONAL]: <bcp14>
|
116
|
+
TAGGED
|
117
|
+
end
|
118
|
+
ret
|
119
|
+
else
|
120
|
+
warn "** Unknwon boilerplate key: #{key}"
|
121
|
+
"{::boilerplate #{key}}"
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def do_the_tls_dance
|
126
|
+
begin
|
127
|
+
require 'openssl'
|
128
|
+
File.open(OpenSSL::X509::DEFAULT_CERT_FILE) do end
|
129
|
+
# This guards against having an unreadable cert file (yes, that appears to happen a lot).
|
130
|
+
rescue
|
131
|
+
if Dir[File.join(OpenSSL::X509::DEFAULT_CERT_DIR, "*.pem")].empty?
|
132
|
+
# This guards against having no certs at all, not against missing the right one for IETF.
|
133
|
+
# Oh well.
|
134
|
+
warn "** Configuration problem with OpenSSL certificate store."
|
135
|
+
warn "** You may want to examine #{OpenSSL::X509::DEFAULT_CERT_FILE}"
|
136
|
+
warn "** and #{OpenSSL::X509::DEFAULT_CERT_DIR}."
|
137
|
+
warn "** Activating suboptimal workaround."
|
138
|
+
warn "** Occasionally run `certified-update` to maintain that workaround."
|
139
|
+
require 'certified'
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
RE_NL = /(?:\n|\r|\r\n)/
|
145
|
+
RE_SECTION = /---(?: +(\w+)(-?))?\s*#{RE_NL}(.*?#{RE_NL})(?=---(?:\s+\w+-?)?\s*#{RE_NL}|\Z)/m
|
146
|
+
|
147
|
+
NMDTAGS = ["{:/nomarkdown}\n\n", "\n\n{::nomarkdown}\n"]
|
148
|
+
|
149
|
+
NORMINFORM = { "!" => :normative, "?" => :informative }
|
150
|
+
|
151
|
+
def yaml_load(input, *args)
|
152
|
+
if YAML.respond_to?(:safe_load)
|
153
|
+
begin
|
154
|
+
YAML.safe_load(input, *args)
|
155
|
+
rescue ArgumentError
|
156
|
+
YAML.safe_load(input, permitted_classes: args[0], permitted_symbols: args[1], aliases: args[2])
|
157
|
+
end
|
158
|
+
else
|
159
|
+
YAML.load(input)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
def process_kramdown_options(coding_override = nil,
|
164
|
+
smart_quotes = nil, typographic_symbols = nil,
|
165
|
+
header_kramdown_options = nil)
|
166
|
+
|
167
|
+
ascii_target = coding_override && coding_override =~ /ascii/
|
168
|
+
suppress_typography = ascii_target || $options.v3
|
169
|
+
entity_output = ascii_target ? :numeric : :as_char;
|
170
|
+
|
171
|
+
options = {input: 'RFC2629Kramdown', entity_output: entity_output, link_defs: {}}
|
172
|
+
|
173
|
+
if smart_quotes.nil? && suppress_typography
|
174
|
+
smart_quotes = false
|
175
|
+
end
|
176
|
+
if smart_quotes == false
|
177
|
+
smart_quotes = ["'".ord, "'".ord, '"'.ord, '"'.ord]
|
178
|
+
end
|
179
|
+
case smart_quotes
|
180
|
+
when Array
|
181
|
+
options[:smart_quotes] = smart_quotes
|
182
|
+
when nil, true
|
183
|
+
# nothin
|
184
|
+
else
|
185
|
+
warn "*** Can't deal with smart_quotes value #{smart_quotes.inspect}"
|
186
|
+
end
|
187
|
+
|
188
|
+
if typographic_symbols.nil? && suppress_typography
|
189
|
+
typographic_symbols = false
|
190
|
+
end
|
191
|
+
if typographic_symbols == false
|
192
|
+
typographic_symbols = Hash[::Kramdown::Parser::Kramdown::TYPOGRAPHIC_SYMS.map { |k, v|
|
193
|
+
if Symbol === v
|
194
|
+
[v.intern, k]
|
195
|
+
end
|
196
|
+
}.compact]
|
197
|
+
end
|
198
|
+
# warn [:TYPOGRAPHIC_SYMBOLS, typographic_symbols].to_yaml
|
199
|
+
case typographic_symbols
|
200
|
+
when Hash
|
201
|
+
options[:typographic_symbols] = typographic_symbols
|
202
|
+
when nil, true
|
203
|
+
# nothin
|
204
|
+
else
|
205
|
+
warn "*** Can't deal with typographic_symbols value #{typographic_symbols.inspect}"
|
206
|
+
end
|
207
|
+
|
208
|
+
if header_kramdown_options
|
209
|
+
options.merge! header_kramdown_options
|
210
|
+
end
|
211
|
+
|
212
|
+
$global_markdown_options = options # For nested calls in bibref annotation processing and xref text
|
213
|
+
|
214
|
+
options
|
215
|
+
end
|
216
|
+
|
217
|
+
XREF_SECTIONS_RE = ::Kramdown::Parser::RFC2629Kramdown::SECTIONS_RE
|
218
|
+
XSR_PREFIX = "#{XREF_SECTIONS_RE} of "
|
219
|
+
XSR_SUFFIX = ", (#{XREF_SECTIONS_RE})| \\((#{XREF_SECTIONS_RE})\\)"
|
220
|
+
XREF_TXT = ::Kramdown::Parser::RFC2629Kramdown::XREF_TXT
|
221
|
+
XREF_TXT_SUFFIX = " \\(#{XREF_TXT}\\)"
|
222
|
+
|
223
|
+
def spacify_re(s)
|
224
|
+
s.gsub(' ', '[\u00A0\s]+')
|
225
|
+
end
|
226
|
+
|
227
|
+
def xml_from_sections(input)
|
228
|
+
|
229
|
+
unless ENV["KRAMDOWN_NO_SOURCE"]
|
230
|
+
require 'kramdown-rfc/gzip-clone'
|
231
|
+
require 'base64'
|
232
|
+
compressed_input = Gzip.compress(input)
|
233
|
+
$source = Base64.encode64(compressed_input)
|
234
|
+
end
|
235
|
+
|
236
|
+
sections = input.scan(RE_SECTION)
|
237
|
+
# resulting in an array; each section is [section-label, nomarkdown-flag, section-text]
|
238
|
+
|
239
|
+
# the first section is a YAML with front matter parameters (don't put a label here)
|
240
|
+
# We put back the "---" plus gratuitous blank lines to hack the line number in errors
|
241
|
+
yaml_in = input[/---\s*/] << sections.shift[2]
|
242
|
+
ps = KramdownRFC::ParameterSet.new(yaml_load(yaml_in, [Date], [], true))
|
243
|
+
|
244
|
+
if v = ps[:v]
|
245
|
+
warn "*** unsupported RFCXML version #{v}" if v != 3
|
246
|
+
if $options.v2
|
247
|
+
warn "*** command line --v2 wins over document's 'v: #{v}'"
|
248
|
+
else
|
249
|
+
$options.v3 = true
|
250
|
+
$options.v = 3
|
251
|
+
ps.default!(:stand_alone, true)
|
252
|
+
ps.default!(:ipr, "trust200902")
|
253
|
+
ps.default!(:pi, {"toc" => true, "sortrefs" => true, "symrefs" => true})
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
coding_override = ps.has(:coding)
|
258
|
+
smart_quotes = ps[:smart_quotes]
|
259
|
+
typographic_symbols = ps[:typographic_symbols]
|
260
|
+
header_kramdown_options = ps[:kramdown_options]
|
261
|
+
|
262
|
+
kramdown_options = process_kramdown_options(coding_override,
|
263
|
+
smart_quotes, typographic_symbols,
|
264
|
+
header_kramdown_options)
|
265
|
+
|
266
|
+
# all the other sections are put in a Hash, possibly concatenated from parts there
|
267
|
+
sechash = Hash.new{ |h,k| h[k] = ""}
|
268
|
+
snames = [] # a stack of section names
|
269
|
+
sections.each do |sname, nmdflag, text|
|
270
|
+
# warn [:SNAME, sname, nmdflag, text[0..10]].inspect
|
271
|
+
nmdin, nmdout = {
|
272
|
+
"-" => ["", ""], # stay in nomarkdown
|
273
|
+
"" => NMDTAGS, # pop out temporarily
|
274
|
+
}[nmdflag || ""]
|
275
|
+
if sname
|
276
|
+
snames << sname # "--- label" -> push label (now current)
|
277
|
+
else
|
278
|
+
snames.pop # just "---" -> pop label (previous now current)
|
279
|
+
end
|
280
|
+
sechash[snames.last] << "#{nmdin}#{text}#{nmdout}"
|
281
|
+
end
|
282
|
+
|
283
|
+
ref_replacements = { }
|
284
|
+
anchor_to_bibref = { }
|
285
|
+
|
286
|
+
[:ref, :normative, :informative].each do |sn|
|
287
|
+
if refs = ps.has(sn)
|
288
|
+
warn "*** bad section #{sn}: #{refs.inspect}" unless refs.respond_to? :each
|
289
|
+
refs.each do |k, v|
|
290
|
+
if v.respond_to? :to_str
|
291
|
+
if bibtagsys(v) # enable "foo: RFC4711" as a custom anchor definition
|
292
|
+
anchor_to_bibref[k] = v.to_str
|
293
|
+
end
|
294
|
+
ref_replacements[v.to_str] = k
|
295
|
+
end
|
296
|
+
if Hash === v
|
297
|
+
if aliasname = v.delete("-")
|
298
|
+
ref_replacements[aliasname] = k
|
299
|
+
end
|
300
|
+
if bibref = v.delete("=")
|
301
|
+
anchor_to_bibref[k] = bibref
|
302
|
+
end
|
303
|
+
end
|
304
|
+
end
|
305
|
+
end
|
306
|
+
end
|
307
|
+
open_refs = ps[:ref] || { } # consumed
|
308
|
+
|
309
|
+
norm_ref = { }
|
310
|
+
|
311
|
+
# convenience replacement of {{-coap}} with {{I-D.ietf-core-coap}}
|
312
|
+
# collect normative/informative tagging {{!RFC2119}} {{?RFC4711}}
|
313
|
+
sechash.each do |k, v|
|
314
|
+
next if k == "fluff"
|
315
|
+
v.gsub!(/{{(#{
|
316
|
+
spacify_re(XSR_PREFIX)
|
317
|
+
})?(?:([?!])(-)?|(-))([\w._\-]+)(?:=([\w.\/_\-]+))?(#{
|
318
|
+
XREF_TXT_SUFFIX
|
319
|
+
})?(#{
|
320
|
+
spacify_re(XSR_SUFFIX)
|
321
|
+
})?}}/) do |match|
|
322
|
+
xsr_prefix = $1
|
323
|
+
norminform = $2
|
324
|
+
replacing = $3 || $4
|
325
|
+
word = $5
|
326
|
+
bibref = $6
|
327
|
+
xrt_suffix = $7
|
328
|
+
xsr_suffix = $8
|
329
|
+
if replacing
|
330
|
+
if new = ref_replacements[word]
|
331
|
+
word = new
|
332
|
+
else
|
333
|
+
warn "*** no alias replacement for {{-#{word}}}"
|
334
|
+
word = "-#{word}"
|
335
|
+
end
|
336
|
+
end # now, word is the anchor
|
337
|
+
if bibref
|
338
|
+
if old = anchor_to_bibref[word]
|
339
|
+
if bibref != old
|
340
|
+
warn "*** conflicting definitions for xref #{word}: #{old} != #{bibref}"
|
341
|
+
end
|
342
|
+
else
|
343
|
+
anchor_to_bibref[word] = bibref
|
344
|
+
end
|
345
|
+
end
|
346
|
+
|
347
|
+
# things can be normative in one place and informative in another -> normative
|
348
|
+
# collect norm/inform above and assign it by priority here
|
349
|
+
if norminform
|
350
|
+
norm_ref[word] ||= norminform == '!' # one normative ref is enough
|
351
|
+
end
|
352
|
+
"{{#{xsr_prefix}#{word}#{xrt_suffix}#{xsr_suffix}}}"
|
353
|
+
end
|
354
|
+
end
|
355
|
+
|
356
|
+
[:normative, :informative].each do |k|
|
357
|
+
ps.rest[k.to_s] ||= { }
|
358
|
+
end
|
359
|
+
|
360
|
+
norm_ref.each do |k, v|
|
361
|
+
# could check bibtagsys here: needed if open_refs is nil or string
|
362
|
+
target = ps.has(v ? :normative : :informative)
|
363
|
+
warn "*** overwriting #{k}" if target.has_key?(k)
|
364
|
+
target[k] = open_refs[k] # add reference to normative/informative
|
365
|
+
end
|
366
|
+
# note that unused items from ref are considered OK, therefore no check for that here
|
367
|
+
|
368
|
+
# also should allow norm/inform check of other references
|
369
|
+
# {{?coap}} vs. {{!coap}} vs. {{-coap}} (undecided)
|
370
|
+
# or {{?-coap}} vs. {{!-coap}} vs. {{-coap}} (undecided)
|
371
|
+
# could require all references to be decided by a global flag
|
372
|
+
overlap = [:normative, :informative].map { |s| (ps.has(s) || { }).keys }.reduce(:&)
|
373
|
+
unless overlap.empty?
|
374
|
+
warn "*** #{overlap.join(', ')}: both normative and informative"
|
375
|
+
end
|
376
|
+
|
377
|
+
stand_alone = ps[:stand_alone]
|
378
|
+
|
379
|
+
[:normative, :informative].each do |sn|
|
380
|
+
if refs = ps[sn]
|
381
|
+
refs.each do |k, v|
|
382
|
+
href = ::Kramdown::Parser::RFC2629Kramdown.idref_cleanup(k)
|
383
|
+
kramdown_options[:link_defs][k] = ["##{href}", nil] # allow [RFC2119] in addition to {{RFC2119}}
|
384
|
+
|
385
|
+
bibref = anchor_to_bibref[k] || k
|
386
|
+
bts, url = bibtagsys(bibref, k, stand_alone)
|
387
|
+
if bts && (!v || v == {} || v.respond_to?(:to_str))
|
388
|
+
if stand_alone
|
389
|
+
a = %{{: anchor="#{k}"}}
|
390
|
+
sechash[sn.to_s] << %{\n#{NMDTAGS[0]}\n#{a}\n#{NMDTAGS[1]}\n}
|
391
|
+
else
|
392
|
+
bts.gsub!('/', '_')
|
393
|
+
(ps.rest["bibxml"] ||= []) << [bts, url]
|
394
|
+
sechash[sn.to_s] << %{&#{bts};\n} # ???
|
395
|
+
end
|
396
|
+
else
|
397
|
+
unless v && Hash === v
|
398
|
+
warn "*** don't know how to expand ref #{k}"
|
399
|
+
next
|
400
|
+
end
|
401
|
+
if bts && !v.delete("override")
|
402
|
+
warn "*** warning: explicit settings completely override canned bibxml in reference #{k}"
|
403
|
+
end
|
404
|
+
sechash[sn.to_s] << KramdownRFC::ref_to_xml(href, v)
|
405
|
+
end
|
406
|
+
end
|
407
|
+
end
|
408
|
+
end
|
409
|
+
|
410
|
+
erbfilename = File.expand_path '../../../data/kramdown-rfc2629.erb', __FILE__
|
411
|
+
erbfile = File.read(erbfilename, coding: "UTF-8")
|
412
|
+
erb = ERB.trim_new(erbfile, '-')
|
413
|
+
# remove redundant nomarkdown pop outs/pop ins as they confuse kramdown
|
414
|
+
input = erb.result(binding).gsub(%r"{::nomarkdown}\s*{:/nomarkdown}"m, "")
|
415
|
+
ps.warn_if_leftovers
|
416
|
+
sechash.delete("fluff") # fluff is a "commented out" section
|
417
|
+
if !sechash.empty? # any sections unused by the ERb file?
|
418
|
+
warn "*** sections left #{sechash.keys.inspect}!"
|
419
|
+
end
|
420
|
+
|
421
|
+
[input, kramdown_options, coding_override]
|
422
|
+
end
|
423
|
+
|
424
|
+
XML_RESOURCE_ORG_PREFIX = Kramdown::Converter::Rfc2629::XML_RESOURCE_ORG_PREFIX
|
425
|
+
|
426
|
+
# return XML entity name, url, rewrite_anchor flag
|
427
|
+
def bibtagsys(bib, anchor=nil, stand_alone=true)
|
428
|
+
if bib =~ /\Arfc(\d+)/i
|
429
|
+
rfc4d = "%04d" % $1.to_i
|
430
|
+
[bib.upcase,
|
431
|
+
"#{XML_RESOURCE_ORG_PREFIX}/bibxml/reference.RFC.#{rfc4d}.xml"]
|
432
|
+
elsif $options.v3 && bib =~ /\A(bcp|std)(\d+)/i
|
433
|
+
n4d = "%04d" % $2.to_i
|
434
|
+
[bib.upcase,
|
435
|
+
"#{XML_RESOURCE_ORG_PREFIX}/bibxml-rfcsubseries-new/reference.#{$1.upcase}.#{n4d}.xml"]
|
436
|
+
elsif bib =~ /\A([-A-Z0-9]+)\./ &&
|
437
|
+
(xro = Kramdown::Converter::Rfc2629::XML_RESOURCE_ORG_MAP[$1])
|
438
|
+
dir, _ttl, rewrite_anchor = xro
|
439
|
+
bib1 = ::Kramdown::Parser::RFC2629Kramdown.idref_cleanup(bib)
|
440
|
+
if anchor && bib1 != anchor
|
441
|
+
if rewrite_anchor
|
442
|
+
a = %{?anchor=#{anchor}}
|
443
|
+
else
|
444
|
+
if !stand_alone
|
445
|
+
warn "*** selecting a custom anchor '#{anchor}' for '#{bib1}' requires stand_alone mode"
|
446
|
+
warn " the output will need manual editing to correct this"
|
447
|
+
end
|
448
|
+
end
|
449
|
+
end
|
450
|
+
[bib1,
|
451
|
+
"#{XML_RESOURCE_ORG_PREFIX}/#{dir}/reference.#{bib}.xml#{a}"]
|
452
|
+
end
|
453
|
+
end
|
454
|
+
|
455
|
+
def read_encodings
|
456
|
+
encfilename = File.expand_path '../../../data/encoding-fallbacks.txt', __FILE__
|
457
|
+
encfile = File.read(encfilename, coding: "UTF-8")
|
458
|
+
Hash[encfile.lines.map{|l|
|
459
|
+
l.chomp!;
|
460
|
+
x, s = l.split(" ", 2)
|
461
|
+
[x.hex.chr(Encoding::UTF_8), s || " "]}]
|
462
|
+
end
|
463
|
+
|
464
|
+
FALLBACK = read_encodings
|
465
|
+
|
466
|
+
def expand_tabs(s, tab_stops = 8)
|
467
|
+
s.gsub(/([^\t\n]*)\t/) do
|
468
|
+
$1 + " " * (tab_stops - ($1.size % tab_stops))
|
469
|
+
end
|
470
|
+
end
|
471
|
+
|
472
|
+
|
473
|
+
require 'optparse'
|
474
|
+
require 'ostruct'
|
475
|
+
|
476
|
+
$options ||= OpenStruct.new
|
477
|
+
op = OptionParser.new do |opts|
|
478
|
+
opts.banner = <<BANNER
|
479
|
+
Usage: kramdown-rfc2629 [options] file.md|file.mkd > file.xml
|
480
|
+
Version: #{KDRFC_VERSION}
|
481
|
+
BANNER
|
482
|
+
opts.on("-V", "--version", "Show version and exit") do |v|
|
483
|
+
puts "kramdown-rfc2629 #{KDRFC_VERSION}"
|
484
|
+
exit
|
485
|
+
end
|
486
|
+
opts.on("-H", "--help", "Show option summary and exit") do |v|
|
487
|
+
puts opts
|
488
|
+
exit
|
489
|
+
end
|
490
|
+
opts.on("-v", "--[no-]verbose", "Run verbosely") do |v|
|
491
|
+
$options.verbose = v
|
492
|
+
end
|
493
|
+
opts.on("-3", "--[no-]v3", "Use RFCXML v3 processing rules") do |v|
|
494
|
+
$options.v3 = v
|
495
|
+
end
|
496
|
+
opts.on("-2", "--[no-]v2", "Use RFCXML v2 processing rules") do |v|
|
497
|
+
$options.v2 = v
|
498
|
+
end
|
499
|
+
end
|
500
|
+
op.parse!
|
501
|
+
|
502
|
+
if $options.v2 && $options.v3
|
503
|
+
warn "*** can't have v2 and eat v3 cake"
|
504
|
+
$options.v2 = false
|
505
|
+
end
|
506
|
+
|
507
|
+
if $options.v3.nil? && !$options.v2
|
508
|
+
if Time.now.to_i >= 1645567342 # Time.parse("2022-02-22T22:02:22Z").to_i
|
509
|
+
$options.v3 = true # new default from the above date
|
510
|
+
end
|
511
|
+
end
|
512
|
+
|
513
|
+
warn "*** v2 #{$options.v2.inspect} v3 #{$options.v3.inspect}" if $options.verbose
|
514
|
+
|
515
|
+
input = ARGF.read
|
516
|
+
if input[0] == "\uFEFF"
|
517
|
+
warn "*** There is a leading byte order mark. Ignored."
|
518
|
+
input[0..0] = ''
|
519
|
+
end
|
520
|
+
if input[-1] != "\n"
|
521
|
+
# warn "*** added missing newline at end"
|
522
|
+
input << "\n" # fix #26
|
523
|
+
end
|
524
|
+
process_includes(input) unless ENV["KRAMDOWN_SAFE"]
|
525
|
+
input.gsub!(/^\{::boilerplate\s+(.*?)\}/) {
|
526
|
+
boilerplate($1)
|
527
|
+
}
|
528
|
+
if input =~ /[\t]/
|
529
|
+
warn "*** Input contains HT (\"tab\") characters. Undefined behavior will ensue."
|
530
|
+
input = expand_tabs(input)
|
531
|
+
end
|
532
|
+
|
533
|
+
if input =~ /\A---/ # this is a sectionized file
|
534
|
+
do_the_tls_dance unless ENV["KRAMDOWN_DONT_VERIFY_HTTPS"]
|
535
|
+
input, options, coding_override = xml_from_sections(input)
|
536
|
+
else
|
537
|
+
options = process_kramdown_options # all default
|
538
|
+
end
|
539
|
+
if input =~ /\A<\?xml/ # if this is a whole XML file, protect it
|
540
|
+
input = "{::nomarkdown}\n#{input}\n{:/nomarkdown}\n"
|
541
|
+
end
|
542
|
+
|
543
|
+
if $options.v3_used && !$options.v3
|
544
|
+
warn $options.v3_used
|
545
|
+
$options.v3_used = nil
|
546
|
+
$options.v3 = true
|
547
|
+
end
|
548
|
+
|
549
|
+
if coding_override
|
550
|
+
input = input.encode(Encoding.find(coding_override), fallback: FALLBACK)
|
551
|
+
end
|
552
|
+
|
553
|
+
# 1.4.17: because of UTF-8 bibxml files, kramdown always needs to see UTF-8 (!)
|
554
|
+
if input.encoding != Encoding::UTF_8
|
555
|
+
input = input.encode(Encoding::UTF_8)
|
556
|
+
end
|
557
|
+
|
558
|
+
# warn "options: #{options.inspect}"
|
559
|
+
doc = Kramdown::Document.new(input, options)
|
560
|
+
$stderr.puts doc.warnings.to_yaml unless doc.warnings.empty?
|
561
|
+
output = doc.to_rfc2629
|
562
|
+
|
563
|
+
if $options.v3_used && !$options.v3
|
564
|
+
warn $options.v3_used
|
565
|
+
$options.v3 = true
|
566
|
+
end
|
567
|
+
|
568
|
+
if coding_override
|
569
|
+
output = output.encode(Encoding.find(coding_override), fallback: FALLBACK)
|
570
|
+
end
|
571
|
+
|
572
|
+
puts output
|
@@ -18,7 +18,8 @@ class KDRFC
|
|
18
18
|
KDRFC_PREPEND = [ENV["KDRFC_PREPEND"]].compact
|
19
19
|
|
20
20
|
def v3_flag?
|
21
|
-
@options.v3 ? ["--v3"] : []
|
21
|
+
[*(@options.v3 ? ["--v3"] : []),
|
22
|
+
*(@options.v2 ? ["--v2"] : [])]
|
22
23
|
end
|
23
24
|
|
24
25
|
def process_mkd(input, output)
|
@@ -79,6 +80,7 @@ MODE_AS_FORMAT = {
|
|
79
80
|
}
|
80
81
|
}
|
81
82
|
|
83
|
+
# XXX move to author-tools@ietf.org API
|
82
84
|
def process_xml_remotely(input, output, *flags)
|
83
85
|
warn "* converting remotely from xml #{input} to txt #{output}" if @options.verbose
|
84
86
|
format = flags[0] || "--text"
|
@@ -14,7 +14,15 @@ module KramdownRFC
|
|
14
14
|
@f.delete(pn.to_s)
|
15
15
|
end
|
16
16
|
def []=(pn, val)
|
17
|
-
@f[pn] = val
|
17
|
+
@f[pn.to_s] = val
|
18
|
+
end
|
19
|
+
def default(pn, &block)
|
20
|
+
@f.fetch(pn.to_s, &block)
|
21
|
+
end
|
22
|
+
def default!(pn, value)
|
23
|
+
default(pn) {
|
24
|
+
@f[pn.to_s] = value
|
25
|
+
}
|
18
26
|
end
|
19
27
|
def has(pn)
|
20
28
|
@f[pn.to_s]
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: kramdown-rfc2629
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.6.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Carsten Bormann
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-02-
|
11
|
+
date: 2022-02-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: kramdown
|
@@ -71,6 +71,7 @@ description: |-
|
|
71
71
|
"kramdown" markdown parser. Mostly useful for RFC writers.
|
72
72
|
email: cabo@tzi.org
|
73
73
|
executables:
|
74
|
+
- kramdown-rfc
|
74
75
|
- kramdown-rfc2629
|
75
76
|
- doilit
|
76
77
|
- kramdown-rfc-extract-markdown
|
@@ -86,6 +87,7 @@ files:
|
|
86
87
|
- bin/de-gfm
|
87
88
|
- bin/doilit
|
88
89
|
- bin/kdrfc
|
90
|
+
- bin/kramdown-rfc
|
89
91
|
- bin/kramdown-rfc-cache-i-d-bibxml
|
90
92
|
- bin/kramdown-rfc-cache-subseries-bibxml
|
91
93
|
- bin/kramdown-rfc-extract-markdown
|
@@ -94,6 +96,7 @@ files:
|
|
94
96
|
- data/kramdown-rfc2629.erb
|
95
97
|
- data/math.json
|
96
98
|
- kramdown-rfc2629.gemspec
|
99
|
+
- lib/kramdown-rfc/command.rb
|
97
100
|
- lib/kramdown-rfc/erb.rb
|
98
101
|
- lib/kramdown-rfc/gzip-clone.rb
|
99
102
|
- lib/kramdown-rfc/kdrfc-processor.rb
|