asciidoctor-rfc 0.8.0 → 0.8.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (137) hide show
  1. checksums.yaml +4 -4
  2. data/.hound.yml +3 -0
  3. data/.rubocop.ribose.yml +65 -0
  4. data/.rubocop.tb.yml +640 -0
  5. data/.rubocop.yml +13 -30
  6. data/.travis.yml +8 -3
  7. data/CODE_OF_CONDUCT.md +46 -0
  8. data/Gemfile +8 -1
  9. data/LICENSE +21 -17
  10. data/README.adoc +471 -52
  11. data/asciidoctor-rfc.gemspec +14 -13
  12. data/lib/asciidoctor-rfc.rb +4 -4
  13. data/lib/asciidoctor/rfc/common/base.rb +304 -58
  14. data/lib/asciidoctor/rfc/common/front.rb +3 -3
  15. data/lib/asciidoctor/rfc/v2/base.rb +71 -82
  16. data/lib/asciidoctor/rfc/v2/blocks.rb +33 -10
  17. data/lib/asciidoctor/rfc/v2/converter.rb +0 -3
  18. data/lib/asciidoctor/rfc/v2/inline_anchor.rb +35 -20
  19. data/lib/asciidoctor/rfc/v2/lists.rb +11 -13
  20. data/lib/asciidoctor/rfc/v2/table.rb +11 -9
  21. data/lib/asciidoctor/rfc/v2/validate.rb +18 -724
  22. data/lib/asciidoctor/rfc/v3/base.rb +75 -102
  23. data/lib/asciidoctor/rfc/v3/blocks.rb +7 -9
  24. data/lib/asciidoctor/rfc/v3/converter.rb +0 -1
  25. data/lib/asciidoctor/rfc/v3/front.rb +14 -7
  26. data/lib/asciidoctor/rfc/v3/inline_anchor.rb +12 -9
  27. data/lib/asciidoctor/rfc/v3/lists.rb +31 -45
  28. data/lib/asciidoctor/rfc/v3/table.rb +2 -2
  29. data/lib/asciidoctor/rfc/v3/validate.rb +19 -2153
  30. data/lib/asciidoctor/rfc/v3/validate.rng +1 -1
  31. data/lib/asciidoctor/rfc/version.rb +1 -1
  32. data/rfc2629-other.ent +61 -0
  33. data/rfc2629-xhtml.ent +165 -0
  34. data/rfc2629.dtd +312 -0
  35. data/spec/asciidoctor/rfc/v2/biblio_spec.rb +9 -0
  36. data/spec/asciidoctor/rfc/v2/comments_spec.rb +67 -1
  37. data/spec/asciidoctor/rfc/v2/crossref_spec.rb +127 -5
  38. data/spec/asciidoctor/rfc/v2/date_spec.rb +24 -28
  39. data/spec/asciidoctor/rfc/v2/dlist_spec.rb +5 -5
  40. data/spec/asciidoctor/rfc/v2/image_spec.rb +2 -2
  41. data/spec/asciidoctor/rfc/v2/inline_formatting_spec.rb +28 -0
  42. data/spec/asciidoctor/rfc/v2/listing_spec.rb +2 -2
  43. data/spec/asciidoctor/rfc/v2/literal_spec.rb +4 -4
  44. data/spec/asciidoctor/rfc/v2/preamble_spec.rb +1 -1
  45. data/spec/asciidoctor/rfc/v2/quote_spec.rb +1 -1
  46. data/spec/asciidoctor/rfc/v2/references_spec.rb +210 -3
  47. data/spec/asciidoctor/rfc/v2/section_spec.rb +33 -0
  48. data/spec/asciidoctor/rfc/v2/table_spec.rb +40 -1
  49. data/spec/asciidoctor/rfc/v2/text_spec.rb +71 -14
  50. data/spec/asciidoctor/rfc/v2/ulist_spec.rb +1 -1
  51. data/spec/asciidoctor/rfc/v3/biblio_spec.rb +13 -0
  52. data/spec/asciidoctor/rfc/v3/comments_spec.rb +71 -0
  53. data/spec/asciidoctor/rfc/v3/crossref_spec.rb +7 -0
  54. data/spec/asciidoctor/rfc/v3/dlist_spec.rb +4 -4
  55. data/spec/asciidoctor/rfc/v3/literal_spec.rb +1 -1
  56. data/spec/asciidoctor/rfc/v3/preamble_spec.rb +1 -1
  57. data/spec/asciidoctor/rfc/v3/references_spec.rb +208 -3
  58. data/spec/asciidoctor/rfc/v3/series_info_spec.rb +1 -1
  59. data/spec/asciidoctor/rfc/v3/table_spec.rb +70 -1
  60. data/spec/examples/README.adoc +42 -6
  61. data/spec/examples/biblio-insert-v2.adoc +29 -0
  62. data/spec/examples/biblio-insert-v3.adoc +30 -0
  63. data/spec/examples/davies-template-bare-06.adoc +1 -0
  64. data/spec/examples/draft-iab-html-rfc-bis.xml.adoc +2300 -0
  65. data/spec/examples/draft-iab-html-rfc-bis.xml.orig +1999 -0
  66. data/spec/examples/draft-iab-html-rfc-bis.xml.txt +2298 -0
  67. data/spec/examples/draft-iab-rfc-framework-bis.xml.adoc +662 -0
  68. data/spec/examples/draft-iab-rfc-framework-bis.xml.orig +476 -0
  69. data/spec/examples/draft-ietf-core-block-xx.mkd.adoc +8 -33
  70. data/spec/examples/hoffmanv2.xml.adoc +4 -0
  71. data/spec/examples/mib-doc-template-xml-06.adoc +6 -8
  72. data/spec/examples/refs-v2-database.xml +86 -0
  73. data/spec/examples/refs-v2.adoc +49 -0
  74. data/spec/examples/refs-v2.xml +50 -0
  75. data/spec/examples/refs-v2/AsciiDoc.xml +8 -0
  76. data/spec/examples/refs-v2/AsciiMathML.xml +8 -0
  77. data/spec/examples/refs-v2/IETF-BibXML.xml +9 -0
  78. data/spec/examples/refs-v2/MathJax.xml +8 -0
  79. data/spec/examples/refs-v2/NroffEdit.xml +8 -0
  80. data/spec/examples/refs-v2/TeX-LaTeX.xml +8 -0
  81. data/spec/examples/refs-v2/abarth +17 -0
  82. data/spec/examples/refs-v2/asciidoctor-bibliography.xml +19 -0
  83. data/spec/examples/refs-v2/asciidoctor-manual.xml +11 -0
  84. data/spec/examples/refs-v2/asciidoctor-rfc.xml +18 -0
  85. data/spec/examples/refs-v2/asciidoctor.xml +10 -0
  86. data/spec/examples/refs-v2/draftr.xml +8 -0
  87. data/spec/examples/refs-v2/kramdown-rfc2629.xml +8 -0
  88. data/spec/examples/refs-v2/lyx2rfc.xml +8 -0
  89. data/spec/examples/refs-v2/mmark.xml +9 -0
  90. data/spec/examples/refs-v2/pandoc2rfc.xml +8 -0
  91. data/spec/examples/refs-v2/reference.RFC.2119.xml +7 -0
  92. data/spec/examples/refs-v2/reference.RFC.5385.xml +7 -0
  93. data/spec/examples/refs-v2/reference.RFC.7328.xml +7 -0
  94. data/spec/examples/refs-v2/reference.RFC.7749.xml +7 -0
  95. data/spec/examples/refs-v2/reference.RFC.7763.xml +7 -0
  96. data/spec/examples/refs-v2/reference.RFC.7764.xml +7 -0
  97. data/spec/examples/refs-v2/reference.RFC.7990.xml +7 -0
  98. data/spec/examples/refs-v2/reference.RFC.7991.xml +7 -0
  99. data/spec/examples/refs-v3-database.xml +137 -0
  100. data/spec/examples/refs-v3.adoc +57 -0
  101. data/spec/examples/refs-v3.xml +90 -0
  102. data/spec/examples/refs-v3/AsciiDoc.xml +8 -0
  103. data/spec/examples/refs-v3/AsciiMathML.xml +8 -0
  104. data/spec/examples/refs-v3/IETF-BibXML.xml +9 -0
  105. data/spec/examples/refs-v3/MathJax.xml +8 -0
  106. data/spec/examples/refs-v3/NroffEdit.xml +8 -0
  107. data/spec/examples/refs-v3/TeX-LaTeX.xml +8 -0
  108. data/spec/examples/refs-v3/abarth +17 -0
  109. data/spec/examples/refs-v3/asciidoctor-bibliography.xml +19 -0
  110. data/spec/examples/refs-v3/asciidoctor-manual.xml +11 -0
  111. data/spec/examples/refs-v3/asciidoctor-rfc.xml +18 -0
  112. data/spec/examples/refs-v3/asciidoctor.xml +10 -0
  113. data/spec/examples/refs-v3/draftr.xml +8 -0
  114. data/spec/examples/refs-v3/kramdown-rfc2629.xml +8 -0
  115. data/spec/examples/refs-v3/lyx2rfc.xml +8 -0
  116. data/spec/examples/refs-v3/mathrefs.xml +19 -0
  117. data/spec/examples/refs-v3/mmark.xml +9 -0
  118. data/spec/examples/refs-v3/pandoc2rfc.xml +8 -0
  119. data/spec/examples/refs-v3/reference.RFC.2119.xml +7 -0
  120. data/spec/examples/refs-v3/reference.RFC.5385.xml +7 -0
  121. data/spec/examples/refs-v3/reference.RFC.7328.xml +7 -0
  122. data/spec/examples/refs-v3/reference.RFC.7749.xml +7 -0
  123. data/spec/examples/refs-v3/reference.RFC.7763.xml +7 -0
  124. data/spec/examples/refs-v3/reference.RFC.7764.xml +7 -0
  125. data/spec/examples/refs-v3/reference.RFC.7990.xml +7 -0
  126. data/spec/examples/refs-v3/reference.RFC.7991.xml +7 -0
  127. data/spec/examples/rfc2100.md.adoc +57 -44
  128. data/spec/examples/rfc3514.md.adoc +1 -1
  129. data/spec/examples/rfc6350.adoc +701 -695
  130. data/spec/examples/rfc6350.txt.orig +3372 -0
  131. data/spec/examples/rfc6350_refs.xml +774 -0
  132. data/spec/examples/rfc748.md.adoc +1 -1
  133. data/spec/examples/rfc7511.md.adoc +3 -3
  134. data/spec/examples/skel.mkd.adoc +1 -0
  135. data/spec/examples/stupid-s.mkd.adoc +4 -4
  136. metadata +94 -8
  137. data/spec/examples/rfc6350.bib +0 -763
