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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 47ff7c7c6f4593d4d4c036c5986e7ff84ddec57ecb7a55959942d86ee6f0c9c8
4
- data.tar.gz: 363ddf3a404d78bc9c4daa71ae33850ec8a0f214fc9488e0c5d9230784f40056
3
+ metadata.gz: e14f146c7079e4611aa6ddf79f706ef0fddb168e1bb510c278ead6c93595b99a
4
+ data.tar.gz: efa3c83941f0eaa68e82d9d2bbe554826f70bee1469498e870506f006806bcf8
5
5
  SHA512:
6
- metadata.gz: b06841b1a0188f1f343eff51e2846b981d42592bbadbec8cbcdf38010ef66b23f560aab3effa183042d3207b9c0356346ab01dfa89ac551d3146e843cd6e35b1
7
- data.tar.gz: e80bb4fe75e33268c804907f332138fe68440355ef026a853d749e0d82c56d807acf19864b49cbf49f670d4c6c6fd6d1ef58719d534495866fa656ecb11cb562
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
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- coding: utf-8 -*-
3
+ require 'ostruct'
4
+
5
+ $options ||= OpenStruct.new
6
+
7
+ $options.v3 = true
8
+ require 'kramdown-rfc/command'
9
+
data/bin/kramdown-rfc2629 CHANGED
@@ -1,539 +1,4 @@
1
1
  #!/usr/bin/env ruby
2
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'
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![:include:](#{bts})#{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
@@ -1,6 +1,6 @@
1
1
  spec = Gem::Specification.new do |s|
2
2
  s.name = 'kramdown-rfc2629'
3
- s.version = '1.5.26'
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![:include:](#{bts})#{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.5.26
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-09 00:00:00.000000000 Z
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