kramdown-rfc2629-1.7.14/ 0000755 0000041 0000041 00000000000 14630024004 014542 5 ustar www-data www-data kramdown-rfc2629-1.7.14/data/ 0000755 0000041 0000041 00000000000 14630024004 015453 5 ustar www-data www-data kramdown-rfc2629-1.7.14/data/encoding-fallbacks.txt 0000644 0000041 0000041 00000002344 14630024004 021725 0 ustar www-data www-data 00a0
00a1 !
00a2 [cents]
00a3 GBP
00a4 [currency units]
00a5 JPY
00a6 |
00a7 S.
00a9 (C)
00aa a
00ab <<
00ac [not]
00ae (R)
00af _
00b0 o
00b1 +/-
00b2 ^2
00b3 ^3
00b4 '
00b5 [micro]
00b6 P.
00b7 .
00b8 ,
00b9 ^1
00ba o
00bb >>
00bc 1/4
00bd 1/2
00be 3/4
00bf ?
00c0 A
00c1 A
00c2 A
00c3 A
00c4 Ae
00c5 Ae
00c6 AE
00c7 C
00c8 E
00c9 E
00ca E
00cb E
00cc I
00cd I
00ce I
00cf I
00d0 [ETH]
00d1 N
00d2 O
00d3 O
00d4 O
00d5 O
00d6 Oe
00d7 x
00d8 Oe
00d9 U
00da U
00db U
00dc Ue
00dd Y
00de [THORN]
00df ss
00e0 a
00e1 a
00e2 a
00e3 a
00e4 ae
00e5 ae
00e6 ae
00e7 c
00e8 e
00e9 e
00ea e
00eb e
00ec i
00ed i
00ee i
00ef i
00f0 [eth]
00f1 n
00f2 o
00f3 o
00f4 o
00f5 o
00f6 oe
00f7 /
00f8 oe
00f9 u
00fa u
00fb u
00fc ue
00fd y
00fe [thorn]
00ff y
0152 OE
0153 oe
0161 s
0178 Y
0192 f
02dc ~
2002
2003
2009
2013 --
2014 ---
2018 '
2019 '
201a '
201c "
201d "
201e "
2020 *!*
2021 *!!*
2022 o
2026 ...
2030 [/1000]
2032 '
2039 <
203a >
2044 /
20ac EUR
2122 [TM]
2190 <--
2192 -->
2194 <->
21d0 <==
21d2 ==>
21d4 <=>
2212 -
2217 *
2264 <=
2265 >=
2329 <
232a >
0021 !
0023 #
0024 $
0025 %
0028 (
0029 )
002a *
002b +
002c ,
002d -
002e .
002f /
003a :
003b ;
003d =
003f ?
0040 @
005b [
005d ]
005e ^
005f _
0060 `
007b {
007c |
007d }
017d Z
017e z
2010 -
kramdown-rfc2629-1.7.14/data/math.json 0000644 0000041 0000041 00000035342 14630024004 017306 0 ustar www-data www-data {"replacements":[["\\textfractionsolidus","⁄"],["\\leftrightsquigarrow","↭"],["\\textpertenthousand","‱"],["\\blacktriangleright","▸"],["\\blacktriangledown","▾"],["\\blacktriangleleft","◂"],["\\twoheadrightarrow","↠"],["\\leftrightharpoons","⇋"],["\\rightleftharpoons","⇌"],["\\textreferencemark","※"],["\\circlearrowright","↻"],["\\rightrightarrows","⇉"],["\\vartriangleright","⊳"],["\\textordmasculine","º"],["\\textvisiblespace","␣"],["\\twoheadleftarrow","↞"],["\\downharpoonright","⇂"],["\\ntrianglerighteq","⋭"],["\\rightharpoondown","⇁"],["\\textperthousand","‰"],["\\leftrightarrows","⇆"],["\\textmusicalnote","♪"],["\\nleftrightarrow","↮"],["\\rightleftarrows","⇄"],["\\bigtriangledown","▽"],["\\textordfeminine","ª"],["\\ntrianglelefteq","⋬"],["\\rightthreetimes","⋌"],["\\trianglerighteq","⊵"],["\\vartriangleleft","⊲"],["\\rightsquigarrow","⇝"],["\\downharpoonleft","⇃"],["\\curvearrowright","↷"],["\\circlearrowleft","↺"],["\\leftharpoondown","↽"],["\\nLeftrightarrow","⇎"],["\\curvearrowleft","↶"],["\\guilsinglright","›"],["\\leftthreetimes","⋋"],["\\leftrightarrow","↔"],["\\rightharpoonup","⇀"],["\\guillemotright","»"],["\\downdownarrows","⇊"],["\\hookrightarrow","↪"],["\\hspace{0.25em}"," "],["\\dashrightarrow","⇢"],["\\leftleftarrows","⇇"],["\\trianglelefteq","⊴"],["\\ntriangleright","⋫"],["\\doublebarwedge","⌆"],["\\upharpoonright","↾"],["\\rightarrowtail","↣"],["\\looparrowright","↬"],["\\Leftrightarrow","⇔"],["\\sphericalangle","∢"],["\\divideontimes","⋇"],["\\measuredangle","∡"],["\\blacktriangle","▴"],["\\ntriangleleft","⋪"],["\\mathchar\"1356","⁁"],["\\texttrademark","™"],["\\mathchar\"2208","⌖"],["\\triangleright","▹"],["\\leftarrowtail","↢"],["\\guilsinglleft","‹"],["\\upharpoonleft","↿"],["\\mathbb{gamma}","ℽ"],["\\fallingdotseq","≒"],["\\looparrowleft","↫"],["\\textbrokenbar","¦"],["\\hookleftarrow","↩"],["\\smallsetminus","﹨"],["\\dashleftarrow","⇠"],["\\guillemotleft","«"],["\\leftharpoonup","↼"],["\\mathbb{Gamma}","ℾ"],["\\bigtriangleup","△"],["\\textcircledP","℗"],["\\risingdotseq","≓"],["\\triangleleft","◃"],["\\mathsterling","£"],["\\textcurrency","¤"],["\\triangledown","▿"],["\\blacklozenge",""],["\\sfrac{5}{6}","⅚"],["\\preccurlyeq","≼"],["\\Rrightarrow","⇛"],["\\circledcirc","⊚"],["\\nRightarrow","⇏"],["\\sfrac{3}{8}","⅜"],["\\sfrac{1}{3}","⅓"],["\\sfrac{2}{5}","⅖"],["\\vartriangle","▵"],["\\Updownarrow","⇕"],["\\nrightarrow","↛"],["\\sfrac{1}{2}","½"],["\\sfrac{3}{5}","⅗"],["\\succcurlyeq","≽"],["\\sfrac{4}{5}","⅘"],["\\diamondsuit","♦"],["\\hphantom{0}"," "],["\\sfrac{1}{6}","⅙"],["\\curlyeqsucc","⋟"],["\\blacksquare","▪"],["\\hphantom{,}"," "],["\\curlyeqprec","⋞"],["\\sfrac{1}{8}","⅛"],["\\sfrac{7}{8}","⅞"],["\\sfrac{1}{5}","⅕"],["\\sfrac{2}{3}","⅔"],["\\updownarrow","↕"],["\\backepsilon","∍"],["\\circleddash","⊝"],["\\eqslantless","⋜"],["\\sfrac{3}{4}","¾"],["\\sfrac{5}{8}","⅝"],["\\hspace{1pt}"," "],["\\sfrac{1}{4}","¼"],["\\mathbb{Pi}","ℿ"],["\\mathcal{M}","ℳ"],["\\mathcal{o}","ȓ4"],["\\mathcal{O}","ᵊA"],["\\nsupseteqq","⊉"],["\\mathcal{B}","ℬ"],["\\textrecipe","℞"],["\\nsubseteqq","⊈"],["\\subsetneqq","⊊"],["\\mathcal{I}","ℑ"],["\\upuparrows","⇈"],["\\mathcal{e}","ℯ"],["\\mathcal{L}","ℒ"],["\\nleftarrow","↚"],["\\mathcal{H}","ℋ"],["\\mathcal{E}","ℰ"],["\\eqslantgtr","⋝"],["\\curlywedge","⋏"],["\\varepsilon","ε"],["\\supsetneqq","⊋"],["\\rightarrow","→"],["\\mathcal{R}","ℛ"],["\\sqsubseteq","⊑"],["\\mathcal{g}","ℊ"],["\\sqsupseteq","⊒"],["\\complement","∁"],["\\Rightarrow","⇒"],["\\gtreqqless","⋛"],["\\lesseqqgtr","⋚"],["\\circledast","⊛"],["\\nLeftarrow","⇍"],["\\Lleftarrow","⇚"],["\\varnothing","∅"],["\\mathcal{N}","𝒩"],["\\Leftarrow","⇐"],["\\gvertneqq","≩"],["\\mathbb{C}","ℂ"],["\\supsetneq","⊋"],["\\leftarrow","←"],["\\nleqslant","≰"],["\\mathbb{Q}","ℚ"],["\\mathbb{Z}","ℤ"],["\\llbracket","〚"],["\\mathbb{H}","ℍ"],["\\spadesuit","♠"],["\\mathit{o}","ℴ"],["\\mathbb{P}","ℙ"],["\\rrbracket","〛"],["\\supseteqq","⊇"],["\\copyright","©"],["\\textsc{k}","ĸ"],["\\gtreqless","⋛"],["\\mathbb{j}","ⅉ"],["\\pitchfork","⋔"],["\\estimated","℮"],["\\ngeqslant","≱"],["\\mathbb{e}","ⅇ"],["\\therefore","∴"],["\\triangleq","≜"],["\\varpropto","∝"],["\\subsetneq","⊊"],["\\heartsuit","♥"],["\\mathbb{d}","ⅆ"],["\\lvertneqq","≨"],["\\checkmark","✓"],["\\nparallel","∦"],["\\mathbb{R}","ℝ"],["\\lesseqgtr","⋚"],["\\downarrow","↓"],["\\mathbb{D}","ⅅ"],["\\mathbb{i}","ⅈ"],["\\backsimeq","⋍"],["\\mathbb{N}","ℕ"],["\\Downarrow","⇓"],["\\subseteqq","⊆"],["\\setminus","∖"],["\\succnsim","⋩"],["\\doteqdot","≑"],["\\clubsuit","♣"],["\\emptyset","∅"],["\\sqsupset","⊐"],["\\fbox{~~}","▭"],["\\curlyvee","⋎"],["\\varkappa","ϰ"],["\\llcorner","⌞"],["\\varsigma","ς"],["\\approxeq","≊"],["\\backcong","≌"],["\\supseteq","⊇"],["\\circledS","Ⓢ"],["\\circledR","®"],["\\textcent","¢"],["\\urcorner","⌝"],["\\lrcorner","⌟"],["\\boxminus","⊟"],["\\texteuro","€"],["\\vartheta","ϑ"],["\\barwedge","⊼"],["\\ding{86}","✶"],["\\sqsubset","⊏"],["\\subseteq","⊆"],["\\intercal","⊺"],["\\ding{73}","☆"],["\\ulcorner","⌜"],["\\recorder","⌕"],["\\precnsim","⋨"],["\\parallel","∥"],["\\boxtimes","⊠"],["\\ding{55}","✗"],["\\multimap","⊸"],["\\maltese","✠"],["\\nearrow","↗"],["\\swarrow","↙"],["\\lozenge","◊"],["\\sqrt[3]","∛"],["\\succsim","≿"],["\\dotplus","∔"],["\\tilde{}","~"],["\\check{}","ˇ"],["\\lessgtr","≶"],["\\Upsilon","ϒ"],["\\Cdprime","Ъ"],["\\gtrless","≷"],["\\backsim","∽"],["\\nexists","∄"],["\\dotplus","∔"],["\\searrow","↘"],["\\lessdot","⋖"],["\\boxplus","⊞"],["\\upsilon","υ"],["\\epsilon","ε"],["\\diamond","⋄"],["\\bigstar","★"],["\\ddagger","‡"],["\\cdprime","ъ"],["\\Uparrow","⇑"],["\\sqrt[4]","∜"],["\\between","≬"],["\\sqangle","∟"],["\\digamma","Ϝ"],["\\uparrow","↑"],["\\nwarrow","↖"],["\\precsim","≾"],["\\breve{}","˘"],["\\because","∵"],["\\bigcirc","◯"],["\\acute{}","´"],["\\grave{}","`"],["\\check{}","ˇ"],["\\lesssim","≲"],["\\partial","∂"],["\\natural","♮"],["\\supset","⊃"],["\\hstrok","ħ"],["\\Tstrok","Ŧ"],["\\coprod","∐"],["\\models","⊧"],["\\otimes","⊗"],["\\degree","°"],["\\gtrdot","⋗"],["\\preceq","≼"],["\\Lambda","Λ"],["\\lambda","λ"],["\\cprime","ь"],["\\varrho","ϱ"],["\\Bumpeq","≎"],["\\hybull","⁃"],["\\lmidot","ŀ"],["\\nvdash","⊬"],["\\lbrace","{"],["\\bullet","•"],["\\varphi","φ"],["\\bumpeq","≏"],["\\ddot{}","¨"],["\\Lmidot","Ŀ"],["\\Cprime","Ь"],["\\female","♀"],["\\rtimes","⋊"],["\\gtrsim","≳"],["\\mapsto","↦"],["\\daleth","ℸ"],["\\square","■"],["\\nVDash","⊯"],["\\rangle","〉"],["\\tstrok","ŧ"],["\\oslash","⊘"],["\\ltimes","⋉"],["\\lfloor","⌊"],["\\marker","▮"],["\\Subset","⋐"],["\\Vvdash","⊪"],["\\propto","∝"],["\\Hstrok","Ħ"],["\\dlcrop","⌍"],["\\forall","∀"],["\\nVdash","⊮"],["\\Supset","⋑"],["\\langle","〈"],["\\ominus","⊖"],["\\rfloor","⌋"],["\\circeq","≗"],["\\eqcirc","≖"],["\\drcrop","⌌"],["\\veebar","⊻"],["\\ulcrop","⌏"],["\\nvDash","⊭"],["\\urcrop","⌎"],["\\exists","∃"],["\\approx","≈"],["\\dagger","†"],["\\boxdot","⊡"],["\\succeq","≽"],["\\bowtie","⋈"],["\\subset","⊂"],["\\Sigma","Σ"],["\\Omega","Ω"],["\\nabla","∇"],["\\colon",":"],["\\boxHu","╧"],["\\boxHd","╤"],["\\aleph","ℵ"],["\\gnsim","⋧"],["\\boxHU","╩"],["\\boxHD","╦"],["\\equiv","≡"],["\\lneqq","≨"],["\\alpha","α"],["\\amalg","∐"],["\\boxhU","╨"],["\\boxhD","╥"],["\\uplus","⊎"],["\\boxhu","┴"],["\\kappa","κ"],["\\sigma","σ"],["\\boxDL","╗"],["\\Theta","Θ"],["\\Vdash","⊩"],["\\boxDR","╔"],["\\boxDl","╖"],["\\sqcap","⊓"],["\\boxDr","╓"],["\\bar{}","¯"],["\\dashv","⊣"],["\\vDash","⊨"],["\\boxdl","┐"],["\\boxVl","╢"],["\\boxVh","╫"],["\\boxVr","╟"],["\\boxdr","┌"],["\\boxdL","╕"],["\\boxVL","╣"],["\\boxVH","╬"],["\\boxVR","╠"],["\\boxdR","╒"],["\\theta","θ"],["\\lhblk","▄"],["\\uhblk","▀"],["\\ldotp","."],["\\ldots","…"],["\\boxvL","╡"],["\\boxvH","╪"],["\\boxvR","╞"],["\\boxvl","┤"],["\\boxvh","┼"],["\\boxvr","├"],["\\Delta","Δ"],["\\boxUR","╚"],["\\boxUL","╝"],["\\oplus","⊕"],["\\boxUr","╙"],["\\boxUl","╜"],["\\doteq","≐"],["\\happy","㋡"],["\\varpi","ϖ"],["\\smile","☺"],["\\boxul","┘"],["\\simeq","≃"],["\\boxuR","╘"],["\\boxuL","╛"],["\\boxhd","┬"],["\\gimel","ℷ"],["\\Gamma","Γ"],["\\lnsim","⋦"],["\\sqcup","⊔"],["\\omega","ω"],["\\sharp","♯"],["\\times","×"],["\\block","█"],["\\hat{}","^"],["\\wedge","∧"],["\\vdash","⊢"],["\\angle","∠"],["\\infty","∞"],["\\gamma","γ"],["\\asymp","≍"],["\\rceil","⌉"],["\\dot{}","˙"],["\\lceil","⌈"],["\\delta","δ"],["\\gneqq","≩"],["\\frown","⌢"],["\\phone","☎"],["\\vdots","⋮"],["\\boxr","└"],["\\k{i}","į"],["\\`{I}","Ì"],["\\perp","⊥"],["\\\"{o}","ö"],["\\={I}","Ī"],["\\`{a}","à"],["\\v{T}","Ť"],["\\surd","√"],["\\H{O}","Ő"],["\\vert","|"],["\\k{I}","Į"],["\\\"{y}","ÿ"],["\\\"{O}","Ö"],["\\'{Y}","Ý"],["\\u{u}","ў"],["\\u{G}","Ğ"],["\\.{E}","Ė"],["\\.{z}","ż"],["\\v{t}","ť"],["\\prec","≺"],["\\H{o}","ő"],["\\mldr","…"],["\\'{y}","ý"],["\\cong","≅"],["\\.{e}","ė"],["\\'{L}","Ĺ"],["\\star","*"],["\\.{Z}","Ż"],["\\'{e}","é"],["\\geqq","≧"],["\\cdot","⋅"],["\\`{U}","Ù"],["\\'{l}","ĺ"],["\\v{L}","Ľ"],["\\c{s}","ş"],["\\'{s}","ś"],["\\~{A}","Ã"],["\\Vert","‖"],["\\k{e}","ę"],["\\lnot","¬"],["\\'{z}","ź"],["\\leqq","≦"],["\\beta","β"],["\\beth","ℶ"],["\\'{E}","É"],["\\~{n}","ñ"],["\\u{i}","й"],["\\c{S}","Ş"],["\\c{N}","Ņ"],["\\H{u}","ű"],["\\v{n}","ň"],["\\'{S}","Ś"],["\\={U}","Ū"],["\\~{O}","Õ"],["\\'{Z}","Ź"],["\\v{E}","Ě"],["\\'{R}","Ŕ"],["\\H{U}","Ű"],["\\v{N}","Ň"],["\\prod","∏"],["\\v{s}","š"],["\\\"{U}","Ü"],["\\c{n}","ņ"],["\\k{U}","Ų"],["\\c{R}","Ŗ"],["\\'{A}","Á"],["\\~{o}","õ"],["\\v{e}","ě"],["\\v{S}","Š"],["\\u{A}","Ă"],["\\circ","∘"],["\\\"{u}","ü"],["\\flat","♭"],["\\v{z}","ž"],["\\r{U}","Ů"],["\\`{O}","Ò"],["\\={u}","ū"],["\\oint","∮"],["\\c{K}","Ķ"],["\\k{u}","ų"],["\\not<","≮"],["\\not>","≯"],["\\`{o}","ò"],["\\\"{I}","Ï"],["\\v{D}","Ď"],["\\.{G}","Ġ"],["\\r{u}","ů"],["\\not=","≠"],["\\`{u}","ù"],["\\v{c}","č"],["\\c{k}","ķ"],["\\.{g}","ġ"],["\\'{N}","Ń"],["\\odot","⊙"],["\\`{e}","э"],["\\c{T}","Ţ"],["\\v{d}","ď"],["\\\"{e}","ё"],["\\'{I}","Í"],["\\v{R}","Ř"],["\\k{a}","ą"],["\\nldr","‥"],["\\`{A}","À"],["\\'{n}","ń"],["\\~{N}","Ñ"],["\\nmid","∤"],["\\.{C}","Ċ"],["\\zeta","ζ"],["\\~{u}","ũ"],["\\`{E}","Э"],["\\~{a}","ã"],["\\c{t}","ţ"],["\\={o}","ō"],["\\v{r}","ř"],["\\={A}","Ā"],["\\.{c}","ċ"],["\\~{U}","Ũ"],["\\k{A}","Ą"],["\\\"{a}","ä"],["\\u{U}","Ў"],["\\iota","ι"],["\\={O}","Ō"],["\\c{C}","Ç"],["\\gneq","≩"],["\\'{c}","ć"],["\\boxH","═"],["\\hbar","ℏ"],["\\\"{A}","Ä"],["\\boxv","│"],["\\boxh","─"],["\\male","♂"],["\\'{u}","ú"],["\\sqrt","√"],["\\succ","≻"],["\\c{c}","ç"],["\\'{C}","Ć"],["\\v{l}","ľ"],["\\u{a}","ă"],["\\v{Z}","Ž"],["\\'{o}","ó"],["\\c{G}","Ģ"],["\\v{C}","Č"],["\\lneq","≨"],["\\\"{E}","Ё"],["\\={a}","ā"],["\\c{l}","ļ"],["\\'{a}","á"],["\\={E}","Ē"],["\\boxV","║"],["\\u{g}","ğ"],["\\'{O}","Ó"],["\\'{g}","ǵ"],["\\u{I}","Й"],["\\c{L}","Ļ"],["\\k{E}","Ę"],["\\.{I}","İ"],["\\~{I}","Ĩ"],["\\quad"," "],["\\c{r}","ŗ"],["\\'{r}","ŕ"],["\\\"{Y}","Ÿ"],["\\={e}","ē"],["\\'{U}","Ú"],["\\leq","≤"],["\\Cup","⋓"],["\\Psi","Ψ"],["\\neq","≠"],["\\k{}","˛"],["\\={}","‾"],["\\H{}","˝"],["\\cup","∪"],["\\geq","≥"],["\\mho","℧"],["\\Dzh","Џ"],["\\cap","∩"],["\\bot","⊥"],["\\psi","ψ"],["\\chi","χ"],["\\c{}","¸"],["\\Phi","Φ"],["\\ast","*"],["\\ell","ℓ"],["\\top","⊤"],["\\lll","⋘"],["\\tau","τ"],["\\Cap","⋒"],["\\sad","☹"],["\\iff","⇔"],["\\eta","η"],["\\eth","ð"],["\\d{}","̣"],["\\rho","ρ"],["\\dzh","џ"],["\\div","÷"],["\\phi","ϕ"],["\\Rsh","↱"],["\\vee","∨"],["\\b{}","ˍ"],["\\t{}","͡"],["\\int","∫"],["\\sim","∼"],["\\r{}","˚"],["\\Lsh","↰"],["\\yen","¥"],["\\ggg","⋙"],["\\mid","∣"],["\\sum","∑"],["\\Dz","Ѕ"],["\\Re","ℜ"],["\\oe","œ"],["\\DH","Ð"],["\\ll","≪"],["\\ng","ŋ"],["\\'G","Ѓ"],["\\wr","≀"],["\\wp","℘"],["\\=I","І"],["\\:)","☺"],["\\:(","☹"],["\\AE","Æ"],["\\AA","Å"],["\\ss","ß"],["\\dz","ѕ"],["\\ae","æ"],["\\aa","å"],["\\th","þ"],["\\to","→"],["\\Pi","Π"],["\\mp","∓"],["\\Im","ℑ"],["\\pm","±"],["\\pi","π"],["\\\"I","Ї"],["\\'C","Ћ"],["\\in","∈"],["\\'K","Ќ"],["\\'k","ќ"],["\\'c","ћ"],["\\'g","ѓ"],["\\ni","∋"],["\\ne","≠"],["\\TH","Þ"],["\\Xi","Ξ"],["\\nu","ν"],["\\NG","Ŋ"],["\\:G","㋡"],["\\xi","ξ"],["\\OE","Œ"],["\\gg","≫"],["\\DJ","Đ"],["\\=e","є"],["\\=E","Є"],["\\mu","μ"],["\\dj","đ"],["\\:"," "],["\\;"," "],["\\&","&"],["\\$","$"],["\\%","%"],["\\#","#"],["\\,"," "],["\\-",""],["\\S","§"],["\\P","¶"],["\\O","Ø"],["\\L","Ł"],["\\}","}"],["\\o","ø"],["\\l","ł"],["\\h","ℎ"],["\\i","ℹ"],["-","−"]],"combiningmarks":[["\\doubleunderline","̳"],["\\strikethrough","̵"],["\\underline","̲"],["\\overline","̅"],["\\tilde","̃"],["\\grave","̀"],["\\acute","́"],["\\slash","̸"],["\\breve","̆"],["\\ddot","̈"],["\\dot","̇"],["\\bar","̅"],["\\vec","⃗"],["\\hat","̂"]],"subsuperscripts":[["_x","ₓ"],["_v","ᵥ"],["_u","ᵤ"],["_t","ₜ"],["_s","ₛ"],["_r","ᵣ"],["_p","ₚ"],["_o","ₒ"],["_n","ₙ"],["_m","ₘ"],["_l","ₗ"],["_k","ₖ"],["_j","ⱼ"],["_i","ᵢ"],["_h","ₕ"],["_e","ₑ"],["_a","ₐ"],["^∫","ᶴ"],["_>","˲"],["_=","₌"],["_<","˱"],["_9","₉"],["_8","₈"],["_7","₇"],["_6","₆"],["_5","₅"],["_4","₄"],["_3","₃"],["_2","₂"],["_1","₁"],["_0","₀"],["_-","₋"],["_−","₋"],["_+","₊"],["_)","₎"],["_(","₍"],["_ρ","ᵨ"],["_χ","ᵪ"],["_φ","ᵩ"],["_β","ᵦ"],["_γ","ᵧ"],["^φ","ᵠ"],["^χ","ᵡ"],["^δ","ᵟ"],["^γ","ᵞ"],["^β","ᵝ"],["^8","⁸"],["^9","⁹"],["^<","˂"],["^=","⁼"],["^>","˃"],["^0","⁰"],["^1","¹"],["^2","²"],["^3","³"],["^4","⁴"],["^5","⁵"],["^6","⁶"],["^7","⁷"],["^(","⁽"],["^)","⁾"],["^*","*"],["^+","⁺"],["^-","⁻"],["^−","⁻"],["^P","ᴾ"],["^R","ᴿ"],["^T","ᵀ"],["^U","ᵁ"],["^V","ᄑ"],["^W","ᵂ"],["^H","ᴴ"],["^I","ᴵ"],["^J","ᴶ"],["^K","ᴷ"],["^L","ᴸ"],["^M","ᴹ"],["^N","ᴺ"],["^O","ᴼ"],["^A","ᴬ"],["^B","ᴮ"],["^D","ᴰ"],["^E","ᴱ"],["^G","ᴳ"],["^x","ˣ"],["^y","ʸ"],["^z","ᶻ"],["^p","ᵖ"],["^r","ʳ"],["^s","ˢ"],["^t","ᵗ"],["^u","ᵘ"],["^v","ᵛ"],["^w","ʷ"],["^h","ʰ"],["^i","ⁱ"],["^j","ʲ"],["^k","ᵏ"],["^l","ˡ"],["^m","ᵐ"],["^n","ⁿ"],["^o","ᵒ"],["^a","ᵃ"],["^b","ᵇ"],["^c","ᶜ"],["^d","ᵈ"],["^e","ᵉ"],["^f","ᶠ"],["^g","ᵍ"]]}
kramdown-rfc2629-1.7.14/data/kramdown-rfc2629.erb 0000644 0000041 0000041 00000015136 14630024004 021070 0 ustar www-data www-data "?>
<% if $options.v3 %>
[
<% else %>
<% ps.arr("bibxml") do |tag, sys| -%>
SYSTEM "<%= sys %>">
<% end -%>
<% ps.arr("entity", false) do |en, ev| -%>
"<%=ev%>">
<% end -%>
]>
<%
ps.rest.fetch("consensus") do
# consensus not given -- default intelligently
cat = ps.has("category") || ps.has("cat")
MUST_CONSENSUS = {"std" => true, "bcp" => true}
ps["consensus"] = true if MUST_CONSENSUS[cat]
end
rfcattrs = ps.attrs("ipr", "docName=docname", "category=cat",
"consensus", "submissionType=submissiontype=stream", "xml:lang=lang",
"number", "obsoletes", "updates", "seriesNo=seriesno")
TRUE_FALSE = {nil => "true", false => "false", true => "true",
"yes" => "true", "no" => "false"}
YES_NO = {"true" => "yes", "false" => "no"}
pis = KramdownRFC::ParameterSet.new({})
ps.arr("pi", false) do |pi, val|
pis[pi] = TRUE_FALSE[val] || val
end
if $options.v3
piattrs = pis.attrs("tocDepth=tocdepth", "tocInclude=toc",
"sortRefs=sortrefs", "symRefs=symrefs", "indexInclude=index")
if piattrs != ""
rfcattrs << " " << piattrs
end
end
pis.rest.each do |pi, val|
v = YES_NO[val] || pis.escattr(val)
-%>
="<%=v%>"?>
<% end -%>
>
<%= ps.ele("title", ps.attr("abbrev=titleabbrev")) %>
<% ps.arr("author") do |au|
aups = KramdownRFC::authorps_from_hash(au)
-%>
<%= KramdownRFC::person_element_from_aups("author", aups) -%>
<% aups.warn_if_leftovers -%>
<% end -%>
/>
<%= ps.ele("area") %>
<%= ps.ele("workgroup=wg") %>
<%= ps.ele("keyword=kw") %>
<%= sechash.delete("abstract") %>
<% if $options.v3 -%>
<% venue = ps[:venue] -%>
<% if venue -%>
<% venue = KramdownRFC::ParameterSet.new(venue) -%>
<% if (dn = ps.av[:docName]) &&
(dt = dn.rpartition('-')[0]) != ""
-%>
<% if latest = venue[:latest] -%>
The latest revision of this draft can be found at .
<% end -%>
Status information for this document may be found at .
<% end -%>
<% mail = venue[:mail] -%>
<% homepage = venue[:home] -%>
<% gtype = venue[:type] -%>
<% if mail || homepage -%>
<% end -%>
<% if mail
mail_local, mail_host = mail.split("@", 2)
end
if mail_host -%>
<% default_links = {
"iab.org" => true,
"ietf.org" => true,
"irtf.org" => true,
}[mail_host]
mail_subdomain, mail_domain = mail_host.split(".", 2)
group = venue[:group] || mail_local # XXX
arch = venue[:arch] || default_links && "https://mailarchive.ietf.org/arch/browse/#{mail_local}/"
subscribe = venue[:subscribe] || default_links && "https://www.ietf.org/mailman/listinfo/#{mail_local}/"
GROUPS = {"ietf" => "Working ", "irtf" => "Research "}
gtype ||= "#{GROUPS[mail_subdomain]}Group" -%>
Discussion of this document takes place on the
<%=group%> <%=gtype%> mailing list ()<% if arch -%>,
which is archived at <% end -%>.
<% if subscribe -%>
Subscribe at .
<% end -%>
<% end -%>
<% if homepage -%>
<%=gtype%> information can be found at .
<% end -%>
<% if mail || homepage -%>
<% end -%>
<% if repo = venue[:repo] || ((gh = venue[:github]) && "https://github.com/#{gh}") -%>
Source for this draft and an issue tracker can be found at
.
<% end -%>
<%= venue.ele("t=text", nil, nil, true) -%>
<% venue.warn_if_leftovers -%>
<% end -%>
<% end -%>
<% sechash.keys.each do |k| -%>
<% if k =~ /\A(to_be_removed_)?note_(.*)/ -%>
<% option = ""
text = ""
if $1
if $options.v3
option = " removeInRFC=\"true\""
else
text = " [This note is to be removed before publishing as an RFC.]\n"
end
end
-%>
"<%= option %>>
<%= text -%>
<%= sechash.delete(k) -%>
<% end -%>
<% end -%>
{:/nomarkdown}
{:quote: gi="blockquote"}
{:aside: gi="aside"}
{:markers: sourcecode-markers="true"}
{:unnumbered: numbered="false"}
{:vspace: vspace="0"}
{:removeinrfc: removeinrfc="true"}
{:notoc: toc="exclude"}
{:compact: spacing="compact"}
{:noabbrev: noabbrev="true"}
{::nomarkdown}
<%= sechash.delete("middle") %>
<% displayref.each do |k, v| -%>
<% end -%>
<% if sh = sechash.delete("normative") -%>
<%= sh %>
<% end -%>
<% if sh = sechash.delete("informative") -%>
<%= sh %>
<% end -%>
<%= sechash.delete("back") %>
<% sh = sechash.delete("contributor") -%>
<% consec = ps.has("contributor") -%>
<% if sh || consec -%>
<% if $options.v3 -%>
Contributors
<% else -%>
<% warn "*** To use YAML contributors, use --v3 (kdrfc -3)" if consec -%>
<% end -%>
<%= sh -%>
<% if $options.v3 && consec
ps.arr("contributor") do |au|
aups = KramdownRFC::authorps_from_hash(au)
-%>
<%= KramdownRFC::person_element_from_aups("contact", aups) -%>
<%= if contrib = aups["contribution"]
<
<% aups.warn_if_leftovers -%>
<% end -%>
<% end -%>
<% end -%>
<% if $source -%>
<% end -%>
kramdown-rfc2629-1.7.14/bin/ 0000755 0000041 0000041 00000000000 14630024004 015312 5 ustar www-data www-data kramdown-rfc2629-1.7.14/bin/kramdown-rfc 0000755 0000041 0000041 00000000217 14630024004 017632 0 ustar www-data www-data #!/usr/bin/env ruby
# -*- coding: utf-8 -*-
require 'ostruct'
$options ||= OpenStruct.new
$options.v3 = true
require 'kramdown-rfc/command'
kramdown-rfc2629-1.7.14/bin/kramdown-rfc-clean-svg-ids 0000755 0000041 0000041 00000000764 14630024004 022273 0 ustar www-data www-data #!/usr/bin/env ruby
# -*- coding: utf-8 -*-
require 'rexml/document'
require 'kramdown-rfc/rexml-formatters-conservative'
require 'kramdown-rfc/svg-id-cleanup'
def svg_clean_ids(s)
d = REXML::Document.new(s)
d.context[:attribute_quote] = :quote # Set double-quote as the attribute value delimiter
svg_id_cleanup(d)
tr = REXML::Formatters::Conservative.new
o = ''
tr.write(d, o)
o
rescue => detail
warn "*** Can't clean SVG: #{detail}"
d.to_s
end
puts svg_clean_ids(ARGF.read)
kramdown-rfc2629-1.7.14/bin/kramdown-rfc2629 0000755 0000041 0000041 00000000114 14630024004 020151 0 ustar www-data www-data #!/usr/bin/env ruby
# -*- coding: utf-8 -*-
require 'kramdown-rfc/command'
kramdown-rfc2629-1.7.14/bin/kramdown-rfc-cache-subseries-bibxml 0000755 0000041 0000041 00000007104 14630024004 024152 0 ustar www-data www-data #!/usr/bin/env ruby
# prerequisite:
# gem install net-http-persistent
#
# generates referencegroup files for BCP and STD series (like bibxml9)
# unfortunately, needs to re-fetch rfc-index.xml
# uses pretty slow built-in XML parser, so it will take a while even on regen
#
# uses ENV["KRAMDOWN_REFCACHEDIR"] for where you want to have your bibxml9 data
#
require 'rexml/document'
require 'fileutils'
begin
require 'net/http/persistent'
rescue LoadError
warn "*** please install net-http-persistent:"
warn " gem install net-http-persistent"
warn "(prefix by sudo only if required)."
exit 72 # EX_OSFILE
end
TARGET_DIR = ENV["KRAMDOWN_REFCACHEDIR"] || (
path = File.expand_path("~/.cache/xml2rfc")
warn "*** set environment variable KRAMDOWN_REFCACHEDIR to #{path} to actually use the cache"
path
)
FileUtils.mkdir_p(TARGET_DIR)
FileUtils.chdir(TARGET_DIR)
$http = Net::HTTP::Persistent.new name: 'subseries'
KRAMDOWN_PERSISTENT_VERBOSE = true
def get_and_write_resource_persistently(url, fn, age_verbose=false)
t1 = Time.now
response = $http.request(URI(url))
if response.code != "200"
raise "*** Status code #{response.code} while fetching #{url}"
else
File.write(fn, response.body)
end
t2 = Time.now
warn "#{url} -> #{fn} (#{"%.3f" % (t2 - t1)} s)" if KRAMDOWN_PERSISTENT_VERBOSE
if age_verbose
if age = response.get_fields("age")
warn "(working from a web cache, index is #{age.first} seconds stale)"
end
end
end
CLEAR_RET = "\e[K\r" # XXX all the world is ECMA-48 (ISO 6429), no?
def noisy(name)
print "#{name}...#{CLEAR_RET}"
end
def clear_noise
print CLEAR_RET
end
def normalize_name(n)
n.sub(/([A-Z])0+/) {$1}
end
def regress_name(n)
n.sub(/([A-Z])(\d+)/) {"#$1#{"%04d" % $2.to_i}"}
end
def regress_name_dot(n)
n.sub(/([A-Z])(\d+)/) {"#$1.#{"%04d" % $2.to_i}"}
end
def get_ref(rfcname)
name = "reference.#{regress_name_dot(rfcname)}.xml"
begin
file = File.read(name) # no age check
rescue Errno::ENOENT
get_and_write_resource_persistently("https://www.rfc-editor.org/refs/bibxml/" << name, name)
file = File.read(name)
end
d = REXML::Document.new(file)
d.xml_decl.nowrite
"\n" << d.to_s.lstrip
end
def create_bib(series, subname, rfcnames)
p [series, subname, rfcnames]
subname_norm = normalize_name(subname)
refs = %{\n}
refs << %{\n}
refs << rfcnames.map {|x| get_ref(x)}.join
refs << "\n"
File.write("reference.#{regress_name_dot(subname)}.xml", refs)
end
def handle_sub(series, entry)
ids = entry.get_elements("doc-id")
warn "** ids #{ids} #{entry}" unless ids.size == 1
subname = ids.first.text
isalso = entry.get_elements("is-also")
if isalso.size == 1
rfcs = isalso.first.get_elements("doc-id")
if rfcs.size > 0
rfcnames = rfcs.map {|r| r.text}
create_bib(series, subname, rfcnames)
end
end
end
RFCINDEX_SOURCE = "https://www.rfc-editor.org/rfc/rfc-index.xml"
RFCINDEX_COPY = File.basename(RFCINDEX_SOURCE)
get_and_write_resource_persistently(RFCINDEX_SOURCE, RFCINDEX_COPY, true) unless ENV["KRAMDOWN_DONT_REFRESH_RFCINDEX"]
doc = REXML::Document.new(File.read(RFCINDEX_COPY))
REXML::XPath.each(doc.root, "/rfc-index/bcp-entry") { |e| handle_sub("BCP", e) }
REXML::XPath.each(doc.root, "/rfc-index/std-entry") { |e| handle_sub("STD", e) }
kramdown-rfc2629-1.7.14/bin/kramdown-rfc-extract-sourcecode 0000755 0000041 0000041 00000011047 14630024004 023436 0 ustar www-data www-data #!/usr/bin/env ruby -KU
require 'rexml/document' # for SVG and bibxml acrobatics
require 'yaml'
require 'shellwords'
require 'fileutils'
require_relative '../lib/kramdown-rfc/rfc8792'
require_relative '../lib/kramdown-rfc/rexml-all-text.rb'
def clean(s)
s.gsub!(/\//, ":")
s.gsub!(/\A([-.]+)/) {"_" * $1.size }
s.gsub!(/[^-.:\w]+/, "-")
s
end
$seen_slugs = {}
def slugify_like_xml2rfc(s, delta = 0)
s = s.unicode_normalize(:nfkd).force_encoding(Encoding::ASCII).scrub('').downcase
s.gsub!(/[^-\w\s\/]/, '')
s = s.strip
s.gsub!(/[-\s\/]/, '-')
n = 32 - delta
nmax = [s.size, 40 - delta].min
while $seen_slugs[slugex = s[0...n]] && n < nmax
n += 1
end
if $seen_slugs[slugex]
m = 2
while $seen_slugs[slugex = "#{s[0...n]}-#{m}"]
m += 1
if m == 1000
raise ArgumentError, "Can't build distinguishable slug for '#{s}'"
end
end
end
$seen_slugs[slugex] = s
slugex
end
target = nil
dir = nil
unfold = true
targets = [:list, :files, :zip, :yaml]
require 'optparse'
begin
op = OptionParser.new do |opts|
opts.banner = "Usage: kramdown-rfc-extract-sourcecode [options] document.xml"
opts.on("-tFMT", "--to=FMT", targets, "Target format #{targets.map(&:to_s)}") do |v|
target = v
end
opts.on("-dDIR", "--dir=DIR", "Target directory (default: sourcecode)") do |v|
dir = v
end
opts.on("-f", "--[no-]unfold", "RFC8792-unfold (default: yes)") do |v|
unfold = v
end
end
op.parse!
rescue Exception => e
warn e
exit 1
end
if dir
target ||= :files
unless [:files, :zip].include? target
warn "** Unused argument --dir=#{dir}"
end
end
dir ||= "sourcecode"
target ||= :list
gensym = "unnamed-000"
taken = Hash.new { |h, k| h[k] = Hash.new }
warned = Hash.new { |h, k| h[k] = Hash.new }
d = REXML::Document.new(ARGF)
REXML::XPath.each(d.root, "//sourcecode|//artwork") do |x|
if ty = x[:type]
is_svg = false
REXML::XPath.each(x, "svg") do is_svg = true end
ty = clean(ty)
if is_svg && ty != "svg"
warn "** replacing SVG type '#{ty}' by type 'svg'"
ty = "svg"
end
ext = ty
if ty.empty?
ty = "=txt"
ext = "txt"
end
name = x[:name]
name_is_from_slug = false
if !name || name.empty?
REXML::XPath.each(x, "ancestor::*/name[position() = 1]") do |nameel|
unless sname = nameel['slugifiedName']
alltext = nameel.all_text
nameel.add_attribute('slugifiedName',
sname = clean("name-" << slugify_like_xml2rfc(alltext, 5)))
end
name = clean(sname.to_s.sub(/^name-/, ''))
name_is_from_slug = true
end
if !name || name.empty?
name = gensym.succ!.dup
end
name = "#{name}.#{ext}"
end
name = clean(name)
if taken[ty][name]
if name_is_from_slug || is_svg
# rename old entry as well if it was from slug?
nameparts = name.split(".")
if is_svg && nameparts[-1] != "svg"
nameparts << "svg"
end
ext1 = nameparts.pop || "txt"
suffix = "b"
while taken[ty][name = [*nameparts[0...-1], "#{nameparts[-1]}-#{suffix}", ext1].join(".")]
suffix.succ!
end
taken[ty][name] = ''
else
unless warned[ty][name]
warn "Concatenating to #{ty}/#{name}."
warned[ty][name] = true
end
end
else
taken[ty][name] = ''
end
extracted = false
REXML::XPath.each(x, "svg") do |svg|
# According to v3.rnc, there should be only one...
taken[ty][name] << svg.to_s
extracted = true
end
unless extracted
taken[ty][name] << handle_artwork_sourcecode(x.all_text, unfold)
end
end
end
def make_directory_from(dir, taken)
if File.exist?(dir)
bak = "#{dir}.bak"
begin
FileUtils.mv(dir, bak)
rescue Errno::EEXIST
bak.succ!
retry
end
end
FileUtils.mkdir_p(dir)
taken.each { |dir1, v|
FileUtils.mkdir_p("#{dir}/#{dir1}")
v.each { |fn, value|
IO.write("#{dir}/#{dir1}/#{fn}", value)
}
}
end
case target
when :yaml
puts taken.to_yaml
when :list
puts Hash[
taken.map {|k, v|
[k, v.keys]
}
].to_yaml
when :files
make_directory_from(dir, taken)
when :zip
make_directory_from(dir, taken)
zip = "#{dir}.zip"
if File.exist?(zip)
bak = "#{zip}.bak"
begin
FileUtils.mv(zip, bak)
rescue Errno::EEXIST # This doesn't actually happen. XXX
bak.succ!
retry
end
end
cmd = ["zip", "-mr", zip, dir].shelljoin
warn cmd
system(cmd)
end
kramdown-rfc2629-1.7.14/bin/de-gfm 0000755 0000041 0000041 00000001634 14630024004 016403 0 ustar www-data www-data #!/usr/bin/env ruby -Ku
Encoding.default_external = "UTF-8" # wake up, smell the coffee
require 'kramdown'
require 'kramdown-parser-gfm'
options = ''
while /\A-([4bck]+)\z/ === ARGV[0]
ARGV.shift
options << $1
end
if /k/ === options # kramdown
MARKDOWN_BR = "\\\\\n"
end
if /c/ === options # commonmark
MARKDOWN_BR = "\\\n"
end
if /b/ === options # universal HTML
MARKDOWN_BR = "
\n"
end
MARKDOWN_BR ||= " \n" # original Gruber
module Kramdown
module Converter
# Converts an element tree to the kramdown format.
class Kramdown < Base
# Argh
def convert_br(_el, _opts)
MARKDOWN_BR
end
end
end
end
list_indent = 2
list_indent = 4 if /4/ === options
doc = Kramdown::Document.new(ARGF.read, input: 'GFM', gfm_quirks: 'paragraph_end',
list_indent: list_indent)
puts doc.to_kramdown
kramdown-rfc2629-1.7.14/bin/kramdown-rfc-lsr 0000755 0000041 0000041 00000016233 14630024004 020435 0 ustar www-data www-data #!/usr/bin/env ruby -KU
# frozen_string_literal: true
# List Section References from a RFCXML document
#
# (PoC, in urgent need of refactoring)
# Requires xml2rfc and tidy commands in path
# Use without open-uri-cached is untested
require 'rexml/document'
require 'yaml'
require 'json'
require 'shellwords'
require 'fileutils'
begin
require 'open-uri/cached'
rescue LoadError
warn '*** please "gem install open-uri-cached" to enable caching'
require 'open-uri'
end
require_relative '../lib/kramdown-rfc/rexml-all-text.rb'
target = :shortname
require 'optparse'
begin
op = OptionParser.new do |opts|
opts.banner = "Usage: kramdown-rfc-lsr [options] xml-source"
opts.on("-u", "--url", "Source is URL") do |v|
target = :url
end
opts.on("-s", "--shortname", "Source is shortname (default)") do |v|
target = :shortname
end
opts.on("-f", "--file", "Source is filename") do |v|
target = :file
end
end
op.parse!
rescue Exception => e
warn e
exit 1
end
$exit_code = 0
if target != :file && ARGV.size != 1
puts op
exit 1
end
xmlsource = ARGV[0]
if target == :shortname
xmlsource = case xmlsource
when /^(?:rfc)?(\d+)$/i
"https://www.rfc-editor.org/rfc/rfc#{$1.to_i.to_s}.xml"
when /^(?:draft-|I-D.|)(.*-\d\d)$/
"https://www.ietf.org/archive/id/draft-#$1.xml"
# XXX find xml source for most recent version!
else
warn "*** Can't parse shortname #{xmlsource.inspect}"
puts op
exit 1
end
target = :url
end
begin
xml = case target
when :file
ARGF.read
when :url
URI(xmlsource).open.read
else
fail
end
rescue Exception => e
warn "#{xmlsource.inspect}: #{e}"
exit 1
end
doc = REXML::Document.new(xml)
def series_info_to_URI(si)
case si[:name]
when "RFC"
"https://www.rfc-editor.org/rfc/rfc#{si[:value]}.xml"
when "Internet-Draft"
"https://www.ietf.org/archive/id/#{si[:value]}.xml"
end
end
def series_info_to_name(si)
case si[:name]
when "RFC"
"RFC#{si[:value]}"
when "Internet-Draft"
si[:value]
end
end
def section_number_to_pn_candidates(s)
if /^[0-9]/ =~ s
["section-#{s}"]
elsif /[.]/ =~ s
["section-#{s.downcase}", "section-appendix.#{s.downcase}"]
else
["section-appendix.#{s.downcase}"]
end
end
def section_number_to_htmlid(s)
if /^[0-9]/ =~ s
"section-#{s}"
else
"appendix-#{s.upcase}"
end
end
references = Hash[REXML::XPath.match(doc.root, "//reference").map {|r|
si = REXML::XPath.match(r, "seriesInfo").map {|s|
series_info_to_URI(s)}.compact.first
sn = REXML::XPath.match(r, "seriesInfo").map {|s|
series_info_to_name(s)}.compact.first
[r[:anchor], si ? [si, sn] : nil]
}] # XXX duplicates?
heading1 = "# #{xmlsource}"
title = REXML::XPath.first(doc.root, "//title")
heading1 << "\n(#{title.all_text})" if title
puts heading1
per_reference = Hash.new { |h, k| h[k] = Set[]}
REXML::XPath.each(doc.root, "//xref[@section]") do |x|
trg = x[:target]
if x[:relative]
puts "\n## #{x[:target]}#{x[:relative]}: #{x[:section]}"
else
# p x
per_reference[trg] << x[:section]
end
end
def error_out(s)
warn ""
warn s
warn ""
$exit_code = 1
end
def num_expand(s)
s.gsub(/\d+/) {|n| "%09d" % n.to_i}
end
def want_one(secs, what)
case secs.size
when 0
error_out "*** cannot match #{what}"
"*** DOESN'T EXIST ***"
when 1
yield secs.first
else
error_out "*** multiple matches for #{what}"
"*** MULTIPLE MATCHES ***"
end
end
require 'open3'
module OpenURI
class << self
def processed(uri, old, camo, *rest)
newuri = uri.to_s.sub(old, camo) # camo name for processed data
response = Cache.get(newuri) || (
unprocessed = open_uri(uri, *rest).read
fn = [OpenURI::Cache.cache_path, uri.sub(/.*\//, '')].join('/')
File.open(fn, 'wb'){|f| f.write unprocessed }
new_fn = yield newuri, fn
Cache.set(newuri, File.open(new_fn))
)
response
end
def prepped(uri)
processed(uri, /\.xml$/, ".prepped.xml") do |newuri, fn|
_prep_out, s = Open3.capture2("xml2rfc", "--prep", fn)
fail s.inspect unless s.success?
fn.sub(/\.xml$/, ".prepped.xml") # xml2rfc creates new file
end
end
def tidied(uri)
processed(uri, /\.html$/, ".tidied.html") do |newuri, fn|
_prep_out, s = Open3.capture2("tidy", "-mq", "-asxml", "-f", "/dev/null", fn)
fail s.inspect unless s.exited? # can't check success
fn # -m makes in-place change
end
end
end
end
# go through section-referenced documents in sequence
per_reference.keys.sort_by {|x| num_expand(x)}.each do |trg|
uri, sname = references[trg]
add = +''
if sname != trg
add << " [#{sname}]"
end
begin
ref = URI(uri).open.read
refdoc = REXML::Document.new(ref)
if REXML::XPath.match(refdoc.root, "/rfc/front/abstract[@pn]").size == 0
ref = OpenURI.prepped(uri).read
refdoc = REXML::Document.new(ref)
add << " [+prep]"
end
add << " (#{REXML::XPath.match(refdoc.root, "//title").first.all_text})"
rescue OpenURI::HTTPError => e
begin
jsonuri = uri.sub(/\.xml$/, ".json")
refjson = URI(jsonuri).open.read
refdata = JSON.load(refjson)
add << " (#{refdata["title"].strip})"
rescue OpenURI::HTTPError => e
add << " [No XML or JSON]"
rescue Exception => e
warn "*** error getting #{jsonuri.inspect}: #{e}"
end
rescue Exception => e
warn "*** error getting #{uri.inspect}: #{e}"
end
puts "\n## #{trg}#{add}"
unless refdoc
begin
htmluri = uri.sub(/\.xml$/, ".html")
refhtml = OpenURI.tidied(htmluri).read
refhtmldoc = REXML::Document.new(refhtml)
rescue Exception => e
warn "*** error tidying up HTML for #{htmluri.inspect}: #{e}"
end
end
# go through individual section references in sequence
per_reference[trg].to_a.sort_by {|x| num_expand(x)}.each do |s|
add = +''
if refdoc # find section name in XML from anchor s
secpn = section_number_to_pn_candidates(s)
secs = secpn.flat_map{ |c|
REXML::XPath.match(refdoc.root, "//section[@pn=$pn]",
{}, {"pn" => c})}
what = "#{secpn.join(" or ")} in #{trg}"
add << " (#{want_one(secs, what) do |sec|
sec[:title] || sec.elements["name"].all_text
end})"
elsif refhtmldoc # find section name in HTML from anchor s
secpn = section_number_to_htmlid(s)
secs = REXML::XPath.match(refhtmldoc.root,
"//xmlns:a[@id=$pn]/ancestor::xmlns:span",
{"xmlns" => "http://www.w3.org/1999/xhtml"},
{"pn" => secpn})
what = "#{secpn} in #{trg}"
add << " (#{want_one(secs, what) do |sec|
sec.text.sub(/^\.\s+/, '')
end})"
end
puts "* #{/^[0-9]/ =~ s ? "Section" : "Appendix"} #{s}#{add}"
end
end
exit $exit_code
kramdown-rfc2629-1.7.14/bin/kdrfc 0000755 0000041 0000041 00000005026 14630024004 016334 0 ustar www-data www-data #!/usr/bin/env ruby -KU
require 'kramdown-rfc/kdrfc-processor'
require 'optparse'
# try to get this from gemspec.
KDRFC_VERSION=Gem.loaded_specs["kramdown-rfc2629"].version rescue "unknown-version"
kdrfc = KramdownRFC::KDRFC.new
kdrfc.options.txt = true # default
op = OptionParser.new do |opts|
opts.banner = <= 1645567342 # Time.parse("2022-02-22T22:02:22Z").to_i
kdrfc.options.v3 = true # new default from the above date
end
end
warn "*** v2 #{kdrfc.options.v2.inspect} v3 #{kdrfc.options.v3.inspect}" if kdrfc.options.verbose
case ARGV.size
when 1
fn = ARGV[0]
begin
kdrfc.process(fn)
rescue StandardError => e
warn e.to_s
exit 1
end
else
puts op
exit 1
end
kramdown-rfc2629-1.7.14/bin/doilit 0000755 0000041 0000041 00000002331 14630024004 016523 0 ustar www-data www-data #!/usr/bin/env ruby
require 'yaml'
require 'kramdown-rfc2629'
require 'kramdown-rfc/parameterset'
require 'kramdown-rfc/refxml'
require 'kramdown-rfc/doi'
# doilit -c 10.6028/NIST.SP.800-183 10.1016/j.adhoc.2015.04.007 10.1109/MIC.2012.29 10.1145/2507924.2507954
$verbose = false
$fuzzy = false
$handle = "a"
$xml = false
$site = "https://dx.doi.org"
litent = {}
ARGV.each do |doi|
case doi
when "-c"
begin
require 'open-uri/cached'
rescue LoadError
warn '*** please "gem install open-uri-cached" to enable caching'
end
next
when "-f"
$fuzzy = true
next
when "-v"
$verbose = true
next
when /\A-s=(.*)/
$site = $1
next
when /\A-h=(.*)/
$handle = $1
next
when /\A-x=(.*)/
$handle = $1
$xml = true
next
when /\A-/
warn "*** Usage: doilit [-c] [-f] [-v] [-h=handle|-x=xmlhandle] doi..."
exit 1
end
lit = doi_fetch_and_convert(doi, fuzzy: $fuzzy, verbose: $verbose, site: $site)
while litent[$handle]
$handle.succ!
end
litent[$handle] = lit
end
if $xml
litent.each do |k, v|
puts KramdownRFC::ref_to_xml(k, v)
end
else
# 1.9 compat: s/lines/each_line.to_a/
puts litent.to_yaml.gsub(/^/, " ").each_line.to_a[1..-1]
end
kramdown-rfc2629-1.7.14/bin/kramdown-rfc-extract-markdown 0000755 0000041 0000041 00000000476 14630024004 023131 0 ustar www-data www-data #!/usr/bin/env ruby -KU
require 'kramdown-rfc/gzip-clone'
require 'base64'
EMBEDDED_RE = %r{}
embedded = ARGF.read.scan(EMBEDDED_RE)
unless embedded.empty?
puts Gzip.decompress(Base64.decode64(embedded[0][0]))
else
warn "*** No embedded markdown source found!"
end
kramdown-rfc2629-1.7.14/bin/kramdown-rfc-cache-i-d-bibxml 0000755 0000041 0000041 00000006056 14630024004 022624 0 ustar www-data www-data #!/usr/bin/env ruby
# prerequisite:
# gem install net-http-persistent
#
# dumps all bibxml for current, "Active" I-Ds in cache
# reasonably efficient after initial call if the output is retained
#
# requires Ruby 2.4 or above because of "liberal_parsing" option
#
# uses ENV["KRAMDOWN_REFCACHEDIR"] for where you want to have your bibxml3 data
#
require 'csv'
require 'fileutils'
begin
require 'net/http/persistent'
rescue LoadError
warn "*** please install net-http-persistent:"
warn " gem install net-http-persistent"
warn "(prefix by sudo only if required)."
exit 72 # EX_OSFILE
end
TARGET_DIR = ENV["KRAMDOWN_REFCACHEDIR"] || (
path = File.expand_path("~/.cache/xml2rfc")
warn "*** set environment variable KRAMDOWN_REFCACHEDIR to #{path} to actually use the cache"
path
)
FileUtils.mkdir_p(TARGET_DIR)
FileUtils.chdir(TARGET_DIR)
$http = Net::HTTP::Persistent.new name: 'allid'
KRAMDOWN_PERSISTENT_VERBOSE = true
def get_and_write_resource_persistently(url, fn, age_verbose=false)
t1 = Time.now
response = $http.request(URI(url))
if response.code != "200"
raise "*** Status code #{response.code} while fetching #{url}"
else
File.write(fn, response.body)
end
t2 = Time.now
warn "#{url} -> #{fn} (#{"%.3f" % (t2 - t1)} s)" if KRAMDOWN_PERSISTENT_VERBOSE
if age_verbose
if age = response.get_fields("age")
warn "(working from a web cache, index is #{age.first} seconds stale)"
end
end
end
CLEAR_RET = "\e[K\r" # XXX all the world is ECMA-48 (ISO 6429), no?
def noisy(name)
print "#{name}...#{CLEAR_RET}"
end
def clear_noise
print CLEAR_RET
end
ALL_ID2_SOURCE = "https://www.ietf.org/id/all_id2.txt"
ALL_ID2_COPY = ".all_id2.txt"
get_and_write_resource_persistently(ALL_ID2_SOURCE, ALL_ID2_COPY, true) unless ENV["KRAMDOWN_DONT_REFRESH_ALL_ID2"]
ix = File.read(ALL_ID2_COPY).lines.grep_v(/^#/).join
csv = CSV.new(ix, col_sep: "\t", liberal_parsing: true)
drafts = csv.read
active = drafts.select { |d| d[2] == "Active" }
active_names = active.map { |a| a[0] }
puts "#{active_names.size} active drafts"
active_names.each do |name|
if name =~ /\Adraft-(.*)-(\d\d)\z/
namepart = $1
version = $2
name0 = "reference.I-D.#{namepart}.xml"
noisy(name0) if File.exist?(name0)
name1 = "reference.I-D.draft-#{namepart}-#{version}.xml"
if File.exist?(name1)
noisy(name1)
FileUtils.touch(name0) # because name1 already exists, we believe name0 is fresh
else
begin
url0 = "https://datatracker.ietf.org/doc/bibxml3/draft-#{namepart}.xml"
get_and_write_resource_persistently(url0, name0) # get name0 first
url1 = "https://datatracker.ietf.org/doc/bibxml3/draft-#{namepart}-#{version}.xml"
get_and_write_resource_persistently(url1, name1) # then name1 to mark this as updated
rescue => e
warn "*** #{name0}: #{e}"
end
end
else
warn "*** Malformed draft name: #{name}"
end
end
clear_noise
kramdown-rfc2629-1.7.14/bin/kramdown-rfc-autolink-iref-cleanup 0000755 0000041 0000041 00000000273 14630024004 024030 0 ustar www-data www-data #!/usr/bin/env ruby
# -*- coding: utf-8 -*-
require 'rexml/document'
require 'kramdown-rfc/autolink-iref-cleanup'
d = REXML::Document.new(ARGF.read)
autolink_iref_cleanup(d)
puts d.to_s
kramdown-rfc2629-1.7.14/bin/echars 0000755 0000041 0000041 00000003137 14630024004 016511 0 ustar www-data www-data #!/usr/bin/env ruby
require 'unicode/name'
require 'unicode/scripts'
require 'unicode/blocks'
require 'json'
require 'differ'
module Differ
module Format
module Color
class << self
def as_change(change) # monkey patch
as_insert(change) << "\n" << as_delete(change)
end
end
end
end
end
def readable(c)
j = c.to_json
if j.size == 3
j[1...-1]
else
j
end
end
def explain(s)
ret = ''
hist = Hash.new(0)
s.each_char do |c|
hist[c] += 1 unless c.ord.between?(32, 126)
end
hist.delete("\n")
hist.keys.sort.group_by {|c| Unicode::Blocks.block(c)}.each do |block, l|
scripts = Set[]
l.each do |c|
scripts << Unicode::Scripts.scripts(c)
end
ret << "*** #{block}"
ret << " (#{scripts.join})" if scripts.size == 1
ret << "\n"
l.each do |c|
ret << "#{readable(c)}: U+#{"%04X %4d" % [c.ord, hist[c]]
} #{Unicode::Name.correct(c) ||
Unicode::Name.label(c)
}"
ret << " (#{Unicode::Scripts.scripts(c).join(", ")})" if scripts.size != 1
ret << "\n"
end
end
ret
end
s = ARGF.read
es = explain(s)
n = s.unicode_normalize
en = explain(n)
if es == en
puts es
else
puts "*** Warning: some characters are not normalized and are shown in red."
puts " ...showing a normalized variant (NFC) in green."
puts " Lack of normalization may or may not be a problem."
puts " (Characters may appear to be under wrong block heading.)"
puts Differ.diff_by_line(en, es).format_as(:color)
end
kramdown-rfc2629-1.7.14/kramdown-rfc2629.gemspec 0000644 0000041 0000041 00000003561 14630024004 021031 0 ustar www-data www-data spec = Gem::Specification.new do |s|
s.name = 'kramdown-rfc2629'
s.version = '1.7.14'
s.summary = "Kramdown extension for generating RFCXML (RFC 799x)."
s.description = %{An RFCXML (RFC 799x) generating backend for Thomas Leitner's
"kramdown" markdown parser. Mostly useful for RFC writers.}
s.add_dependency('kramdown', '~> 2.4.0')
s.add_dependency('kramdown-parser-gfm', '~> 1.1')
s.add_dependency('certified', '~> 1.0')
s.add_dependency('json_pure', '~> 2.0')
s.add_dependency('unicode-name', '~> 1.0')
s.add_dependency('unicode-blocks', '~> 1.0')
s.add_dependency('unicode-scripts', '~> 1.0')
s.add_dependency('net-http-persistent', '~> 4.0')
s.add_dependency('differ', '~> 0.1')
s.add_dependency('base64', '>= 0.1')
s.files = Dir['lib/**/*.rb'] + %w(README.md LICENSE kramdown-rfc2629.gemspec bin/kdrfc bin/kramdown-rfc bin/kramdown-rfc2629 bin/doilit bin/echars bin/kramdown-rfc-extract-markdown bin/kramdown-rfc-extract-sourcecode bin/kramdown-rfc-lsr data/kramdown-rfc2629.erb data/encoding-fallbacks.txt data/math.json bin/kramdown-rfc-cache-subseries-bibxml bin/kramdown-rfc-autolink-iref-cleanup bin/de-gfm bin/kramdown-rfc-clean-svg-ids)
s.require_path = 'lib'
s.executables = ['kramdown-rfc', 'kramdown-rfc2629', 'doilit', 'echars',
'kramdown-rfc-extract-markdown',
'kramdown-rfc-extract-sourcecode',
'kramdown-rfc-lsr',
'kdrfc', 'kramdown-rfc-cache-i-d-bibxml',
'kramdown-rfc-cache-subseries-bibxml',
'kramdown-rfc-autolink-iref-cleanup',
'de-gfm',
'kramdown-rfc-clean-svg-ids']
s.required_ruby_version = '>= 2.3.0'
# s.requirements = 'wget'
# s.has_rdoc = true
s.author = "Carsten Bormann"
s.email = "cabo@tzi.org"
s.homepage = "http://github.com/cabo/kramdown-rfc"
s.license = 'MIT'
end
kramdown-rfc2629-1.7.14/lib/ 0000755 0000041 0000041 00000000000 14630024004 015310 5 ustar www-data www-data kramdown-rfc2629-1.7.14/lib/kramdown-rfc/ 0000755 0000041 0000041 00000000000 14630024004 017702 5 ustar www-data www-data kramdown-rfc2629-1.7.14/lib/kramdown-rfc/gzip-clone.rb 0000644 0000041 0000041 00000001771 14630024004 022304 0 ustar www-data www-data require 'zlib'
require 'stringio'
# cloned from module ActiveSupport
# A convenient wrapper for the zlib standard library that allows
# compression/decompression of strings with gzip.
#
# gzip = Gzip.compress('compress me!')
# # => "\x1F\x8B\b\x00o\x8D\xCDO\x00\x03K\xCE\xCF-(J-.V\xC8MU\x04\x00R>n\x83\f\x00\x00\x00"
#
# Gzip.decompress(gzip)
# # => "compress me!"
module Gzip
class Stream < StringIO
def initialize(*)
super
set_encoding "BINARY"
end
def close; rewind; end
end
# Decompresses a gzipped string.
def self.decompress(source)
Zlib::GzipReader.new(StringIO.new(source)).read
end
# Compresses a string using gzip, setting mtime to 0
def self.compress_m0(source, level=Zlib::DEFAULT_COMPRESSION, strategy=Zlib::DEFAULT_STRATEGY)
output = Stream.new
gz = Zlib::GzipWriter.new(output, level, strategy)
gz.mtime = 0
gz.write(source)
gz.close
output.string
end
end
# end
kramdown-rfc2629-1.7.14/lib/kramdown-rfc/parameterset.rb 0000644 0000041 0000041 00000004624 14630024004 022731 0 ustar www-data www-data module KramdownRFC
class ParameterSet
include Kramdown::Utils::Html
attr_reader :f
def initialize(y)
raise "*** invalid parameter set #{y.inspect}" unless Hash === y
@f = y
@av = {}
end
attr :av
def [](pn)
@f.delete(pn.to_s)
end
def []=(pn, val)
@f[pn.to_s] = val
end
def default(pn, &block)
@f.fetch(pn.to_s, &block)
end
def default!(pn, value)
default(pn) {
@f[pn.to_s] = value
}
end
def has(pn)
@f[pn.to_s]
end
def has?(pn)
@f.key?(pn.to_s)
end
def escattr(str)
escape_html(str.to_s, :attribute)
end
def van(pn) # pn is a parameter name, possibly with =aliases
names = pn.to_s.split("=")
[self[names.reverse.find{|nm| has?(nm)}], names.first]
end
def attr(pn)
val, an = van(pn)
@av[an.intern] = val
%{#{an}="#{escattr(val)}"} if val # see attrtf below
end
def attrs(*pns)
pns.map{ |pn| attr(pn) if pn }.compact.join(" ")
end
def attrtf(pn) # can do an overriding false value
val, an = van(pn)
@av[an.intern] = val
%{#{an}="#{escattr(val)}"} unless val.nil?
end
def attrstf(*pns)
pns.map{ |pn| attrtf(pn) if pn }.compact.join(" ")
end
def ele(pn, attr=nil, defcontent=nil, markdown=false)
val, an = van(pn)
val ||= defcontent
val = [val] if Hash === val
Array(val).map do |val1|
a = Array(attr).dup
if Hash === val1
val1.each do |k, v|
if k == ":"
val1 = v
else
k = Kramdown::Element.attrmangle(k) || k
a.unshift(%{#{k}="#{escattr(v)}"})
end
end
end
v = val1.to_s.strip
contents =
if markdown
::Kramdown::Converter::Rfc2629::process_markdown(v)
else
escape_html(v)
end
%{<#{[an, *a.map(&:to_s)].join(" ").strip}>#{contents}#{an}>}
end.join(" ")
end
def arr(an, converthash=true, must_have_one=false, &block)
arr = self[an] || []
arr = [arr] if Hash === arr && converthash
arr << { } if must_have_one && arr.empty?
Array(arr).each(&block)
end
def rest
@f
end
def warn_if_leftovers
if !@f.empty?
warn "*** attributes left #{@f.inspect}!"
end
end
end
end
kramdown-rfc2629-1.7.14/lib/kramdown-rfc/rfc8792.rb 0000644 0000041 0000041 00000005674 14630024004 021347 0 ustar www-data www-data
# Note that this doesn't attempt to handle HT characters
def remove_indentation(s)
l = s.lines
indent = l.grep(/\S/).map {|l| l[/^\s*/].size}.min
l.map {|li| li.sub(/^ {0,#{indent}}/, "")}.join
end
def trim_empty_lines_around(s) # this deletes the trailing newline, which may need to be reconstructed
s.sub(/\A(\r?\n)*/, '').sub(/(\r?\n)*\z/, '')
end
def fix_unterminated_line(s)
s.sub(/[^\n]\z/) { "#$&\n" } # XXX
end
def handle_artwork_sourcecode(s, unfold = true)
s = trim_empty_lines_around(s)
s = unfold8792(s) if unfold
fix_unterminated_line(s)
end
FOLD_MSG = "NOTE: '\\' line wrapping per RFC 8792".freeze
UNFOLD_RE = /\A.*#{FOLD_MSG.sub("\\", "(\\\\\\\\\\\\\\\\?)")}.*\n\r?\n/
def unfold8792(s)
if s =~ UNFOLD_RE
indicator = $1
s = $'
sub = case indicator
when "\\"
s.gsub!(/\\\n[ \t]*/, '')
when "\\\\"
s.gsub!(/\\\n[ \t]*\\/, '')
else
fail "indicator" # Cannot happen
end
warn "** encountered RFC 8792 header without folded lines" unless sub
end
s
end
MIN_FOLD_COLUMNS = FOLD_MSG.size
FOLD_COLUMNS = 69
RE_IDENT = /\A[A-Za-z0-9_]\z/
def fold8792_1(s, columns = FOLD_COLUMNS, left = false, dry = false)
if s.index("\t")
warn "*** HT (\"TAB\") in text to be folded. Giving up."
return s
end
if columns < MIN_FOLD_COLUMNS
columns =
if columns == 0
FOLD_COLUMNS
else
warn "*** folding to #{MIN_FOLD_COLUMNS}, not #{columns}"
MIN_FOLD_COLUMNS
end
end
lines = s.lines.map(&:chomp)
did_fold = false
ix = 0
while li = lines[ix]
col = columns
if li[col].nil?
if li[-1] == "\\"
lines[ix..ix] = [li << "\\", ""]
ix += 1
end
ix += 1
else
did_fold = true
min_indent = left || 0
col -= 1 # space for "\\"
while li[col] == " " # can't start new line with " "
col -= 1
end
if col <= min_indent
warn "*** Cannot RFC8792-fold1 to #{columns} cols #{"with indent #{left}" if left} |#{li.inspect}|"
else
if RE_IDENT === li[col] # Don't split IDs
col2 = col
while col2 > min_indent && RE_IDENT === li[col2-1]
col2 -= 1
end
if col2 > min_indent
col = col2
end
end
rest = li[col..-1]
indent = left || columns - rest.size
if !left && li[-1] == "\\"
indent -= 1 # leave space for next round
end
if indent > 0
rest = " " * indent + rest
end
lines[ix..ix] = [li[0...col] << "\\", rest]
end
ix += 1
end
end
if did_fold
msg = FOLD_MSG.dup
if !dry && columns >= msg.size + 4
delta = columns - msg.size - 2 # 2 spaces
half = delta/2
msg = "#{"=" * half} #{msg} #{"=" * (delta - half)}"
end
lines[0...0] = [msg, ""]
lines.map{|x| x << "\n"}.join
else
s
end
end
kramdown-rfc2629-1.7.14/lib/kramdown-rfc/autolink-iref-cleanup.rb 0000644 0000041 0000041 00000001543 14630024004 024430 0 ustar www-data www-data require 'rexml/document'
def autolink_iref_cleanup(d)
d.root.get_elements("//section[@anchor]").each do |sec|
anchor = sec['anchor']
irefs = {}
sec.get_elements(".//xref[@target='#{anchor}'][@format='none']").each do |xr|
ne = xr.previous_element # 9c87e84 iref now before xref
if ne && ne.name == "iref" && (item = ne['item'])
irefs[item] = ne['subitem'] # XXX one subitem only
ne.remove
chi = xr.children
chi[1..-1].reverse.each do |ch|
xr.parent.insert_after(xr, ch)
end
xr.replace_with(chi[0])
end
end
irefs.each do |k, v|
sec.insert_after(sec.get_elements("name").first,
e = REXML::Element.new("iref", sec))
e.attributes["item"] = k
e.attributes["subitem"] = v
e.attributes["primary"] = 'true'
end
end
end
kramdown-rfc2629-1.7.14/lib/kramdown-rfc/rexml-all-text.rb 0000644 0000041 0000041 00000000542 14630024004 023107 0 ustar www-data www-data require 'rexml/document'
module REXML
# all_text: Get all text from descendants that are Text or CData
class Element
def all_text
@children.map {|c| c.all_text}.join
end
end
class Text # also: ancestor of CData
def all_text
value
end
end
class Child
def all_text
''
end
end
end
kramdown-rfc2629-1.7.14/lib/kramdown-rfc/command.rb 0000755 0000041 0000041 00000051100 14630024004 021645 0 ustar www-data www-data #!/usr/bin/env ruby
# -*- coding: utf-8 -*-
require 'kramdown-rfc2629'
require 'kramdown-rfc/parameterset'
require 'kramdown-rfc/refxml'
require 'kramdown-rfc/rfc8792'
require 'yaml'
require 'kramdown-rfc/erb'
require 'date'
# try to get this from gemspec.
KDRFC_VERSION=Gem.loaded_specs["kramdown-rfc2629"].version rescue "unknown-version"
Encoding.default_external = "UTF-8" # wake up, smell the coffee
def add_quote(s)
l = s.lines
l.map {|li| "> #{li}"}.join
end
def process_chunk(s, nested, dedent, fold, quote)
process_includes(s) if nested
s = remove_indentation(s) if dedent
s = fold8792_1(s, *fold) if fold
s = add_quote(s) if quote
s
end
def process_includes(input)
input.gsub!(/^\{::include((?:-[a-z0-9]+)*)\s+(.*?)\}/) {
include_flags = $1
fn = [$2]
chunks = false
nested = false
dedent = false
fold = false
quote = false
include_flags.split("-") do |flag|
case flag
when ""
when "nested"
nested = true
when "quote"
quote = true
when "dedent"
dedent = true
when /\Afold(\d*)(left(\d*))?(dry)?\z/
fold = [$1.to_i, # col 0 for ''
($3.to_i if $2), # left 0 for '', nil if no "left"
$4] # dry
when "all", "last"
fn = fn.flat_map{|n| Dir[n]}
fn = [fn.last] if flag == "last"
chunks = fn.map{ |f|
ret = process_chunk(File.read(f), nested, dedent, fold, quote)
nested = false; dedent = false; fold = false; quote = false
ret
}
else
warn "** unknown include flag #{flag}"
end
end
chunks = fn.map{|f| File.read(f)} unless chunks # no all/last
chunks = chunks.map {|ch| process_chunk(ch, nested, dedent, fold, quote)}
chunks.join.chomp
}
end
def boilerplate(key)
ret = ''
case key.downcase
when /\Abcp14(info)?(\+)?(-tagged)?(-bcp)?\z/i
# $1 $2 $3 $4
if $1
ret << <
*[MUST NOT]:
*[REQUIRED]:
*[SHALL]:
*[SHALL NOT]:
*[SHOULD]:
*[SHOULD NOT]:
*[RECOMMENDED]:
*[NOT RECOMMENDED]:
*[MAY]:
*[OPTIONAL]:
TAGGED
end
if $4 # experimental; idnits complains:
# ** The document seems to lack a both a reference to RFC 2119 and the
# recommended RFC 2119 boilerplate, even if it appears to use RFC 2119
# keywords -- however, there's a paragraph with a matching beginning.
# Boilerplate error?
ret.sub!("{{!RFC2119}} {{!RFC8174}}", "{{!BCP14}}")
end
ret
when /\Arfc\s*7942(info)?\z/i
if $1
ret << < :normative, "?" => :informative }
def yaml_load(input, *args)
begin
if YAML.respond_to?(:safe_load)
begin
YAML.safe_load(input, *args)
rescue ArgumentError
YAML.safe_load(input, permitted_classes: args[0], permitted_symbols: args[1], aliases: args[2])
end
else
YAML.load(input)
end
rescue Psych::SyntaxError => e
warn "*** YAML syntax error: #{e}"
exit 65 # EX_DATAERR
end
end
def process_kramdown_options(coding_override = nil,
smart_quotes = nil, typographic_symbols = nil,
header_kramdown_options = nil)
ascii_target = coding_override && coding_override =~ /ascii/
suppress_typography = ascii_target || $options.v3
entity_output = ascii_target ? :numeric : :as_char;
options = {input: 'RFC2629Kramdown', entity_output: entity_output, link_defs: {}}
if smart_quotes.nil? && suppress_typography
smart_quotes = false
end
if smart_quotes == false
smart_quotes = ["'".ord, "'".ord, '"'.ord, '"'.ord]
end
case smart_quotes
when Array
options[:smart_quotes] = smart_quotes
when nil, true
# nothin
else
warn "*** Can't deal with smart_quotes value #{smart_quotes.inspect}"
end
if typographic_symbols.nil? && suppress_typography
typographic_symbols = false
end
if typographic_symbols == false
typographic_symbols = Hash[::Kramdown::Parser::Kramdown::TYPOGRAPHIC_SYMS.map { |k, v|
if Symbol === v
[v.intern, k]
end
}.compact]
end
# warn [:TYPOGRAPHIC_SYMBOLS, typographic_symbols].to_yaml
case typographic_symbols
when Hash
options[:typographic_symbols] = typographic_symbols
when nil, true
# nothin
else
warn "*** Can't deal with typographic_symbols value #{typographic_symbols.inspect}"
end
if header_kramdown_options
options.merge! header_kramdown_options
end
$global_markdown_options = options # For nested calls in bibref annotation processing and xref text
options
end
XREF_SECTIONS_RE = ::Kramdown::Parser::RFC2629Kramdown::SECTIONS_RE
XSR_PREFIX = "#{XREF_SECTIONS_RE} of "
XSR_SUFFIX = ", (#{XREF_SECTIONS_RE})| \\((#{XREF_SECTIONS_RE})\\)"
XREF_TXT = ::Kramdown::Parser::RFC2629Kramdown::XREF_TXT
XREF_TXT_SUFFIX = " \\(#{XREF_TXT}\\)"
def spacify_re(s)
s.gsub(' ', '[\u00A0\s]+')
end
include ::Kramdown::Utils::Html
def xml_from_sections(input)
unless ENV["KRAMDOWN_NO_SOURCE"]
require 'kramdown-rfc/gzip-clone'
require 'base64'
compressed_input = Gzip.compress_m0(input)
$source = Base64.encode64(compressed_input)
end
sections = input.scan(RE_SECTION)
# resulting in an array; each section is [section-label, nomarkdown-flag, section-text]
line = 1 # skip "---"
sections.each do |section|
section << line
line += 1 + section[2].lines.count
end
# warn "#{line-1} lines"
# the first section is a YAML with front matter parameters (don't put a label here)
# We put back the "---" plus gratuitous blank lines to hack the line number in errors
yaml_in = input[/---\s*/] << sections.shift[2]
ps = KramdownRFC::ParameterSet.new(yaml_load(yaml_in, [Date], [], true))
if v = ps[:v]
warn "*** unsupported RFCXML version #{v}" if v != 3
if $options.v2
warn "*** command line --v2 wins over document's 'v: #{v}'"
else
$options.v3 = true
$options.v = 3
ps.default!(:stand_alone, true)
ps.default!(:ipr, "trust200902")
ps.default!(:pi, {"toc" => true, "sortrefs" => true, "symrefs" => true})
end
end
if r = ENV["KRAMDOWN_RFC_DOCREV"]
warn "** building document revision -#{r}"
unless n = ps.has(:docname) and n.sub!(/-latest\z/, "-#{r}")
warn "** -d#{r}: docname #{n.inspect} doesn't have a '-latest' suffix"
end
end
if o = ps[:'autolink-iref-cleanup']
$options.autolink_iref_cleanup = o
end
if o = ps[:'svg-id-cleanup']
$options.svg_id_cleanup = o
end
coding_override = ps.has(:coding)
smart_quotes = ps[:smart_quotes] || ps[:"smart-quotes"]
typographic_symbols = ps[:typographic_symbols]
header_kramdown_options = ps[:kramdown_options]
kramdown_options = process_kramdown_options(coding_override,
smart_quotes, typographic_symbols,
header_kramdown_options)
# all the other sections are put in a Hash, possibly concatenated from parts there
sechash = Hash.new{ |h,k| h[k] = ""}
snames = [] # a stack of section names
sections.each do |sname, nmdflag, text, line|
# warn [:SNAME, sname, nmdflag, text[0..10]].inspect
nmdin, nmdout = {
"-" => ["", ""], # stay in nomarkdown
"" => NMDTAGS, # pop out temporarily
}[nmdflag || ""]
if sname
snames << sname # "--- label" -> push label (now current)
else
snames.pop # just "---" -> pop label (previous now current)
end
sechash[snames.last] << "#{nmdin}\n#{text}#{nmdout}"
end
ref_replacements = { }
anchor_to_bibref = { }
displayref = {}
[:ref, :normative, :informative].each do |sn|
if refs = ps.has(sn)
warn "*** bad section #{sn}: #{refs.inspect}" unless refs.respond_to? :each
refs.each do |k, v|
if v.respond_to? :to_str
if bibtagsys(v) # enable "foo: RFC4711" as a custom anchor definition
anchor_to_bibref[k] = v.to_str
end
ref_replacements[v.to_str] = k
end
if Hash === v
if aliasname = v.delete("-")
ref_replacements[aliasname] = k
end
if bibref = v.delete("=")
anchor_to_bibref[k] = bibref
end
if dr = v.delete("display")
displayref[k.gsub("/", "_")] = dr
end
end
end
end
end
open_refs = ps[:ref] || { } # consumed
norm_ref = { }
# convenience replacement of {{-coap}} with {{I-D.ietf-core-coap}}
# collect normative/informative tagging {{!RFC2119}} {{?RFC4711}}
sechash.each do |k, v|
next if k == "fluff"
v.gsub!(/{{(#{
spacify_re(XSR_PREFIX)
})?([\w.\/_\-]+@)?(?:([?!])(-)?|(-))([\w._\-]+)(?:=([\w.\/_\-]+))?(#{
XREF_TXT_SUFFIX
})?(#{
spacify_re(XSR_SUFFIX)
})?}}/) do |match|
xsr_prefix = $1
subref = $2
norminform = $3
replacing = $4 || $5
word = $6
bibref = $7
xrt_suffix = $8
xsr_suffix = $9
if replacing
if new = ref_replacements[word]
word = new
else
warn "*** no alias replacement for {{-#{word}}}"
word = "-#{word}"
end
end # now, word is the anchor
if bibref
if old = anchor_to_bibref[word]
if bibref != old
warn "*** conflicting definitions for xref #{word}: #{old} != #{bibref}"
end
else
anchor_to_bibref[word] = bibref
end
end
# things can be normative in one place and informative in another -> normative
# collect norm/inform above and assign it by priority here
if norminform
norm_ref[word] ||= norminform == '!' # one normative ref is enough
end
"{{#{xsr_prefix}#{subref}#{word}#{xrt_suffix}#{xsr_suffix}}}"
end
end
[:normative, :informative].each do |k|
ps.rest[k.to_s] ||= { }
end
norm_ref.each do |k, v|
# could check bibtagsys here: needed if open_refs is nil or string
target = ps.has(v ? :normative : :informative)
warn "*** overwriting #{k}" if target.has_key?(k)
target[k] = open_refs[k] # add reference to normative/informative
end
# note that unused items from ref are considered OK, therefore no check for that here
# also should allow norm/inform check of other references
# {{?coap}} vs. {{!coap}} vs. {{-coap}} (undecided)
# or {{?-coap}} vs. {{!-coap}} vs. {{-coap}} (undecided)
# could require all references to be decided by a global flag
overlap = [:normative, :informative].map { |s| (ps.has(s) || { }).keys }.reduce(:&)
unless overlap.empty?
warn "*** #{overlap.join(', ')}: both normative and informative"
end
stand_alone = ps[:stand_alone]
[:normative, :informative].each do |sn|
if refs = ps[sn]
refs.each do |k, v|
href = ::Kramdown::Parser::RFC2629Kramdown.idref_cleanup(k)
kramdown_options[:link_defs][k] = ["##{href}", nil] # allow [RFC2119] in addition to {{RFC2119}}
bibref = anchor_to_bibref[k] || k
bts, url = bibtagsys(bibref, k, stand_alone)
ann = v.delete("annotation") || v.delete("ann") if Hash === v
if bts && (!v || v == {} || v.respond_to?(:to_str))
if stand_alone
a = %{{: anchor="#{k}"}}
a[-1...-1] = %{ ann="#{escape_html(ann, :attribute)}"} if ann
sechash[sn.to_s] << %{\n#{NMDTAGS[0]}\n#{a}\n#{NMDTAGS[1]}\n}
else
warn "*** please use standalone mode for adding annotations to references" if ann
bts.gsub!('/', '_')
(ps.rest["bibxml"] ||= []) << [bts, url]
sechash[sn.to_s] << %{{bts};\n} # ???
end
else
unless v && Hash === v
warn "*** don't know how to expand ref #{k}"
next
end
if bts && !v.delete("override")
warn "*** warning: explicit settings completely override canned bibxml in reference #{k}"
end
v["ann"] = ann if ann
sechash[sn.to_s] << KramdownRFC::ref_to_xml(href, v)
end
end
end
end
erbfilename = File.expand_path '../../../data/kramdown-rfc2629.erb', __FILE__
erbfile = File.read(erbfilename, coding: "UTF-8")
erb = ERB.trim_new(erbfile, '-')
# remove redundant nomarkdown pop outs/pop ins as they confuse kramdown
input = erb.result(binding).gsub(%r"{::nomarkdown}\s*{:/nomarkdown}"m, "")
ps.warn_if_leftovers
sechash.delete("fluff") # fluff is a "commented out" section
if !sechash.empty? # any sections unused by the ERb file?
warn "*** sections left #{sechash.keys.inspect}!"
end
[input, kramdown_options, coding_override]
end
XML_RESOURCE_ORG_PREFIX = Kramdown::Converter::Rfc2629::XML_RESOURCE_ORG_PREFIX
# return XML entity name, url, rewrite_anchor flag
def bibtagsys(bib, anchor=nil, stand_alone=true)
if bib =~ /\Arfc(\d+)/i
rfc4d = "%04d" % $1.to_i
[bib.upcase,
"#{XML_RESOURCE_ORG_PREFIX}/bibxml/reference.RFC.#{rfc4d}.xml"]
elsif $options.v3 && bib =~ /\A(bcp|std)(\d+)/i
n4d = "%04d" % $2.to_i
[bib.upcase,
"#{XML_RESOURCE_ORG_PREFIX}/bibxml-rfcsubseries-new/reference.#{$1.upcase}.#{n4d}.xml"]
elsif bib =~ /\A([-A-Z0-9]+)\./ &&
(xro = Kramdown::Converter::Rfc2629::XML_RESOURCE_ORG_MAP[$1])
dir, _ttl, rewrite_anchor = xro
bib1 = ::Kramdown::Parser::RFC2629Kramdown.idref_cleanup(bib)
if anchor && bib1 != anchor
if rewrite_anchor
a = %{?anchor=#{anchor}}
else
if !stand_alone
warn "*** selecting a custom anchor '#{anchor}' for '#{bib1}' requires stand_alone mode"
warn " the output will need manual editing to correct this"
end
end
end
[bib1,
"#{XML_RESOURCE_ORG_PREFIX}/#{dir}/reference.#{bib}.xml#{a}"]
end
end
def read_encodings
encfilename = File.expand_path '../../../data/encoding-fallbacks.txt', __FILE__
encfile = File.read(encfilename, coding: "UTF-8")
Hash[encfile.lines.map{|l|
l.chomp!;
x, s = l.split(" ", 2)
[x.hex.chr(Encoding::UTF_8), s || " "]}]
end
FALLBACK = read_encodings
def expand_tabs(s, tab_stops = 8)
s.gsub(/([^\t\n]*)\t/) do
$1 + " " * (tab_stops - ($1.size % tab_stops))
end
end
require 'optparse'
require 'ostruct'
$options ||= OpenStruct.new
op = OptionParser.new do |opts|
opts.banner = < file.xml
Version: #{KDRFC_VERSION}
BANNER
opts.on("-V", "--version", "Show version and exit") do |v|
puts "kramdown-rfc #{KDRFC_VERSION}"
exit
end
opts.on("-H", "--help", "Show option summary and exit") do |v|
puts opts
exit
end
opts.on("-v", "--[no-]verbose", "Run verbosely") do |v|
$options.verbose = v
end
opts.on("-3", "--[no-]v3", "Use RFCXML v3 processing rules") do |v|
$options.v3 = v
end
opts.on("-2", "--[no-]v2", "Use RFCXML v2 processing rules") do |v|
$options.v2 = v
end
end
op.parse!
if $options.v2 && $options.v3
warn "*** can't have v2 and eat v3 cake"
$options.v2 = false
end
if $options.v3.nil? && !$options.v2
if Time.now.to_i >= 1645567342 # Time.parse("2022-02-22T22:02:22Z").to_i
$options.v3 = true # new default from the above date
end
end
warn "*** v2 #{$options.v2.inspect} v3 #{$options.v3.inspect}" if $options.verbose
input = ARGF.read
input.scrub! do |c|
warn "*** replaced invalid UTF-8 byte sequence #{c.inspect} by U+FFFD REPLACEMENT CHARACTER"
0xFFFD.chr(Encoding::UTF_8)
end
if input[0] == "\uFEFF"
warn "*** There is a leading byte order mark. Ignored."
input[0..0] = ''
end
if input[-1] != "\n"
# warn "*** added missing newline at end"
input << "\n" # fix #26
end
process_includes(input) unless ENV["KRAMDOWN_SAFE"]
input.gsub!(/^\{::boilerplate\s+(.*?)\}/) {
bp = boilerplate($1)
delta = bp.lines.count
bp + "\n"
}
if input =~ /[\t]/
warn "*** Input contains HT (\"tab\") characters. Undefined behavior will ensue."
input = expand_tabs(input)
end
if input =~ /\A---/ # this is a sectionized file
do_the_tls_dance unless ENV["KRAMDOWN_DONT_VERIFY_HTTPS"]
input, options, coding_override = xml_from_sections(input)
else
options = process_kramdown_options # all default
end
if input =~ /\A<\?xml/ # if this is a whole XML file, protect it
input = "{::nomarkdown}\n#{input}\n{:/nomarkdown}\n"
end
if $options.v3_used && !$options.v3
warn $options.v3_used
$options.v3_used = nil
$options.v3 = true
end
if coding_override
input = input.encode(Encoding.find(coding_override), fallback: FALLBACK)
end
# 1.4.17: because of UTF-8 bibxml files, kramdown always needs to see UTF-8 (!)
if input.encoding != Encoding::UTF_8
input = input.encode(Encoding::UTF_8)
end
# warn "options: #{options.inspect}"
doc = Kramdown::Document.new(input, options)
$stderr.puts doc.warnings.to_yaml unless doc.warnings.empty?
output = doc.to_rfc2629
if $options.v3_used && !$options.v3
warn $options.v3_used
$options.v3 = true
end
# only reparse output document if cleanup actions required
if $options.autolink_iref_cleanup || $options.svg_id_cleanup
require 'rexml/document'
d = REXML::Document.new(output)
d.context[:attribute_quote] = :quote # Set double-quote as the attribute value delimiter
if $options.autolink_iref_cleanup
require 'kramdown-rfc/autolink-iref-cleanup'
autolink_iref_cleanup(d)
end
if $options.svg_id_cleanup
require 'kramdown-rfc/svg-id-cleanup'
svg_id_cleanup(d)
end
output = d.to_s
end
if coding_override
output = output.encode(Encoding.find(coding_override), fallback: FALLBACK)
end
puts output
kramdown-rfc2629-1.7.14/lib/kramdown-rfc/svg-id-cleanup.rb 0000644 0000041 0000041 00000002253 14630024004 023047 0 ustar www-data www-data require 'rexml/document'
SVG_NAMESPACES = {"svg"=>"http://www.w3.org/2000/svg",
"xlink"=>"http://www.w3.org/1999/xlink"}
def svg_id_cleanup(d)
gensym = "gensym000"
REXML::XPath.each(d.root, "//svg:svg", SVG_NAMESPACES) do |x|
gensym = gensym.succ
# warn "*** SVG"
# warn "*** SVG: #{x.to_s.size}"
found_as_id = Set[]
found_as_href = Set[]
REXML::XPath.each(x, ".//*[@id]", SVG_NAMESPACES) do |y|
# warn "*** ID: #{y}"
name = y.attributes["id"]
if found_as_id === name
warn "*** duplicate ID #{name}"
end
found_as_id.add(name)
y.attributes["id"] = "#{name}-#{gensym}"
end
REXML::XPath.each(x, ".//*[@xlink:href]", SVG_NAMESPACES) do |y|
# warn "*** HREF: #{y}"
name = y.attributes["href"]
name1 = name[1..-1]
if !found_as_id === name1
warn "*** unknown HREF #{name}"
end
found_as_href.add(name1)
y.attributes["xlink:href"] = "#{name}-#{gensym}"
end
found_as_id -= found_as_href
warn "*** warning: unused ID: #{found_as_id.to_a.join(", ")}" unless found_as_id.empty?
end
rescue => detail
warn "*** Can't clean SVG: #{detail}"
end
kramdown-rfc2629-1.7.14/lib/kramdown-rfc/doi.rb 0000644 0000041 0000041 00000006430 14630024004 021005 0 ustar www-data www-data require 'open-uri'
require 'json'
require 'yaml'
ACCEPT_CITE_JSON = {"Accept" => "application/citeproc+json"}
def doi_fetch_and_convert(doi, fuzzy: false, verbose: false, site: "https://dx.doi.org")
doipath = doi.sub(/^([0-9.]+)_/) {"#$1/"} # convert initial _ back to /
# warn "** SUB #{doi} #{doipath}" if doi != doipath
begin
cite = JSON.parse(URI("#{site}/#{doipath}").open(ACCEPT_CITE_JSON).read)
puts cite.to_yaml if verbose
doi_citeproc_to_lit(cite, fuzzy)
rescue OpenURI::HTTPError => e
begin
site = "https://dl.acm.org"
percent_escaped = doipath.gsub("/", "%2F")
path = "#{site}/action/exportCiteProcCitation?targetFile=custom-bibtex&format=bibTex&dois=#{percent_escaped}"
op = URI(path).open # first get a cookie, ignore result
# warn [:META, op.meta].inspect
cook = op.meta['set-cookie'].split('; ', 2)[0]
cite = JSON.parse(URI(path).open("Cookie" => cook).read)
cite = cite["items"].first[doipath]
puts cite.to_yaml if verbose
doi_citeproc_to_lit(cite, fuzzy)
rescue
raise e
end
end
end
def doi_citeproc_to_lit(cite, fuzzy)
lit = {}
ser = lit["seriesinfo"] = {}
refcontent = []
lit["title"] = cite["title"]
if (st = cite["subtitle"]) && Array === st # defensive
st.delete('')
if st != []
lit["title"] << ": " << st.join("; ")
end
end
if authors = cite["author"]
lit["author"] = authors.map do |au|
lau = {}
if (f = au["family"])
if (g = au["given"])
lau["name"] = "#{g} #{f}"
lau["ins"] = "#{g[0]}. #{f}"
else
lau["name"] = "#{f}"
# lau["ins"] = "#{g[0]}. #{f}"
end
end
if (f = au["affiliation"]) && Array === f
names = f.map { |affn|
if Hash === affn && (n = affn["name"]) && String === n
n
end
}.compact
if names.size > 0
lau["org"] = names.join("; ")
end
end
lau
end
end
if iss = cite["issued"]
if dp = iss["date-parts"]
if Integer === (dp = dp[0])[0]
lit["date"] = ["%04d" % dp[0], *dp[1..-1].map {|p| "%02d" % p}].join("-")
end
end
end
if !lit.key?("date") && fuzzy && (iss = cite["created"])
if dp = iss["date-parts"]
if Integer === (dp = dp[0])[0]
lit["date"] = ["%04d" % dp[0], *dp[1..-1].map {|p| "%02d" % p}].join("-")
end
end
end
if (ct = cite["container-title"]) && ct != []
info = []
if v = cite["volume"]
vi = "vol. #{v}"
if (v = cite["journal-issue"]) && (issue = v["issue"])
vi << ", no. #{issue}"
end
info << vi
end
if p = cite["page"]
info << "pp. #{p}"
end
rhs = info.join(", ")
if info != []
ser[ct] = rhs
else
spl = ct.split(" ")
ser[spl[0..-2].join(" ")] = spl[-1]
end
end
if pub = cite["publisher"]
refcontent << pub
# info = []
# if t = cite["type"]
# info << t
# end
# rhs = info.join(", ")
# if info != []
# ser[pub] = rhs
# else
# spl = pub.split(" ")
# ser[spl[0..-2].join(" ")] = spl[-1]
# end
end
["DOI", "ISBN"].each do |st|
if a = cite[st]
ser[st] = a
end
end
if refcontent != []
lit["refcontent"] = refcontent.join(", ")
end
lit
end
kramdown-rfc2629-1.7.14/lib/kramdown-rfc/erb.rb 0000644 0000041 0000041 00000000423 14630024004 020776 0 ustar www-data www-data require 'erb'
class ERB
case version.sub("erb.rb [", "")
when /\A2.1/ # works back to 1.9.1
def self.trim_new(s, trim)
ERB.new(s, nil, trim)
end
else
def self.trim_new(s, trim)
ERB.new(s, trim_mode: trim)
end
end
end
kramdown-rfc2629-1.7.14/lib/kramdown-rfc/kdrfc-processor.rb 0000644 0000041 0000041 00000015574 14630024004 023351 0 ustar www-data www-data require 'uri'
require 'net/http'
require 'net/http/persistent'
require 'open3'
require 'ostruct'
require 'json'
module KramdownRFC
class KDRFC
attr_reader :options
def initialize
@options = OpenStruct.new
end
# )))
KDRFC_PREPEND = [ENV["KDRFC_PREPEND"]].compact
def v3_flag?
[*(@options.v3 ? ["--v3"] : []),
*(@options.v2 ? ["--v2"] : [])]
end
def process_mkd(input, output)
warn "* converting locally from markdown #{input} to xml #{output}" if @options.verbose
o, s = Open3.capture2(*KDRFC_PREPEND, "kramdown-rfc2629", *v3_flag?, input)
if s.success?
File.open(output, "w") do |fo|
fo.print(o)
end
warn "* #{output} written" if @options.verbose
else
raise IOError.new("*** kramdown-rfc failed, status #{s.exitstatus}")
end
end
def run_idnits(*args)
if @options.remote
run_idnits_remotely(*args)
else
run_idnits_locally(*args)
end
end
def run_idnits_locally(txt_fn)
warn "* running idnits locally in txt #{txt_fn}" if @options.verbose
unless system("idnits", txt_fn)
warn "*** problem #$? running idnits" if @options.verbose
warn "*** problem running idnits -- falling back to remote idnits processing"
run_idnits_remotely(txt_fn)
end
end
# curl -s https://author-tools.ietf.org/api/idnits -X POST -F file=@draft-ietf-core-comi.txt -F hidetext=true
IDNITS_WEBSERVICE = ENV["KRAMDOWN_IDNITS_WEBSERVICE"] ||
'https://author-tools.ietf.org/api/idnits'
def run_idnits_remotely(txt_fn)
url = URI(IDNITS_WEBSERVICE)
req = Net::HTTP::Post.new(url)
form = [["file", File.open(txt_fn),
{filename: "input.txt",
content_type: "text/plain"}],
["hidetext", "true"]]
diag = ["url/form: ", url, form].inspect
req.set_form(form, 'multipart/form-data')
warn "* requesting idnits at #{url}" if @options.verbose
t0 = Time.now
res = persistent_http.request(url, req)
warn "* elapsed time: #{Time.now - t0}" if @options.verbose
case res
when Net::HTTPBadRequest
result = checked_json(res.body)
raise IOError.new("*** Remote Error: #{result["error"]}")
when Net::HTTPOK
case res.content_type
when 'text/plain'
if res.body == ''
raise IOError.new("*** HTTP response is empty with status #{res.code}, not written")
end
puts res.body
else
warning = "*** HTTP response has unexpected content_type #{res.content_type} with status #{res.code}, #{diag}"
warning << "\n"
warning << res.body
raise IOError.new(warning)
end
else
raise IOError.new("*** HTTP response: #{res.code}, #{diag}")
end
end
def process_xml(*args)
if @options.remote
process_xml_remotely(*args)
else
process_xml_locally(*args)
end
end
def process_xml_locally(input, output, *flags)
warn "* converting locally from xml #{input} to txt #{output}" if @options.verbose
begin
o, s = Open3.capture2(*KDRFC_PREPEND, "xml2rfc", *v3_flag?, *flags, input)
puts o
if s.success?
warn "* #{output} written" if @options.verbose
else
raise IOError.new("*** xml2rfc failed, status #{s.exitstatus} (possibly try with -r)")
end
rescue Errno::ENOENT
warn "*** falling back to remote xml2rfc processing (web service)" # if @options.verbose
process_xml_remotely(input, output, *flags)
end
end
# curl https://author-tools.ietf.org/api/render/text -X POST -F "file=@..."
XML2RFC_WEBSERVICE = ENV["KRAMDOWN_XML2RFC_WEBSERVICE"] ||
'https://author-tools.ietf.org/api/render/'
MODE_AS_FORMAT = {
"--text" => "text",
"--html" => "html",
"--v2v3" => "xml",
"--pdf" => "pdf",
}
def checked_json(t)
begin
JSON.load(t)
rescue => e
raise IOError.new("*** JSON result: #{e.detailed_message}, #{diag}")
end
end
def persistent_http
$http ||= Net::HTTP::Persistent.new name: 'kramdown-rfc'
end
def process_xml_remotely(input, output, *flags)
format = flags[0] || "--text"
warn "* converting remotely from xml #{input} to #{format} #{output}" if @options.verbose
maf = MODE_AS_FORMAT[format]
unless maf
raise ArgumentError.new("*** don't know how to convert remotely from xml #{input} to #{format} #{output}")
end
url = URI(XML2RFC_WEBSERVICE + maf)
req = Net::HTTP::Post.new(url)
form = [["file", File.open(input),
{filename: "input.xml",
content_type: "text/plain"}]]
diag = ["url/form: ", url, form].inspect
req.set_form(form, 'multipart/form-data')
warn "* requesting at #{url}" if @options.verbose
t0 = Time.now
res = persistent_http.request(url, req)
warn "* elapsed time: #{Time.now - t0}" if @options.verbose
case res
when Net::HTTPBadRequest
result = checked_json(res.body)
raise IOError.new("*** Remote Error: #{result["error"]}")
when Net::HTTPOK
case res.content_type
when 'application/json'
if res.body == ''
raise IOError.new("*** HTTP response is empty with status #{res.code}, not written")
end
# warn "* res.body #{res.body}" if @options.verbose
result = checked_json(res.body)
if logs = result["logs"]
if errors = logs["errors"]
errors.each do |err|
warn("*** Error: #{err}")
end
end
if warnings = logs["warnings"]
warnings.each do |w|
warn("** Warning: #{w}")
end
end
end
raise IOError.new("*** No useful result from remote") unless result["url"]
res = persistent_http.request(URI(result["url"]))
warn "* result content type #{res.content_type}" if @options.verbose
if res.body == ''
raise IOError.new("*** Second HTTP response is empty with status #{res.code}, not written")
end
File.open(output, "w") do |fo|
fo.print(res.body)
end
warn "* #{output} written" if @options.verbose
else
warning = "*** HTTP response has unexpected content_type #{res.content_type} with status #{res.code}, #{diag}"
warning << "\n"
warning << res.body
raise IOError.new(warning)
end
else
raise IOError.new("*** HTTP response: #{res.code}, #{diag}")
end
end
def process_the_xml(fn, base)
process_xml(fn, "#{base}.prepped.xml", "--preptool") if @options.prep
process_xml(fn, "#{base}.v2v3.xml", "--v2v3") if @options.v2v3
process_xml(fn, "#{base}.txt") if @options.txt || @options.idnits
process_xml(fn, "#{base}.html", "--html") if @options.html
process_xml(fn, "#{base}.pdf", "--pdf") if @options.pdf
run_idnits("#{base}.txt") if @options.idnits
end
def process(fn)
case fn
when /(.*)\.xml\z/
if @options.xml_only
warn "*** You already have XML"
else # FIXME: copy/paste
process_the_xml(fn, $1)
end
when /(.*)\.mk?d\z/
xml = "#$1.xml"
process_mkd(fn, xml)
process_the_xml(xml, $1) unless @options.xml_only
when /(.*)\.txt\z/
run_idnits(fn) if @options.idnits
else
raise ArgumentError.new("Unknown file type: #{fn}")
end
end
# (((
end
end
kramdown-rfc2629-1.7.14/lib/kramdown-rfc/refxml.rb 0000644 0000041 0000041 00000015622 14630024004 021532 0 ustar www-data www-data require 'kramdown-rfc/erb'
module KramdownRFC
extend Kramdown::Utils::Html
def self.escattr(str)
escape_html(str.to_s, :attribute)
end
AUTHOR_ATTRIBUTES = %w{
initials surname fullname
asciiInitials asciiSurname asciiFullname
role
}
def self.ref_to_xml(k, v)
vps = KramdownRFC::ParameterSet.new(v)
erb = ERB.trim_new <<-REFERB, '-'
>
<%= vps.ele("title") -%>
<% vps.arr("author", true, true) do |au|
aups = authorps_from_hash(au)
-%>
>
<%= aups.ele("organization=org", aups.attr("abbrev=orgabbrev"), "") %>
<% aups.warn_if_leftovers -%>
<% end -%>
/>
<% vps.arr("seriesinfo", false) do |k, v| -%>
<% end -%>
<% vps.arr("format", false) do |k, v| -%>
<% end -%>
<%= vps.ele("annotation=ann", nil, nil, true) -%>
<%= vps.ele("refcontent=rc", nil, nil, true) -%>
REFERB
ret = erb.result(binding)
vps.warn_if_leftovers
ret
end
def self.treat_multi_attribute_member(ps, an)
value = ps.rest[an]
if Hash === value
value.each do |k, v|
ps.rest[if k == ':'
an
else
Kramdown::Element.attrmangle(k + an) ||
Kramdown::Element.attrmangle(k) ||
k
end] = v
end
end
end
def self.initializify(s) # XXX Jean-Pierre
w = '\p{Lu}\p{Lo}'
if s =~ /\A[-.#{w}]+[.]/u
$&
elsif s =~ /\A([#{w}])[^-]*/u
ret = "#$1."
while (s = $') && s =~ /\A(-[\p{L}])[^-]*/u
ret << "#$1."
end
ret
else
warn "*** Can't initializify #{s}"
s
end
end
def self.looks_like_initial(s)
s =~ /\A[\p{Lu}\p{Lo}]([-.][\p{Lu}\p{Lo}]?)*\z/u
end
def self.initials_from_parts_and_surname(aups, parts, s)
ssz = s.size
nonsurname = parts[0...-ssz]
if (ns = parts[-ssz..-1]) != s
warn "*** inconsistent surnames #{ns} and #{s}"
end
nonsurname.map{|x| initializify(x)}.join(" ")
end
def self.handle_ins(aups, ins_k, initials_k, surname_k)
if ins = aups[ins_k]
parts = ins.split('.').map(&:strip) # split on dots first
if parts == []
warn "*** an empty '#{ins_k}:' value is not useful, try leaving it out"
return
end
# Coalesce H.-P.
i = 1; while i < parts.size
if parts[i][0] == "-"
parts[i-1..i] = [parts[i-1] + "." + parts[i]]
else
i += 1
end
end
# Multiple surnames in ins?
parts[-1..-1] = parts[-1].split
s = if surname = aups.rest[surname_k]
surname.split
else parts.reverse.take_while{|x| !looks_like_initial(x)}.reverse
end
aups.rest[initials_k] = initials_from_parts_and_surname(aups, parts, s)
aups.rest[surname_k] = s.join(" ")
end
end
def self.handle_name(aups, fn_k, initials_k, surname_k)
if name = aups.rest[fn_k]
names = name.split(/ *\| */, 2) # boundary for given/last name
if names == []
warn "*** an empty '#{fn_k}:' value is not useful, try leaving it out"
return
end
if names[1]
aups.rest[fn_k] = name = names.join(" ") # remove boundary
if surname = aups.rest[surname_k]
if surname != names[1]
warn "*** inconsistent embedded surname #{names[1]} and surname #{surname}"
end
end
aups.rest[surname_k] = names[1]
end
parts = name.split
if parts == []
warn "*** a blank '#{fn_k}:' value is not useful, try leaving it out"
return
end
surname = aups.rest[surname_k] || parts[-1]
s = surname.split
aups.rest[initials_k] ||= initials_from_parts_and_surname(aups, parts, s)
aups.rest[surname_k] = s.join(" ")
end
end
def self.authorps_from_hash(au)
aups = KramdownRFC::ParameterSet.new(au)
if n = aups[:name]
warn "** both name #{n} and fullname #{fn} are set on one author" if fn = aups.rest["fullname"]
aups.rest["fullname"] = n
usename = true
end
["fullname", "ins", "initials", "surname"].each do |an|
treat_multi_attribute_member(aups, an)
end
handle_ins(aups, :ins, "initials", "surname")
handle_ins(aups, :asciiIns, "asciiInitials", "asciiSurname")
# hack ("heuristic for") initials and surname from name
# -- only works for people with exactly one last name and uncomplicated first names
# -- add surname for people with more than one last name
if usename
handle_name(aups, "fullname", "initials", "surname")
handle_name(aups, "asciiFullname", "asciiInitials", "asciiSurname")
end
aups
end
# The below anticipates the "postalLine" changes.
# If a postalLine is used (abbreviated "postal" in YAML),
# non-postalLine elements are appended as further postalLines.
# This prepares for how "country" is expected to be handled
# specially with the next schema update.
# So an address is now best keyboarded as:
# postal:
# - Foo Street
# - 28359 Bar
# country: Germany
PERSON_ERB = <<~ERB
<<%= element_name%> <%=aups.attrs(*AUTHOR_ATTRIBUTES)%>>
<%= aups.ele("organization=org", aups.attrs("abbrev=orgabbrev",
*[$options.v3 && "ascii=orgascii"]), "") %>
<% postal_elements = %w{extaddr pobox street cityarea city region code sortingcode country postal}.select{|gi| aups.has(gi)}
if postal_elements != [] -%>
<% if pl = postal_elements.delete("postal") -%>
<%= aups.ele("postalLine=postal") %>
<% postal_elements.each do |gi| -%>
<%= aups.ele("postalLine=" << gi) %>
<% end -%>
<% else -%>
<% postal_elements.each do |gi| -%>
<%= aups.ele(gi) %>
<% end -%>
<% end -%>
<% end -%>
<% %w{phone facsimile email uri}.select{|gi| aups.has(gi)}.each do |gi| -%>
<%= aups.ele(gi) %>
<% end -%>
<%= element_name%>>
ERB
def self.person_element_from_aups(element_name, aups)
erb = ERB.trim_new(PERSON_ERB, '-')
erb.result(binding)
end
def self.dateattrs(date)
begin
case date
when /\A\d\d\d\d\z/
%{year="#{date}"}
when Integer
%{year="#{"%04d" % date}"}
when String
Date.parse("#{date}-01").strftime(%{year="%Y" month="%B"})
when Date
date.strftime(%{year="%Y" month="%B" day="%d"})
when Array # this allows to explicitly give a string
%{year="#{date.join(" ")}"}
when nil
%{year="n.d."}
end
rescue ArgumentError
warn "*** Invalid date: #{date} -- use 2012, 2012-07, or 2012-07-28"
end
end
end
kramdown-rfc2629-1.7.14/lib/kramdown-rfc/rexml-formatters-conservative.rb 0000644 0000041 0000041 00000001742 14630024004 026254 0 ustar www-data www-data require 'rexml/document'
module REXML
module Formatters
# The Conservative formatter writes an XML document that parses to an
# identical document as the source document. This means that no extra
# whitespace nodes are inserted, and whitespace within text nodes is
# preserved. Attributes are not sorted.
class Conservative < Default
def initialize
@indentation = 0
@level = 0
@ie_hack = false
end
protected
def write_element( node, output )
output << "<#{node.expanded_name}"
node.attributes.each_attribute do |attr|
output << " "
attr.write( output )
end unless node.attributes.empty?
if node.children.empty?
output << "/"
else
output << ">"
node.children.each { |child|
write( child, output )
}
output << "#{node.expanded_name}"
end
output << ">"
end
end
end
end
kramdown-rfc2629-1.7.14/lib/kramdown-rfc2629.rb 0000644 0000041 0000041 00000170075 14630024004 020564 0 ustar www-data www-data # -*- coding: utf-8 -*-
#
#--
# Copyright (C) 2009-2010 Thomas Leitner
# Copyright (C) 2010-2014 Carsten Bormann
#
# This file was derived from a part of the kramdown gem which is licensed under the MIT license.
# This derived work is also licensed under the MIT license, see LICENSE.
#++
#
require 'shellwords'
raise "sorry, 1.8 was last decade" unless RUBY_VERSION >= '1.9'
gem 'kramdown', '~> 2.4.0'
require 'kramdown'
my_span_elements = %w{list xref eref iref cref spanx vspace}
Kramdown::Parser::Html::Constants::HTML_SPAN_ELEMENTS.concat my_span_elements
require 'rexml/parsers/baseparser'
require 'open3' # for math
require 'json' # for math
require 'rexml/document' # for SVG and bibxml acrobatics
require 'kramdown-rfc/doi' # for fetching information for a DOI
require 'kramdown-rfc/rfc8792'
class Object
def deep_clone
Marshal.load(Marshal.dump(self))
end
end
module Kramdown
Kramdown::Options.define(:ol_start_at_first_marker, Kramdown::Options::Boolean, false, <<~EOF)
If this option is `true`, an ordered list ( a.length }
regexps = [Regexp.union(*sorted_abbrevs.map {|k|
/#{Regexp.escape(k).gsub(/\\\s/, "[\\s\\p{Z}]+").force_encoding(Encoding::UTF_8)}/})]
regexps << /(?=(?:\W|^)#{regexps.first}(?!\w))/ # regexp should only match on word boundaries
# warn regexps.inspect
end
super(el, regexps)
end
def initialize(*doc)
super
@span_parsers.unshift(:xref)
@span_parsers.unshift(:iref)
@span_parsers.unshift(:span_pi)
@block_parsers.unshift(:block_pi)
end
XREF_BASE = /#{REXML::XMLTokens::NAME_CHAR}+/ # a token for a reference
XREF_TXT = /(?:[^\(]|\([^\)]*\))+/ # parenthesized text
XREF_RE = /#{XREF_BASE}(?: \(#{XREF_TXT}\))?/
XREF_RE_M = /\A(#{XREF_BASE})(?: \((#{XREF_TXT})\))?/ # matching version of XREF_RE
XREF_SINGLE = /(?:Section|Appendix) #{XREF_RE}/
XREF_MULTI = /(?:Sections|Appendices) (?:#{XREF_RE}, )*#{XREF_RE},? and #{XREF_RE}/
XREF_ANY = /(?:#{XREF_SINGLE}|#{XREF_MULTI})/
SECTIONS_RE = /(?:#{XREF_ANY} and )?#{XREF_ANY}/
def self.idref_cleanup(href)
# can't start an IDREF with a number or reserved start
if href =~ / /
if $options.v3
warn "** space(s) in cross-reference '#{href}' -- are you trying to use section references?"
else
warn "** space(s) in cross-reference '#{href}' -- note that section references are supported in v3 mode only."
end
end
href.gsub(/\A(?:[0-9]|section-|u-|figure-|table-|iref-)/) { "_#{$&}" }
end
def rfc_mention(target1) # only works for RFCnnnn
target1 =~ /\A([A-Z]*)(.*)\z/
"#$1 #$2 "
end
def handle_bares(s, attr, format, href, last_join = nil)
if s.match(/\A(#{XREF_ANY}) and (#{XREF_ANY})\z/)
handle_bares($1, {}, nil, href, " and ")
handle_bares($2, {}, nil, href, " of ")
return
end
href = href.split(' ')[0] # Remove any trailing (...)
target1, target2 = href.split("@", 2)
multi = last_join != nil
(sn, s) = s.split(' ', 2)
loop do
m = s.match(/\A#{XREF_RE_M}(, (?:and )?| and )?/)
break if not m
if not multi and not m[2] and not m[3] and not target2
# Modify |attr| if there is a single reference. This can only be
# used if there is only one section reference and the section part
# has no title.
attr['section'] = m[1]
attr['sectionFormat'] = format
attr['text'] = m[2]
return
end
if sn
@tree.children << Element.new(:text, "#{sn} ", {})
sn = nil
end
multi = true
s[m[0]] = ''
attr1 = { 'target' => target1, 'section' => m[1], 'sectionFormat' => 'bare', 'text' => m[2] }
@tree.children << Element.new(:xref, nil, attr1)
andof = m[3] || last_join || " of "
if andof == " of " && target2
andof += rfc_mention(target1)
end
@tree.children << Element.new(:text, andof, {})
end
end
XREF_START = /\{\{(?:(?:\{(.*?\n??.*?)\}(?:\{(.*?\n??.*?)\})?)|(\X*?))((?:\}\})|\})/u
# Introduce new {{target}} syntax for empty xrefs, which would
# otherwise be an ugly  or 
# (I'd rather use [[target]], but that somehow clashes with links.)
def parse_xref
@src.pos += @src.matched_size
unless @src[4] == "}}"
warn "*** #{@src[0]}: unmatched braces #{@src[4].inspect}"
end
if contact_name = @src[1]
attr = {'fullname' => contact_name.gsub("\n", " ")}
if ascii_name = @src[2]
attr["asciiFullname"] = ascii_name.gsub("\n", " ")
end
el = Element.new(:contact, nil, attr)
else
href = @src[3]
attr = {}
handled_subref = false
if $options.v3
# match Section ... of ...; set section, sectionFormat
case href.gsub(/[\u00A0\s]+/, ' ') # may need nbsp and/or newlines
when /\A(#{SECTIONS_RE}) of (.*)\z/
href = $2
handle_bares($1, attr, "of", href)
handled_subref = true
when /\A(.*), (#{SECTIONS_RE})\z/
href = $1
handle_bares($2, attr, "comma", href)
handled_subref = true
when /\A(.*) \((#{SECTIONS_RE})\)\z/
href = $1
handle_bares($2, attr, "parens", href)
handled_subref = true
when /#{XREF_RE_M}<(.+)\z/
href = $3
if $2
attr['section'] = $2
attr['relative'] = "#" << $1
else
attr['section'] = $1
end
attr['sectionFormat'] = 'bare'
when /\A<<(.+)\z/
href = $1
attr['format'] = 'title'
when /\A<(.+)\z/
href = $1
attr['format'] = 'counter'
end
end
if href.match(/#{XREF_RE_M}\z/)
href = $1
attr['text'] = $2
end
target1, target2 = href.split("@", 2) # should do this only for sectionref...
if target2
href = target2
unless handled_subref
@tree.children << Element.new(:text, rfc_mention(target1), {})
end
end
href = self.class.idref_cleanup(href)
attr['target'] = href
el = Element.new(:xref, nil, attr)
end
@tree.children << el
end
define_parser(:xref, XREF_START, '\{\{')
IREF_START = /\(\(\((.*?)\)\)\)/u
# Introduce new (((target))) syntax for irefs
def parse_iref
@src.pos += @src.matched_size
href = @src[1]
el = Element.new(:iref, nil, {'target' => href}) # XXX
@tree.children << el
end
define_parser(:iref, IREF_START, '\(\(\(')
# HTML_INSTRUCTION_RE = /<\?(.*?)\?>/m # still defined!
# warn [:OPT_SPACE, OPT_SPACE, HTML_INSTRUCTION_RE].inspect
PI_BLOCK_START = /^#{OPT_SPACE}<\?/u
def parse_block_pi
# warn [:BLOCK].inspect
line = @src.current_line_number
if (result = @src.scan(HTML_INSTRUCTION_RE))
@tree.children << Element.new(:xml_pi, result, nil, category: :block, location: line)
@src.scan(TRAILING_WHITESPACE)
true
else
false
end
end
define_parser(:block_pi, PI_BLOCK_START)
PI_SPAN_START = /<\?/u
def parse_span_pi
# warn [:SPAN].inspect
line = @src.current_line_number
if (result = @src.scan(HTML_INSTRUCTION_RE))
@tree.children << Element.new(:xml_pi, result, nil, category: :span, location: line)
else
add_text(@src.getch)
end
end
define_parser(:span_pi, PI_SPAN_START, '<\?')
# warn [:HERE, @@parsers.keys].inspect
end
end
class Element
# Not fixing studly element names postalLine and seriesInfo yet
# occasionally regenerate the studly attribute name list via
# script in data/studly.rb
STUDLY_ATTR = %w(
asciiAbbrev asciiFullname asciiInitials asciiName asciiSurname
asciiValue blankLines derivedAnchor derivedContent derivedCounter
derivedLink displayFormat docName expiresDate hangIndent hangText
indexInclude iprExtract keepWithNext keepWithPrevious originalSrc
prepTime quoteTitle quotedFrom removeInRFC sectionFormat seriesNo
showOnFrontPage slugifiedName sortRefs submissionType symRefs tocDepth
tocInclude
)
STUDLY_ATTR_MAP = Hash[STUDLY_ATTR.map {|s| [s.downcase, s]}]
TRUTHY = Hash.new {|h, k| k}
TRUTHY["false"] = false
TRUTHY["no"] = false
# explicit or automatic studlification
# note that explicit (including trailing "_") opts out of automatic
def self.attrmangle(k)
if (d = k.gsub(/_(.|$)/) { $1.upcase }) != k or d = STUDLY_ATTR_MAP[k]
d
end
end
def rfc2629_fix(opts)
if a = attr
if anchor = a.delete('id')
a['anchor'] = ::Kramdown::Parser::RFC2629Kramdown.idref_cleanup(anchor)
end
if anchor = a.delete('href')
a['target'] = ::Kramdown::Parser::RFC2629Kramdown.idref_cleanup(anchor)
end
if lang = a.delete('lang-')
a['xml:lang'] = lang
end
if av = a.delete('noabbrev') # pseudo attribute -> opts
opts = opts.merge(noabbrev: TRUTHY[av]) # updated copy
end
attr.keys.each do |k|
if d = self.class.attrmangle(k)
a[d] = a.delete(k)
end
end
end
opts
end
end
module Converter
# Converts a Kramdown::Document to HTML.
class Rfc2629 < Base
# we use these to do XML stuff, too
include ::Kramdown::Utils::Html
def el_html_attributes(el)
html_attributes(el.attr)
end
def el_html_attributes_with(el, defattr)
html_attributes(defattr.merge(el.attr))
end
# :stopdoc:
KRAMDOWN_PERSISTENT = ENV["KRAMDOWN_PERSISTENT"]
KRAMDOWN_PERSISTENT_VERBOSE = /v/ === KRAMDOWN_PERSISTENT
if KRAMDOWN_PERSISTENT
begin
require 'net/http/persistent'
$http = Net::HTTP::Persistent.new name: 'kramdown-rfc'
rescue Exception => e
warn "** Not using persistent HTTP -- #{e}"
warn "** To silence this message and get full speed, try:"
warn "** gem install net-http-persistent"
warn "** If this doesn't work, you can ignore this warning."
end
end
# Defines the amount of indentation used when nesting XML tags.
INDENTATION = 2
# Initialize the XML converter with the given Kramdown document +doc+.
def initialize(*doc)
super
@sec_level = 1
@location_delta = 100000 # until reset
@location_correction = 0 # pre-scanning corrections
@in_dt = 0
@footnote_names_in_use = {}
end
def correct_location(location)
location + @location_delta + @location_correction
end
def convert(el)
opts = el.options[:options]
# warn "** tree opts #{opts.inspect}"
if nested_ol_types = @options[:nested_ol_types]
opts[:nested_ol_types] ||= nested_ol_types
# warn "** tree opts out #{opts.inspect}"
end
indent = -INDENTATION
if el.children[-1].type == :raw
raw = convert1(el.children.pop, indent, opts)
end
"#{convert1(el, indent, opts)}#{end_sections(1, indent, el.options[:location])}#{raw}"
end
def convert1(el, indent, opts = {})
nopts = el.rfc2629_fix(opts)
send("convert_#{el.type}", el, indent, nopts)
end
def inner_a(el, indent, opts)
indent += INDENTATION
el.children.map do |inner_el|
nopts = inner_el.rfc2629_fix(opts)
send("convert_#{inner_el.type}", inner_el, indent, nopts)
end
end
def inner(el, indent, opts)
inner_a(el, indent, opts).join('')
end
def convert_blank(el, indent, opts)
"\n"
end
def convert_text(el, indent, opts)
escape_html(el.value, :text)
end
def convert_p(el, indent, opts)
if (el.children.size == 1 && el.children[0].type == :img) || opts[:unpacked]
inner(el, indent, opts) # Part of the bad reference hack
else
"#{' '*indent}#{inner(el, indent, opts)}\n"
end
end
def saner_generate_id(value)
generate_id(value).gsub(/-+/, '-')
end
def self.process_markdown1(v) # Uuh. Heavy coupling.
doc = ::Kramdown::Document.new(v, $global_markdown_options)
$stderr.puts doc.warnings.to_yaml unless doc.warnings.empty?
doc.to_rfc2629
end
def self.process_markdown(v)
process_markdown1(v)[3..-6] # skip ...\n
end
def self.process_markdown_to_rexml(v)
s = process_markdown1(v)
REXML::Document.new(s)
end
SVG_COLORS = Hash.new {|h, k| k}
< white
def svg_munch_color(c, fill)
c = SVG_COLORS[c]
case c
when /\A#(..)(..)(..)\z/
if hex_to_lin($1)*0.2126 + hex_to_lin($2)*0.7152 + hex_to_lin($3)*0.0722 >= B_W_THRESHOLD
'white'
else
'black'
end
when 'none'
'none' if fill # delete for stroke
else
c
end
end
SVG_NAMESPACES = {"xmlns"=>"http://www.w3.org/2000/svg",
"xlink"=>"http://www.w3.org/1999/xlink"}
def svg_clean_kgt(s)
d = REXML::Document.new(s)
REXML::XPath.each(d.root, "/xmlns:svg", SVG_NAMESPACES) do |x|
if (w = x.attributes["width"]) && (h = x.attributes["height"])
x.attributes["viewBox"] = "0 0 %d %d" % [w, h]
end
if x.attributes["viewBox"]
x.attributes["width"] = nil
x.attributes["height"] = nil
end
end
REXML::XPath.each(d.root, "//rect|//line|//path") do |x|
x.attributes["fill"] = "none"
x.attributes["stroke"] = "black"
x.attributes["stroke-width"] = "1.5"
end
d.to_s
rescue => detail
warn "*** Can't clean SVG: #{detail}"
d
end
def svg_clean(s) # expensive, risky
d = REXML::Document.new(s)
REXML::XPath.each(d.root, "//*[@shape-rendering]") { |x| x.attributes["shape-rendering"] = nil } #; warn x.inspect }
REXML::XPath.each(d.root, "//*[@text-rendering]") { |x| x.attributes["text-rendering"] = nil } #; warn x.inspect }
REXML::XPath.each(d.root, "//*[@stroke]") { |x| x.attributes["stroke"] = svg_munch_color(x.attributes["stroke"], false) }
REXML::XPath.each(d.root, "//*[@fill]") { |x| x.attributes["fill"] = svg_munch_color(x.attributes["fill"], true) }
REXML::XPath.each(d.root, "//*[@id]") { |x| x.attributes["id"] = svg_munch_id(x.attributes["id"]) }
## REXML::XPath.each(d.root, "//rect") { |x| x.attributes["style"] = "fill:none;stroke:black;stroke-width:1" unless x.attributes["style"] }
# Fix for mermaid:
REXML::XPath.each(d.root, "//polygon") { |x| x.attributes["rx"] = nil; x.attributes["ry"] = nil }
d.to_s
rescue => detail
warn "*** Can't clean SVG: #{detail}"
d
end
def memoize(meth, *args)
require 'digest'
Dir.mkdir(REFCACHEDIR) unless Dir.exist?(REFCACHEDIR)
kdrfc_version = Gem.loaded_specs["kramdown-rfc2629"].version.to_s.gsub('.', '_') rescue "UNKNOWN"
fn = "#{REFCACHEDIR}/kdrfc-#{kdrfc_version}-#{meth}-#{Digest::SHA256.hexdigest(Marshal.dump(args))[0...40]}.cache"
begin
out = Marshal.load(File.binread(fn))
rescue StandardError => e
# warn e.inspect
out = method(meth).call(*args)
File.binwrite(fn, Marshal.dump(out))
end
out
end
def capture_croak(t, err)
if err != ''
err.lines do |l|
warn "*** [#{t}:] #{l.chomp}"
end
end
end
def shell_prepare(opt)
" " << opt.shellsplit.shelljoin
end
DEFAULT_AASVG="aasvg --spaces=1"
def svg_tool_process(t, svg_opt, txt_opt, result)
require 'tempfile'
file = Tempfile.new("kramdown-rfc")
file.write(result)
file.close
dont_clean = false
dont_check = false
svg_opt = shell_prepare(svg_opt) if svg_opt
txt_opt = shell_prepare(txt_opt) if txt_opt
case t
when "protocol", "protocol-goat", "protocol-aasvg"
cmdparm = result.lines.map(&:strip).select {|x| x != ''}.join(',')
result, err, _s = Open3.capture3("protocol #{Shellwords.escape(cmdparm)}#{txt_opt}", stdin_data: '')
if t == "protocol-goat"
file.unlink
file = Tempfile.new("kramdown-rfc")
file.write(result)
file.close
result1, err, _s = Open3.capture3("goat#{svg_opt} #{file.path}", stdin_data: result);
dont_clean = true
elsif t == "protocol-aasvg"
result1, err, _s = Open3.capture3("#{DEFAULT_AASVG}#{svg_opt}", stdin_data: result);
dont_clean = true
dont_check = true
else
result1 = nil
end
when "goat"
result1, err, _s = Open3.capture3("goat#{svg_opt} #{file.path}", stdin_data: result);
dont_clean = true
when "aasvg"
result1, err, _s = Open3.capture3("#{DEFAULT_AASVG}#{svg_opt}", stdin_data: result);
dont_clean = true
dont_check = true
when "ditaa" # XXX: This needs some form of option-setting
result1, err, _s = Open3.capture3("ditaa #{file.path} --svg -o -#{svg_opt}", stdin_data: result);
when "mscgen"
result1, err, _s = Open3.capture3("mscgen -T svg -i #{file.path} -o -#{svg_opt}", stdin_data: result);
when "mermaid"
result1, err, _s = Open3.capture3("mmdc -i #{file.path}#{svg_opt}", stdin_data: result); # -b transparent
outpath = file.path + ".svg"
result1 = File.read(outpath) rescue '' # don't die before providing error message
File.unlink(outpath) rescue nil # ditto
when "plantuml", "plantuml-utxt"
plantuml = "@startuml\n#{result}\n@enduml"
result1, err, _s = Open3.capture3("plantuml -pipe -tsvg#{svg_opt}", stdin_data: plantuml);
result, err1, _s = Open3.capture3("plantuml -pipe -tutxt#{txt_opt}", stdin_data: plantuml) if t == "plantuml-utxt"
err << err1.to_s
when "railroad", "railroad-utf8"
result1, err1, _s = Open3.capture3("kgt -l abnf -e svg#{svg_opt}", stdin_data: result);
result1 = svg_clean_kgt(result1); dont_clean = true
result, err, _s = Open3.capture3("kgt -l abnf -e rr#{t == "railroad" ? "text" : "utf8"}#{txt_opt}",
stdin_data: result);
err << err1.to_s
when "math", "math-asciitex"
result1, err, _s = Open3.capture3("tex2svg --font STIX --speech=false#{svg_opt} #{Shellwords.escape(' ' << result)}");
begin
raise Errno::ENOENT if t == "math-asciitex"
result, err1, s = Open3.capture3("utftex -m #{txt_opt}", stdin_data: result)
if s.exitstatus != 0
warn "** utftex: #{err1.inspect}"
raise Errno::ENOENT
end
rescue Errno::ENOENT
warn "** utftex not working, falling back to asciitex" unless t == "math-asciitex"
result, err1, _s = Open3.capture3("asciitex -f #{file.path}#{txt_opt}")
end
err << err1
end
capture_croak(t, err)
# warn ["text:", result.inspect]
# warn ["svg:", result1.inspect]
file.unlink
if result1
result1 = svg_clean(result1) unless dont_clean
unless dont_check
file = Tempfile.new("kramdown-rfc")
file.write(result1)
file.close
result1, err, _s = Open3.capture3("svgcheck -qa #{file.path}");
file.unlink
# warn ["svgcheck:", result1.inspect]
capture_croak("svgcheck", err)
end
if result1 == ''
warn "*** could not create svg for #{result.inspect[0...20]}..."
exit 65 # EX_DATAERR
end
end
[result, result1] # text, svg
end
ARTWORK_TYPES = %w(ascii-art binary-art call-flow hex-dump svg)
def convert_codeblock(el, indent, opts)
# el.attr['anchor'] ||= saner_generate_id(el.value) -- no longer in 1.0.6
result = el.value
gi = el.attr.delete('gi')
blockclass = el.attr.delete('class')
if blockclass == 'language-tbreak'
result = result.lines.map {|line| [line.chomp, 0]}
spaceind = 0
result.each_with_index {|pair, index|
if pair[0] == ''
result[spaceind][1] += 1
pair[0] = nil unless index == spaceind
else
spaceind = index
end
}
# $stderr.puts(result.inspect)
result = result.map {|line, space|
"" if line
}.compact.join("\n")
"#{' '*indent}#{result}\n"
else
artwork_attr = {}
t = nil
if blockclass
classes = blockclass.split(' ')
classes.each do |cl|
if md = cl.match(/\Alanguage-(.*)/)
t = artwork_attr["type"] = md[1] # XXX overwrite
else
$stderr.puts "*** Unimplemented codeblock class: #{cl}"
end
end
end
# compensate for XML2RFC idiosyncrasy by insisting on a blank line
unless el.attr.delete('tight')
result[0,0] = "\n" unless result[0,1] == "\n"
end
el.attr.each do |k, v|
if md = k.match(/\A(?:artwork|sourcecode)-(.*)/)
el.attr.delete(k)
artwork_attr[md[1]] = v
end
end
case t
when "aasvg", "ditaa", "goat",
"math", "math-asciitex", "mermaid", "mscgen",
"plantuml", "plantuml-utxt",
"protocol", "protocol-aasvg", "protocol-goat",
"railroad", "railroad-utf8"
if gi
warn "*** Can't set GI #{gi} for composite SVG artset"
end
result, result1 = memoize(:svg_tool_process, t,
artwork_attr.delete("svg-options"),
artwork_attr.delete("txt-options"),
result)
retart = mk_artwork(artwork_attr, "ascii-art",
"")
if result1 # nest TXT in artset with SVG
retsvg = mk_artwork(artwork_attr, "svg",
result1.sub(/.*?