@@ -10,22 +10,22 @@ Gem::Specification.new do |spec|
10
10
  spec.authors = ["Ribose Inc."]
11
11
  spec.email = ["open.source@ribose.com"]
12
12
 
13
- spec.summary = 'asciidoctor-rfc lets you write Internet-Drafts and RFCs in AsciiDoc.'
13
+ spec.summary = "asciidoctor-rfc lets you write Internet-Drafts and RFCs in AsciiDoc."
14
14
  spec.description = <<~DESCRIPTION
15
- asciidoctor-rfc lets you write Internet-Drafts and RFCs in a native
16
- "asciidoctor" syntax.
15
+ asciidoctor-rfc lets you write Internet-Drafts and RFCs in a native
16
+ "asciidoctor" syntax.
17
17
 
18
- RFC XML ("xml2rfc" Vocabulary XML, RFC7322) is the XML-based language used for
19
- writing Internet-Drafts and RFCs, but not everyone likes hand-crafting XML,
20
- especially when the focus should be on the content.
18
+ RFC XML ("xml2rfc" Vocabulary XML, RFC7322) is the XML-based language used for
19
+ writing Internet-Drafts and RFCs, but not everyone likes hand-crafting XML,
20
+ especially when the focus should be on the content.
21
21
 
22
- Specifically, the gem provides two things. First, an "asciidoctor" like syntax
23
- that lets you utilize close to all features of native RFC XML, and maps most
24
- asciidoctor textual syntax (like tables) into RFC XML features. Then, RFC XML
25
- v3 (RFC 7991) and v2 (RFC 7749) backends that lets you render your AsciiDoc
26
- into, you guessed it, RFC XML v3 and v2.
22
+ Specifically, the gem provides two things. First, an "asciidoctor" like syntax
23
+ that lets you utilize close to all features of native RFC XML, and maps most
24
+ asciidoctor textual syntax (like tables) into RFC XML features. Then, RFC XML
25
+ v3 (RFC 7991) and v2 (RFC 7749) backends that lets you render your AsciiDoc
26
+ into, you guessed it, RFC XML v3 and v2.
27
27
 
28
- This gem is in active development.
28
+ This gem is in active development.
29
29
  DESCRIPTION
30
30
 
31
31
  spec.homepage = "https://github.com/riboseinc/asciidoctor-rfc"
@@ -39,7 +39,8 @@ This gem is in active development.
39
39
 
40
40
  spec.add_dependency "asciidoctor", "~> 1.5.6"
41
41
  spec.add_dependency "htmlentities", "~> 4.3.4"
42
- spec.add_dependency "nokogiri", "~> 1.8.1"
42
+ spec.add_dependency "nokogiri"
43
+ spec.add_dependency "ruby-jing"
43
44
  spec.add_dependency "thread_safe"
44
45
 
45
46
  spec.add_development_dependency "bundler", "~> 1.15"
@@ -1,4 +1,4 @@
1
- require 'asciidoctor' unless defined? Asciidoctor::Converter
2
- require_relative 'asciidoctor/rfc/v2/converter'
3
- require_relative 'asciidoctor/rfc/v3/converter'
4
- require_relative 'asciidoctor/rfc/version'
1
+ require "asciidoctor" unless defined? Asciidoctor::Converter
2
+ require_relative "asciidoctor/rfc/v2/converter"
3
+ require_relative "asciidoctor/rfc/v3/converter"
4
+ require_relative "asciidoctor/rfc/version"
@@ -1,6 +1,11 @@
1
1
  require "date"
2
2
  require "nokogiri"
3
3
  require "htmlentities"
4
+ require "json"
5
+ require "pathname"
6
+ require "open-uri"
7
+ require "pp"
8
+ require "set"
4
9
 
5
10
  module Asciidoctor
6
11
  module RFC::Common
@@ -20,7 +25,7 @@ module Asciidoctor
20
25
  end
21
26
 
22
27
  def skip(node, name = nil)
23
- warn %(asciidoctor: WARNING: converter missing for #{name || node.node_name} node in RFC backend)
28
+ warn %(asciidoctor: WARNING (#{current_location(node)}): converter missing for #{name || node.node_name} node in RFC backend)
24
29
  nil
25
30
  end
26
31
 
@@ -36,27 +41,36 @@ module Asciidoctor
36
41
  # @note (boilerplate is ignored)
37
42
  def preamble(node)
38
43
  result = []
39
- $seen_abstract = false
40
- result << node.content
41
- if $seen_abstract
42
- result << "</abstract>"
44
+
45
+ # NOTE: *list is V3, verse is V2, paragraph is both
46
+ abstractable_contexts = %i{paragraph dlist olist ulist verse open}
47
+
48
+ abstract_blocks = node.blocks.take_while do |block|
49
+ abstractable_contexts.include? block.context
43
50
  end
51
+
52
+ remainder_blocks = node.blocks[abstract_blocks.length..-1]
53
+
54
+ result << noko do |xml|
55
+ if abstract_blocks.any?
56
+ xml.abstract do |xml_abstract|
57
+ xml_abstract << abstract_blocks.map(&:render).flatten.join("\n")
58
+ end
59
+ end
60
+ xml << remainder_blocks.map(&:render).flatten.join("\n")
61
+ end
62
+
44
63
  result << "</front><middle>"
45
64
  result
46
65
  end
47
66
 
48
- # TODO: dead code? remove.
49
- # def authorname(node, suffix)
50
- # noko do |xml|
51
- # author_attributes = {
52
- # fullname: node.attr("author#{suffix}") || node.attr("fullname#{suffix}"),
53
- # surname: node.attr("lastname#{suffix}"),
54
- # initials: node.attr("forename_initials#{suffix}"),
55
- # role: node.attr("role#{suffix}"),
56
- # }.reject { |_, value| value.nil? }
57
- # xml.author **author_attributes
58
- # end
59
- # end
67
+ IETF_AREAS = ["art", "Applications and Real-Time",
68
+ "gen", "General",
69
+ "int", "Internet",
70
+ "ops", "Operations and Management",
71
+ "rtg", "Routing",
72
+ "sec", "Security",
73
+ "tsv", "Transport"].freeze
60
74
 
61
75
  # Syntax:
62
76
  # = Title
@@ -64,6 +78,11 @@ module Asciidoctor
64
78
  # :area x, y
65
79
  def area(node, xml)
66
80
  node.attr("area")&.split(/, ?/)&.each do |ar|
81
+ if ar =~ / Area$/i
82
+ warn %(asciidoctor: WARNING (#{current_location(node)}): stripping suffix "Area" from area #{ar})
83
+ ar = ar.gsub(/ Area$/i, "")
84
+ end
85
+ warn %(asciidoctor: WARNING (#{current_location(node)}): unrecognised area #{ar}) unless IETF_AREAS.include?(ar)
67
86
  xml.area { |a| a << ar }
68
87
  end
69
88
  end
@@ -73,7 +92,17 @@ module Asciidoctor
73
92
  # Author
74
93
  # :workgroup x, y
75
94
  def workgroup(node, xml)
95
+ workgroups = cache_workgroup(node)
76
96
  node.attr("workgroup")&.split(/, ?/)&.each do |wg|
97
+ if wg =~ / (Working Group)$/i
98
+ warn %(asciidoctor: WARNING (#{current_location(node)}): suffix "Working Group" will be stripped in published RFC from #{wg})
99
+ wg_norm = wg.gsub(/ Working Group$/i, "")
100
+ end
101
+ if wg =~ / (Research Group)$/i
102
+ warn %(asciidoctor: WARNING (#{current_location(node)}): suffix "Research Group" will be stripped from working group #{wg})
103
+ wg_norm = wg.gsub(/ Research Group$/i, "")
104
+ end
105
+ warn %(asciidoctor: WARNING (#{current_location(node)}): unrecognised working group #{wg}) unless workgroups.include?(wg_norm)
77
106
  xml.workgroup { |w| w << wg }
78
107
  end
79
108
  end
@@ -112,7 +141,7 @@ module Asciidoctor
112
141
  node.text + noko { |xml| xml.iref **attr_code(iref_attributes) }.join
113
142
  else
114
143
  terms = node.attr "terms"
115
- warn %(asciidoctor: WARNING: only primary and secondary index terms supported: #{terms.join(': ')}) if terms.size > 2
144
+ warn %(asciidoctor: WARNING (#{current_location(node)}): only primary and secondary index terms supported: #{terms.join(': ')}) if terms.size > 2
116
145
  iref_attributes = {
117
146
  item: terms[0],
118
147
  subitem: (terms.size > 1 ? terms[1] : nil),
@@ -134,7 +163,7 @@ module Asciidoctor
134
163
  result << ref
135
164
  end
136
165
  else
137
- warn %(asciidoctor: WARNING: references are not raw XML: #{node.context})
166
+ warn %(asciidoctor: WARNING (#{current_location(node)}): references are not raw XML: #{node.context})
138
167
  end
139
168
  result
140
169
  end
@@ -143,6 +172,12 @@ module Asciidoctor
143
172
  # open block is a container of multiple blocks, treated as a single block.
144
173
  # We append each contained block to its parent
145
174
  result = []
175
+ if node.role == "comment"
176
+ return noko do |xml|
177
+ xml.comment " " + [flatten_rawtext(node).map { |x| [x, ""] } ].flatten.join("\n") + " "
178
+ end
179
+ end
180
+
146
181
  if node.blocks?
147
182
  node.blocks.each do |b|
148
183
  result << send(b.context, b)
@@ -153,9 +188,9 @@ module Asciidoctor
153
188
  result
154
189
  end
155
190
 
156
- #def dash(camel_cased_word)
191
+ # def dash(camel_cased_word)
157
192
  # camel_cased_word.gsub(/([a-z])([A-Z])/, '\1-\2').downcase
158
- #end
193
+ # end
159
194
 
160
195
  def common_rfc_pis(node)
161
196
  # Below are generally applicable Processing Instructions (PIs)
@@ -163,45 +198,45 @@ module Asciidoctor
163
198
  # These are set only if explicitly specified, with the exception
164
199
  # of compact and subcompact
165
200
  rfc_pis = {
166
- artworkdelimiter: node.attr("artworkdelimiter"),
167
- artworklines: node.attr("artworklines"),
168
- authorship: node.attr("authorship"),
169
- autobreaks: node.attr("autobreaks"),
170
- background: node.attr("background"),
171
- colonspace: node.attr("colonspace"),
172
- comments: node.attr("comments"),
173
- docmapping: node.attr("docmapping"),
174
- editing: node.attr("editing"),
175
- emoticonic: node.attr("emoticonic"),
176
- footer: node.attr("footer"),
177
- header: node.attr("header"),
178
- inline: node.attr("inline"),
179
- iprnotified: node.attr("iprnotified"),
180
- linkmailto: node.attr("linkmailto"),
181
- linefile: node.attr("linefile"),
182
- notedraftinprogress: node.attr("notedraftinprogress"),
183
- private: node.attr("private"),
184
- refparent: node.attr("refparent"),
185
- rfcedstyle: node.attr("rfcedstyle"),
186
- slides: node.attr("slides"),
187
- "text-list-symbols": node.attr("text-list-symbols"),
188
- tocappendix: node.attr("tocappendix"),
189
- tocindent: node.attr("tocindent"),
190
- tocnarrow: node.attr("tocnarrow"),
191
- tocompact: node.attr("tocompact"),
192
- topblock: node.attr("topblock"),
193
- useobject: node.attr("useobject"),
201
+ artworkdelimiter: node.attr("artworkdelimiter"),
202
+ artworklines: node.attr("artworklines"),
203
+ authorship: node.attr("authorship"),
204
+ autobreaks: node.attr("autobreaks"),
205
+ background: node.attr("background"),
206
+ colonspace: node.attr("colonspace"),
207
+ comments: node.attr("comments"),
208
+ docmapping: node.attr("docmapping"),
209
+ editing: node.attr("editing"),
210
+ emoticonic: node.attr("emoticonic"),
211
+ footer: node.attr("footer"),
212
+ header: node.attr("header"),
213
+ inline: node.attr("inline"),
214
+ iprnotified: node.attr("iprnotified"),
215
+ linkmailto: node.attr("linkmailto"),
216
+ linefile: node.attr("linefile"),
217
+ notedraftinprogress: node.attr("notedraftinprogress"),
218
+ private: node.attr("private"),
219
+ refparent: node.attr("refparent"),
220
+ rfcedstyle: node.attr("rfcedstyle"),
221
+ slides: node.attr("slides"),
222
+ "text-list-symbols": node.attr("text-list-symbols"),
223
+ tocappendix: node.attr("tocappendix"),
224
+ tocindent: node.attr("tocindent"),
225
+ tocnarrow: node.attr("tocnarrow"),
226
+ tocompact: node.attr("tocompact"),
227
+ topblock: node.attr("topblock"),
228
+ useobject: node.attr("useobject"),
194
229
 
195
230
  # give errors regarding ID-nits and DTD validation
196
- strict: node.attr("strict") || "yes",
231
+ strict: node.attr("strict") || "yes",
197
232
 
198
233
  # Vertical whitespace control
199
234
  # (using these PIs as follows is recommended by the RFC Editor)
200
235
 
201
236
  # do not start each main section on a new page
202
- compact: node.attr("compact") || "yes",
237
+ compact: node.attr("compact") || "yes",
203
238
  # keep one blank line between list items
204
- subcompact: node.attr("subcompact") || "no",
239
+ subcompact: node.attr("subcompact") || "no",
205
240
 
206
241
  # TOC control
207
242
  # generate a ToC
@@ -219,6 +254,52 @@ module Asciidoctor
219
254
  attr_code(rfc_pis)
220
255
  end
221
256
 
257
+ def set_pis(node, doc)
258
+ # Below are generally applicable Processing Instructions (PIs)
259
+ # that most I-Ds might want to use. (Here they are set differently than
260
+ # their defaults in xml2rfc v1.32)
261
+
262
+ if node.attr("rfc2629xslt") != "false"
263
+ pi = Nokogiri::XML::ProcessingInstruction.new(doc, "xml-stylesheet",
264
+ 'type="text/xsl" href="rfc2629.xslt"')
265
+ doc.root.add_previous_sibling(pi)
266
+ end
267
+
268
+ doc.create_internal_subset("rfc", nil, "rfc2629.dtd")
269
+ rfc_pis = common_rfc_pis(node)
270
+ rfc_pis.each_pair do |k, v|
271
+ pi = Nokogiri::XML::ProcessingInstruction.new(doc,
272
+ "rfc",
273
+ "#{k}=\"#{v}\"")
274
+ doc.root.add_previous_sibling(pi)
275
+ end
276
+
277
+ doc
278
+ end
279
+
280
+ # extract references which can be expressed as externally defined entities
281
+ def extract_entities(node, xmldoc)
282
+ refs = xmldoc.xpath("//reference")
283
+ ret = []
284
+ biblio = cache_biblio(node)
285
+ refs.each do |ref|
286
+ next if ref.parent.name == "referencegroup"
287
+ id = ref.at('.//seriesInfo[@name="Internet-Draft"]')
288
+ anchor = ref["anchor"]
289
+ url = if id.nil?
290
+ biblio[anchor]
291
+ else
292
+ biblio["I-D.#{id['value']}"] # the specific version reference
293
+ end
294
+ if biblio.has_key? anchor
295
+ ret << { entity: anchor,
296
+ node: ref,
297
+ url: url }
298
+ end
299
+ end
300
+ ret
301
+ end
302
+
222
303
  # if node contains blocks, flatten them into a single line
223
304
  def flatten(node)
224
305
  result = []
@@ -238,12 +319,12 @@ module Asciidoctor
238
319
  node.blocks.each { |b| result << flatten_rawtext(b) }
239
320
  elsif node.respond_to?(:lines)
240
321
  node.lines.each do |x|
241
- if node.respond_to?(:context) && (node.context == :literal || node.context == :listing)
242
- result << x.gsub(/</, "&lt;").gsub(/>/, "&gt;")
243
- else
244
- # strip not only HTML tags <tag>, but also Asciidoc crossreferences <<xref>>
245
- result << x.gsub(/<[^>]*>+/, "")
246
- end
322
+ result << if node.respond_to?(:context) && (node.context == :literal || node.context == :listing)
323
+ x.gsub(/</, "&lt;").gsub(/>/, "&gt;")
324
+ else
325
+ # strip not only HTML tags <tag>, but also Asciidoc crossreferences <<xref>>
326
+ x.gsub(/<[^>]*>+/, "")
327
+ end
247
328
  end
248
329
  elsif node.respond_to?(:text)
249
330
  result << node.text.gsub(/<[^>]*>+/, "")
@@ -253,6 +334,7 @@ module Asciidoctor
253
334
  result.reject(&:empty?)
254
335
  end
255
336
 
337
+ # block for processing XML document fragments as XHTML, to allow for HTMLentities
256
338
  def noko(&block)
257
339
  # fragment = ::Nokogiri::XML::DocumentFragment.parse("")
258
340
  # fragment.doc.create_internal_subset("xml", nil, "xhtml.dtd")
@@ -280,6 +362,170 @@ HERE
280
362
  [k, (v.is_a? String) ? HTMLEntities.new.decode(v) : v]
281
363
  end.to_h
282
364
  end
365
+
366
+ def current_location(node)
367
+ return "Line #{node.lineno}" if node.respond_to?(:lineno) && !node.lineno.nil? && !node.lineno.empty?
368
+ return "ID #{node.id}" if node.respond_to?(:id) && !node.id.nil?
369
+ while !node.nil? && (!node.respond_to?(:level) || node.level > 0) && node.context != :section
370
+ node = node.parent
371
+ return "Section: #{node.title}" if !node.nil? && node.context == :section
372
+ end
373
+ "??"
374
+ end
375
+
376
+ def cache_workgroup(node)
377
+ wgcache_name = "#{Dir.home}/.asciidoc-rfc-workgroup-cache.json"
378
+ # If we are required to, clear the wg cache
379
+ if node.attr("flush-caches") == "true"
380
+ system("rm -f #{wgcache_name}")
381
+ end
382
+ # Is there already a wg cache? If not, create it.
383
+ wg = []
384
+ if Pathname.new(wgcache_name).file?
385
+ File.open(wgcache_name, "r") do |f|
386
+ wg = JSON.parse(f.read)
387
+ end
388
+ else
389
+ File.open(wgcache_name, "w") do |b|
390
+ STDERR.puts "Reading workgroups from https://tools.ietf.org/wg/..."
391
+ Kernel.open("https://tools.ietf.org/wg/") do |f|
392
+ f.each_line do |line|
393
+ line.scan(%r{<td width="50%" style='padding: 0 1ex'>([^<]+)</td>}) do |w|
394
+ wg << w[0].gsub(/\s+$/, "").gsub(/ Working Group$/, "")
395
+ end
396
+ end
397
+ end
398
+ STDERR.puts "Reading workgroups from https://irtf.org/groups..."
399
+ Kernel.open("https://irtf.org/groups", ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE) do |f|
400
+ f.each_line do |line|
401
+ line.scan(%r{<a title="([^"]+) Research Group"[^>]+>([^<]+)<}) do |w|
402
+ wg << w[0].gsub(/\s+$/, "")
403
+ wg << w[1].gsub(/\s+$/, "") # abbrev
404
+ end
405
+ end
406
+ end
407
+ b << wg.to_json
408
+ end
409
+ end
410
+ wg
411
+ end
412
+
413
+ def cache_biblio(node)
414
+ bibliocache_name = "#{Dir.home}/.asciidoc-rfc-biblio-cache.json"
415
+ # If we are required to, clear the biblio cache
416
+ if node.attr("flush-caches") == "true"
417
+ system("rm -f #{bibliocache_name}")
418
+ end
419
+ # Is there already a biblio cache? If not, create it.
420
+ biblio = {}
421
+ if Pathname.new(bibliocache_name).file?
422
+ File.open(bibliocache_name, "r") do |f|
423
+ biblio = JSON.parse(f.read)
424
+ end
425
+ else
426
+ File.open(bibliocache_name, "w") do |b|
427
+ STDERR.puts "Reading references from https://xml2rfc.tools.ietf.org/public/rfc/bibxml/..."
428
+ Kernel.open("https://xml2rfc.tools.ietf.org/public/rfc/bibxml/") do |f|
429
+ # I'm just working off the ls output
430
+ f.each_line do |line|
431
+ line.scan(/a href="reference.RFC.(\d+).xml">/) do |w|
432
+ biblio["RFC#{w[0]}"] = "https://xml2rfc.tools.ietf.org/public/rfc/bibxml/reference.RFC.#{w[0]}.xml"
433
+ end
434
+ end
435
+ ["https://xml2rfc.tools.ietf.org/public/rfc/bibxml2/",
436
+ "https://xml2rfc.tools.ietf.org/public/rfc/bibxml3/",
437
+ "https://xml2rfc.tools.ietf.org/public/rfc/bibxml4/",
438
+ "https://xml2rfc.tools.ietf.org/public/rfc/bibxml5/"].each do |url|
439
+ STDERR.puts "Reading references from #{url}..."
440
+ Kernel.open(url) do |f1|
441
+ f1.each_line do |line|
442
+ line.scan(/a href="reference.(\S+).xml">/) do |w|
443
+ biblio[w[0]] = "#{url}/reference.#{w[0]}.xml"
444
+ end
445
+ end
446
+ end
447
+ end
448
+ end
449
+ b << biblio.to_json
450
+ end
451
+ end
452
+ biblio
453
+ end
454
+
455
+ # insert bibliography based on anchors, references directory, and list of normatives in doc attribute
456
+ def insert_biblio(node, xmldoc)
457
+ # we want no references in this document, so we can ignore any anchors of references
458
+ xmldoc.xpath("//referencegroup | //reference").each(&:remove)
459
+ refs = Set.new
460
+ xmldoc.xpath("//xref | //relref").each { |r| refs << r["target"] }
461
+ anchors1 = Set.new
462
+ # we have no references in this document, so any remaining anchors are internal cross-refs only
463
+ xmldoc.xpath("//@anchor").each { |r| anchors1 << r.value }
464
+ refs = refs - anchors1
465
+ anchors = {}
466
+
467
+ norm_refs_spec = Set.new(node.attr("normative").split(/,[ ]?/))
468
+ anchors[:norm] = refs.intersection(norm_refs_spec)
469
+ anchors[:info] = refs - anchors[:norm]
470
+ seen_refs = { norm: Set.new, info: Set.new }
471
+ refxml_in = { norm: {}, info: {} }
472
+ refxml_out = { norm: [], info: [] }
473
+
474
+ bibliodir = node.attr("biblio-dir")
475
+ Dir.foreach bibliodir do |f|
476
+ next if [".", ".."].include? f
477
+ text = File.read("#{bibliodir}/#{f}", encoding: "utf-8")
478
+ next unless text =~ /<reference/
479
+ text =~ /<reference[^>]*anchor=['"]([^'"]*)/
480
+ anchor = Regexp.last_match(1)
481
+ next if anchor.nil? || anchor.empty?
482
+ if anchors[:norm].include?(anchor)
483
+ refxml_in[:norm][anchor] = text
484
+ seen_refs[:norm] << anchor
485
+ else
486
+ refxml_in[:info][anchor] = text
487
+ seen_refs[:info] << anchor
488
+ end
489
+ end
490
+
491
+ biblio = cache_biblio(node)
492
+ [:norm, :info].each do |reftype|
493
+ anchors[reftype].each do |r|
494
+ if refxml_in[reftype].has_key?(r)
495
+ # priority to on-disk references over skeleton references: they may contain draft information
496
+ refxml_out[reftype] << refxml_in[reftype][r]
497
+ elsif biblio.has_key?(r)
498
+ refxml_out[reftype] << %{<reference anchor="#{r}"/>}
499
+ else
500
+ warn "Reference #{r} has not been includes in references directory, and is not a recognised external RFC reference"
501
+ end
502
+ end
503
+ end
504
+
505
+ xml_location = xmldoc.at('//references[@title="Normative References" or name="Normative References"]')
506
+ xml_location&.children = Nokogiri::XML.fragment(refxml_out[:norm].join)
507
+ xml_location = xmldoc.at('//references[@title="Informative References" or name="Informative References"]')
508
+ xml_location&.children = Nokogiri::XML.fragment(refxml_out[:info].join)
509
+ xmldoc
510
+ end
511
+
512
+ def smart_quote_cleanup(xmldoc)
513
+ # smart quotes: handle smart apostrophe
514
+ xmldoc.traverse do |node|
515
+ if node.text?
516
+ node.content = node.content.tr("\u2019", "'")
517
+ node.content = node.content.gsub(/\&#8217;/, "'")
518
+ node.content = node.content.gsub(/\&#x2019;/, "'")
519
+ elsif node.element?
520
+ node.attributes.each do |k, v|
521
+ node.set_attribute(k, v.content.tr("\u2019", "'"))
522
+ node.set_attribute(k, v.content.gsub(/\&#8217;/, "'"))
523
+ node.set_attribute(k, v.content.gsub(/\&#x2019;/, "'"))
524
+ end
525
+ end
526
+ end
527
+ xmldoc
528
+ end
283
529
  end
284
530
  end
285
531
  end