apex-ruby 1.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (501) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +119 -0
  3. data/apex-ruby.gemspec +31 -0
  4. data/ext/apex_ext/apex_ext.c +215 -0
  5. data/ext/apex_ext/apex_src/BENCHMARK.md +32 -0
  6. data/ext/apex_ext/apex_src/BENCHMARK_COMPARISON.md +67 -0
  7. data/ext/apex_ext/apex_src/CHANGELOG.md +2454 -0
  8. data/ext/apex_ext/apex_src/CMakeLists.txt +454 -0
  9. data/ext/apex_ext/apex_src/Dockerfile.linux-build +15 -0
  10. data/ext/apex_ext/apex_src/Formula/apex.rb +38 -0
  11. data/ext/apex_ext/apex_src/Info.plist.in +27 -0
  12. data/ext/apex_ext/apex_src/LICENSE +21 -0
  13. data/ext/apex_ext/apex_src/Package.swift +160 -0
  14. data/ext/apex_ext/apex_src/PackageSupport/README.md +17 -0
  15. data/ext/apex_ext/apex_src/PackageSupport/cmark-gfm/cmark-gfm_export.h +20 -0
  16. data/ext/apex_ext/apex_src/PackageSupport/cmark-gfm/cmark-gfm_version.h +14 -0
  17. data/ext/apex_ext/apex_src/PackageSupport/cmark-gfm/cmark_gfm_spm_stub.c +4 -0
  18. data/ext/apex_ext/apex_src/PackageSupport/cmark-gfm/config.h +41 -0
  19. data/ext/apex_ext/apex_src/README.md +452 -0
  20. data/ext/apex_ext/apex_src/VERSION +1 -0
  21. data/ext/apex_ext/apex_src/apex-header-2-rb@2x.webp +0 -0
  22. data/ext/apex_ext/apex_src/apex-plugins.json.example +20 -0
  23. data/ext/apex_ext/apex_src/apex.pc.in +11 -0
  24. data/ext/apex_ext/apex_src/cli/main.c +2720 -0
  25. data/ext/apex_ext/apex_src/debug_test.sh +22 -0
  26. data/ext/apex_ext/apex_src/docs/API_REFERENCE.md +451 -0
  27. data/ext/apex_ext/apex_src/docs/ARCHITECTURE.md +166 -0
  28. data/ext/apex_ext/apex_src/docs/CMARK_INTEGRATION.md +220 -0
  29. data/ext/apex_ext/apex_src/docs/CRITICMARKUP.md +501 -0
  30. data/ext/apex_ext/apex_src/docs/DEBUGGING.md +73 -0
  31. data/ext/apex_ext/apex_src/docs/FINAL_STATUS.md +391 -0
  32. data/ext/apex_ext/apex_src/docs/FINAL_STATUS_UPDATE.md +237 -0
  33. data/ext/apex_ext/apex_src/docs/FUTURE_FEATURES.md +456 -0
  34. data/ext/apex_ext/apex_src/docs/IAL_FEATURES.md +210 -0
  35. data/ext/apex_ext/apex_src/docs/IAL_STATUS.md +344 -0
  36. data/ext/apex_ext/apex_src/docs/INTEGRATION_EXAMPLE.m +144 -0
  37. data/ext/apex_ext/apex_src/docs/LIMITATIONS_RESOLVED.md +278 -0
  38. data/ext/apex_ext/apex_src/docs/OUTPUT_MODES.md +321 -0
  39. data/ext/apex_ext/apex_src/docs/PROGRESS.md +167 -0
  40. data/ext/apex_ext/apex_src/docs/STANDALONE_FEATURE.md +174 -0
  41. data/ext/apex_ext/apex_src/docs/TABLE_SPANS_STATUS.md +243 -0
  42. data/ext/apex_ext/apex_src/docs/TEST_COVERAGE.md +316 -0
  43. data/ext/apex_ext/apex_src/docs/USER_GUIDE.md +803 -0
  44. data/ext/apex_ext/apex_src/docs/WIKI_LINKS_ISSUE.md +91 -0
  45. data/ext/apex_ext/apex_src/documentation/README.md +160 -0
  46. data/ext/apex_ext/apex_src/documentation/docsets/Apex Command Line Options.cheatsheet.txt +365 -0
  47. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Info.plist +24 -0
  48. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/C-API.html +1737 -0
  49. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Citations.html +1420 -0
  50. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Command-Line-Options.html +3574 -0
  51. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Configuration.html +1603 -0
  52. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Credits.html +910 -0
  53. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Examples.html +1168 -0
  54. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Getting-Started.html +1003 -0
  55. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Header-IDs.html +1308 -0
  56. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Home.html +1078 -0
  57. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Inline-Attribute-Lists.html +1622 -0
  58. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Installation.html +1168 -0
  59. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Limitations-and-Roadmap.html +1698 -0
  60. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Metadata-Transforms.html +1531 -0
  61. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Modes.html +1980 -0
  62. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Multi-File-Documents.html +1368 -0
  63. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Pandoc-Integration.html +1151 -0
  64. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Plugins.html +2861 -0
  65. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Syntax.html +3981 -0
  66. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Troubleshooting.html +1454 -0
  67. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Usage.html +1200 -0
  68. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Xcode-Integration.html +2066 -0
  69. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/docSet.dsidx +0 -0
  70. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/optimizedIndex.dsidx +0 -0
  71. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/tempOptimizedIndex.dsidx +0 -0
  72. data/ext/apex_ext/apex_src/documentation/docsets/ApexCLI.docset/Contents/Info.plist +22 -0
  73. data/ext/apex_ext/apex_src/documentation/docsets/ApexCLI.docset/Contents/Resources/Documents/cheatset_resources/Open_Sans.woff +0 -0
  74. data/ext/apex_ext/apex_src/documentation/docsets/ApexCLI.docset/Contents/Resources/Documents/cheatset_resources/Open_Sans_Bold.woff +0 -0
  75. data/ext/apex_ext/apex_src/documentation/docsets/ApexCLI.docset/Contents/Resources/Documents/cheatset_resources/Open_Sans_Bold_Italic.woff +0 -0
  76. data/ext/apex_ext/apex_src/documentation/docsets/ApexCLI.docset/Contents/Resources/Documents/cheatset_resources/Open_Sans_Extrabold.woff +0 -0
  77. data/ext/apex_ext/apex_src/documentation/docsets/ApexCLI.docset/Contents/Resources/Documents/cheatset_resources/Open_Sans_Extrabold_Italic.woff +0 -0
  78. data/ext/apex_ext/apex_src/documentation/docsets/ApexCLI.docset/Contents/Resources/Documents/cheatset_resources/Open_Sans_Italic.woff +0 -0
  79. data/ext/apex_ext/apex_src/documentation/docsets/ApexCLI.docset/Contents/Resources/Documents/cheatset_resources/Open_Sans_Semibold.woff +0 -0
  80. data/ext/apex_ext/apex_src/documentation/docsets/ApexCLI.docset/Contents/Resources/Documents/cheatset_resources/Open_Sans_Semibold_Italic.woff +0 -0
  81. data/ext/apex_ext/apex_src/documentation/docsets/ApexCLI.docset/Contents/Resources/Documents/index.html +914 -0
  82. data/ext/apex_ext/apex_src/documentation/docsets/ApexCLI.docset/Contents/Resources/Documents/style.css +399 -0
  83. data/ext/apex_ext/apex_src/documentation/docsets/ApexCLI.docset/Contents/Resources/docSet.dsidx +0 -0
  84. data/ext/apex_ext/apex_src/documentation/docsets/ApexCLI.docset/Contents/Resources/optimizedIndex.dsidx +0 -0
  85. data/ext/apex_ext/apex_src/documentation/generate_app_docs.rb +772 -0
  86. data/ext/apex_ext/apex_src/documentation/generate_app_docs_ai.rb +678 -0
  87. data/ext/apex_ext/apex_src/documentation/generate_docset.rb +873 -0
  88. data/ext/apex_ext/apex_src/documentation/generate_single_html.rb +733 -0
  89. data/ext/apex_ext/apex_src/documentation/html/apex-docs.html +17073 -0
  90. data/ext/apex_ext/apex_src/documentation/shared_scripts.js +64 -0
  91. data/ext/apex_ext/apex_src/documentation/shared_styles.css +646 -0
  92. data/ext/apex_ext/apex_src/documentation/transform_for_app.example.md +260 -0
  93. data/ext/apex_ext/apex_src/examples/bracketed_spans_demo.md +119 -0
  94. data/ext/apex_ext/apex_src/examples/emoji_span_plugin.yml +11 -0
  95. data/ext/apex_ext/apex_src/examples/example.html +53 -0
  96. data/ext/apex_ext/apex_src/examples/example.md +85 -0
  97. data/ext/apex_ext/apex_src/examples/fenced_divs_demo.md +158 -0
  98. data/ext/apex_ext/apex_src/examples/kbd.md +8 -0
  99. data/ext/apex_ext/apex_src/examples/kbd_plugin.rb +250 -0
  100. data/ext/apex_ext/apex_src/examples/kbd_plugin.yml +9 -0
  101. data/ext/apex_ext/apex_src/icon/apexicon-outline-black.png +0 -0
  102. data/ext/apex_ext/apex_src/icon/apexicon-outline-black@2x.png +0 -0
  103. data/ext/apex_ext/apex_src/icon/apexicon-outline-mark.png +0 -0
  104. data/ext/apex_ext/apex_src/icon/apexicon-outline-mark@2x.png +0 -0
  105. data/ext/apex_ext/apex_src/icon/apexicon-outline-white.png +0 -0
  106. data/ext/apex_ext/apex_src/icon/apexicon-outline-white@2x.png +0 -0
  107. data/ext/apex_ext/apex_src/icon/apexicon.png +0 -0
  108. data/ext/apex_ext/apex_src/icon/apexicon@2x.png +0 -0
  109. data/ext/apex_ext/apex_src/include/apex/apex.h +247 -0
  110. data/ext/apex_ext/apex_src/include/apex/buffer.h +93 -0
  111. data/ext/apex_ext/apex_src/include/apex/module.modulemap +16 -0
  112. data/ext/apex_ext/apex_src/include/apex/parser.h +150 -0
  113. data/ext/apex_ext/apex_src/include/apex/renderer.h +39 -0
  114. data/ext/apex_ext/apex_src/man/apex-config.5 +374 -0
  115. data/ext/apex_ext/apex_src/man/apex-config.5.md +260 -0
  116. data/ext/apex_ext/apex_src/man/apex-plugins.7 +456 -0
  117. data/ext/apex_ext/apex_src/man/apex-plugins.7.md +365 -0
  118. data/ext/apex_ext/apex_src/man/apex.1 +828 -0
  119. data/ext/apex_ext/apex_src/man/apex.1.md +643 -0
  120. data/ext/apex_ext/apex_src/man/apex.1.new +338 -0
  121. data/ext/apex_ext/apex_src/objc/Apex.swift +237 -0
  122. data/ext/apex_ext/apex_src/objc/NSString+Apex.h +117 -0
  123. data/ext/apex_ext/apex_src/objc/NSString+Apex.m +332 -0
  124. data/ext/apex_ext/apex_src/src/_README.md +358 -0
  125. data/ext/apex_ext/apex_src/src/apex.c +6326 -0
  126. data/ext/apex_ext/apex_src/src/buffer.c +93 -0
  127. data/ext/apex_ext/apex_src/src/extensions/abbreviations.c +362 -0
  128. data/ext/apex_ext/apex_src/src/extensions/abbreviations.h +45 -0
  129. data/ext/apex_ext/apex_src/src/extensions/advanced_footnotes.c +184 -0
  130. data/ext/apex_ext/apex_src/src/extensions/advanced_footnotes.h +50 -0
  131. data/ext/apex_ext/apex_src/src/extensions/advanced_tables.c +1897 -0
  132. data/ext/apex_ext/apex_src/src/extensions/advanced_tables.h +42 -0
  133. data/ext/apex_ext/apex_src/src/extensions/callouts.c +215 -0
  134. data/ext/apex_ext/apex_src/src/extensions/callouts.h +53 -0
  135. data/ext/apex_ext/apex_src/src/extensions/citations.c +2042 -0
  136. data/ext/apex_ext/apex_src/src/extensions/citations.h +163 -0
  137. data/ext/apex_ext/apex_src/src/extensions/critic.c +329 -0
  138. data/ext/apex_ext/apex_src/src/extensions/critic.h +48 -0
  139. data/ext/apex_ext/apex_src/src/extensions/definition_list.c +1670 -0
  140. data/ext/apex_ext/apex_src/src/extensions/definition_list.h +42 -0
  141. data/ext/apex_ext/apex_src/src/extensions/emoji.c +710 -0
  142. data/ext/apex_ext/apex_src/src/extensions/emoji.h +38 -0
  143. data/ext/apex_ext/apex_src/src/extensions/emoji_data.h +942 -0
  144. data/ext/apex_ext/apex_src/src/extensions/fenced_divs.c +925 -0
  145. data/ext/apex_ext/apex_src/src/extensions/fenced_divs.h +43 -0
  146. data/ext/apex_ext/apex_src/src/extensions/github-emoji.txt +869 -0
  147. data/ext/apex_ext/apex_src/src/extensions/grid_tables.c +1121 -0
  148. data/ext/apex_ext/apex_src/src/extensions/grid_tables.h +33 -0
  149. data/ext/apex_ext/apex_src/src/extensions/header_ids.c +626 -0
  150. data/ext/apex_ext/apex_src/src/extensions/header_ids.h +60 -0
  151. data/ext/apex_ext/apex_src/src/extensions/highlight.c +135 -0
  152. data/ext/apex_ext/apex_src/src/extensions/highlight.h +16 -0
  153. data/ext/apex_ext/apex_src/src/extensions/html_markdown.c +408 -0
  154. data/ext/apex_ext/apex_src/src/extensions/html_markdown.h +42 -0
  155. data/ext/apex_ext/apex_src/src/extensions/ial.c +4084 -0
  156. data/ext/apex_ext/apex_src/src/extensions/ial.h +145 -0
  157. data/ext/apex_ext/apex_src/src/extensions/includes.c +1536 -0
  158. data/ext/apex_ext/apex_src/src/extensions/includes.h +54 -0
  159. data/ext/apex_ext/apex_src/src/extensions/index.c +967 -0
  160. data/ext/apex_ext/apex_src/src/extensions/index.h +90 -0
  161. data/ext/apex_ext/apex_src/src/extensions/inline_footnotes.c +205 -0
  162. data/ext/apex_ext/apex_src/src/extensions/inline_footnotes.h +34 -0
  163. data/ext/apex_ext/apex_src/src/extensions/inline_tables.c +332 -0
  164. data/ext/apex_ext/apex_src/src/extensions/inline_tables.h +13 -0
  165. data/ext/apex_ext/apex_src/src/extensions/insert.c +248 -0
  166. data/ext/apex_ext/apex_src/src/extensions/insert.h +18 -0
  167. data/ext/apex_ext/apex_src/src/extensions/math.c +279 -0
  168. data/ext/apex_ext/apex_src/src/extensions/math.h +32 -0
  169. data/ext/apex_ext/apex_src/src/extensions/metadata.c +3046 -0
  170. data/ext/apex_ext/apex_src/src/extensions/metadata.h +125 -0
  171. data/ext/apex_ext/apex_src/src/extensions/relaxed_tables.c +1297 -0
  172. data/ext/apex_ext/apex_src/src/extensions/relaxed_tables.h +39 -0
  173. data/ext/apex_ext/apex_src/src/extensions/special_markers.c +194 -0
  174. data/ext/apex_ext/apex_src/src/extensions/special_markers.h +29 -0
  175. data/ext/apex_ext/apex_src/src/extensions/sup_sub.c +405 -0
  176. data/ext/apex_ext/apex_src/src/extensions/sup_sub.h +16 -0
  177. data/ext/apex_ext/apex_src/src/extensions/syntax_highlight.c +468 -0
  178. data/ext/apex_ext/apex_src/src/extensions/syntax_highlight.h +44 -0
  179. data/ext/apex_ext/apex_src/src/extensions/table_html_postprocess.c +2679 -0
  180. data/ext/apex_ext/apex_src/src/extensions/table_html_postprocess.h +23 -0
  181. data/ext/apex_ext/apex_src/src/extensions/toc.c +255 -0
  182. data/ext/apex_ext/apex_src/src/extensions/toc.h +34 -0
  183. data/ext/apex_ext/apex_src/src/extensions/wiki_links.c +624 -0
  184. data/ext/apex_ext/apex_src/src/extensions/wiki_links.h +58 -0
  185. data/ext/apex_ext/apex_src/src/html_renderer.c +2762 -0
  186. data/ext/apex_ext/apex_src/src/html_renderer.h +126 -0
  187. data/ext/apex_ext/apex_src/src/parser.c +227 -0
  188. data/ext/apex_ext/apex_src/src/plugins.c +895 -0
  189. data/ext/apex_ext/apex_src/src/plugins.h +39 -0
  190. data/ext/apex_ext/apex_src/src/plugins_env.c +187 -0
  191. data/ext/apex_ext/apex_src/src/plugins_remote.c +263 -0
  192. data/ext/apex_ext/apex_src/src/pretty_html.c +358 -0
  193. data/ext/apex_ext/apex_src/src/renderer.c +241 -0
  194. data/ext/apex_ext/apex_src/src/utf8.c +56 -0
  195. data/ext/apex_ext/apex_src/test-linux-build.sh +20 -0
  196. data/ext/apex_ext/apex_src/test.html +103 -0
  197. data/ext/apex_ext/apex_src/test_coverage.sh +121 -0
  198. data/ext/apex_ext/apex_src/test_ial_fenced.md +6 -0
  199. data/ext/apex_ext/apex_src/test_math_norm.py +79 -0
  200. data/ext/apex_ext/apex_src/test_pandoc_output.html +48 -0
  201. data/ext/apex_ext/apex_src/test_spm.sh +107 -0
  202. data/ext/apex_ext/apex_src/tests/ApexSPMTest/main.swift +50 -0
  203. data/ext/apex_ext/apex_src/tests/BENCHMARK_RESULTS.md +229 -0
  204. data/ext/apex_ext/apex_src/tests/CMakeLists.txt +24 -0
  205. data/ext/apex_ext/apex_src/tests/README.md +146 -0
  206. data/ext/apex_ext/apex_src/tests/benchmark.sh +113 -0
  207. data/ext/apex_ext/apex_src/tests/benchmark_comparison.sh +166 -0
  208. data/ext/apex_ext/apex_src/tests/compare_header_ids.sh +31 -0
  209. data/ext/apex_ext/apex_src/tests/fixtures/basic/headers.md +25 -0
  210. data/ext/apex_ext/apex_src/tests/fixtures/basic/list-interruption.md +24 -0
  211. data/ext/apex_ext/apex_src/tests/fixtures/basic/misc_markup.md +33 -0
  212. data/ext/apex_ext/apex_src/tests/fixtures/basic/test_basic.md +26 -0
  213. data/ext/apex_ext/apex_src/tests/fixtures/code/code-blocks.md +260 -0
  214. data/ext/apex_ext/apex_src/tests/fixtures/combine_summary/SUMMARY.md +6 -0
  215. data/ext/apex_ext/apex_src/tests/fixtures/combine_summary/chapter1.md +7 -0
  216. data/ext/apex_ext/apex_src/tests/fixtures/combine_summary/index.txt +9 -0
  217. data/ext/apex_ext/apex_src/tests/fixtures/combine_summary/intro.md +5 -0
  218. data/ext/apex_ext/apex_src/tests/fixtures/combine_summary/section1_1.md +5 -0
  219. data/ext/apex_ext/apex_src/tests/fixtures/comprehensive_test.md +620 -0
  220. data/ext/apex_ext/apex_src/tests/fixtures/debug_ref_image_ial.md +3 -0
  221. data/ext/apex_ext/apex_src/tests/fixtures/demos/ial.md +11 -0
  222. data/ext/apex_ext/apex_src/tests/fixtures/demos/ial_demo.md +177 -0
  223. data/ext/apex_ext/apex_src/tests/fixtures/extensions/emoji-autocorrect.md +94 -0
  224. data/ext/apex_ext/apex_src/tests/fixtures/extensions/emoji_test.md +3 -0
  225. data/ext/apex_ext/apex_src/tests/fixtures/extensions/kbd_test.md +3 -0
  226. data/ext/apex_ext/apex_src/tests/fixtures/ial/bracketed_spans_test.md +74 -0
  227. data/ext/apex_ext/apex_src/tests/fixtures/images/image_and_encoding_test.md +27 -0
  228. data/ext/apex_ext/apex_src/tests/fixtures/images/multimarkdown_image_attributes_test.md +60 -0
  229. data/ext/apex_ext/apex_src/tests/fixtures/images/pandoc_ial_image_test.md +27 -0
  230. data/ext/apex_ext/apex_src/tests/fixtures/images/width_height_conversion_test.md +94 -0
  231. data/ext/apex_ext/apex_src/tests/fixtures/img-in-div.md +16 -0
  232. data/ext/apex_ext/apex_src/tests/fixtures/includes/code.py +4 -0
  233. data/ext/apex_ext/apex_src/tests/fixtures/includes/data.csv +5 -0
  234. data/ext/apex_ext/apex_src/tests/fixtures/includes/data.tsv +5 -0
  235. data/ext/apex_ext/apex_src/tests/fixtures/includes/image.png +2 -0
  236. data/ext/apex_ext/apex_src/tests/fixtures/includes/metadata_options.yml +11 -0
  237. data/ext/apex_ext/apex_src/tests/fixtures/includes/nested.md +8 -0
  238. data/ext/apex_ext/apex_src/tests/fixtures/includes/raw.html +4 -0
  239. data/ext/apex_ext/apex_src/tests/fixtures/includes/simple.md +7 -0
  240. data/ext/apex_ext/apex_src/tests/fixtures/includes/test_image.png +0 -0
  241. data/ext/apex_ext/apex_src/tests/fixtures/large_doc.md +1094 -0
  242. data/ext/apex_ext/apex_src/tests/fixtures/metadata_options.yml +11 -0
  243. data/ext/apex_ext/apex_src/tests/fixtures/output/gfm_header_id_test.md +96 -0
  244. data/ext/apex_ext/apex_src/tests/fixtures/output/test_citations.md +43 -0
  245. data/ext/apex_ext/apex_src/tests/fixtures/output/test_def_list_links.md +12 -0
  246. data/ext/apex_ext/apex_src/tests/fixtures/output/test_index_mmark.md +53 -0
  247. data/ext/apex_ext/apex_src/tests/fixtures/output/test_index_textindex.md +37 -0
  248. data/ext/apex_ext/apex_src/tests/fixtures/tables/advanced_tables_test.md +93 -0
  249. data/ext/apex_ext/apex_src/tests/fixtures/tables/inline_tables_test.md +38 -0
  250. data/ext/apex_ext/apex_src/tests/fixtures/tables/relaxed-table.md +12 -0
  251. data/ext/apex_ext/apex_src/tests/fixtures/tables/table_cr_line_endings.md +15 -0
  252. data/ext/apex_ext/apex_src/tests/fixtures/tables/table_no_trailing_newline.md +15 -0
  253. data/ext/apex_ext/apex_src/tests/generate_gfm_ids.sh +105 -0
  254. data/ext/apex_ext/apex_src/tests/generate_ial_demo.sh +143 -0
  255. data/ext/apex_ext/apex_src/tests/gfm_id_comparison_summary.md +96 -0
  256. data/ext/apex_ext/apex_src/tests/gh_api_test.md +6 -0
  257. data/ext/apex_ext/apex_src/tests/ial_demo.html +186 -0
  258. data/ext/apex_ext/apex_src/tests/include_code.py +19 -0
  259. data/ext/apex_ext/apex_src/tests/include_snippet.md +15 -0
  260. data/ext/apex_ext/apex_src/tests/multi_file_cli_test.sh +64 -0
  261. data/ext/apex_ext/apex_src/tests/sample_data.csv +7 -0
  262. data/ext/apex_ext/apex_src/tests/table_escaped_ltlt.md +4 -0
  263. data/ext/apex_ext/apex_src/tests/test_basic.c +74 -0
  264. data/ext/apex_ext/apex_src/tests/test_extensions.c +2116 -0
  265. data/ext/apex_ext/apex_src/tests/test_helpers.c +183 -0
  266. data/ext/apex_ext/apex_src/tests/test_helpers.h +91 -0
  267. data/ext/apex_ext/apex_src/tests/test_ial.c +282 -0
  268. data/ext/apex_ext/apex_src/tests/test_links.c +418 -0
  269. data/ext/apex_ext/apex_src/tests/test_marked_integration.c +265 -0
  270. data/ext/apex_ext/apex_src/tests/test_metadata.c +908 -0
  271. data/ext/apex_ext/apex_src/tests/test_output.c +1118 -0
  272. data/ext/apex_ext/apex_src/tests/test_plugins.c +219 -0
  273. data/ext/apex_ext/apex_src/tests/test_refs.bib +31 -0
  274. data/ext/apex_ext/apex_src/tests/test_runner.c +244 -0
  275. data/ext/apex_ext/apex_src/tests/test_syntax_highlight.c +198 -0
  276. data/ext/apex_ext/apex_src/tests/test_tables.c +862 -0
  277. data/ext/apex_ext/apex_src/tests/update_benchmarks.sh +9 -0
  278. data/ext/apex_ext/apex_src/tests/yaml_test.md +13 -0
  279. data/ext/apex_ext/apex_src/tests.rb +39 -0
  280. data/ext/apex_ext/apex_src/vendor/cmark-gfm/CMakeLists.txt +48 -0
  281. data/ext/apex_ext/apex_src/vendor/cmark-gfm/COPYING +170 -0
  282. data/ext/apex_ext/apex_src/vendor/cmark-gfm/CheckFileOffsetBits.c +14 -0
  283. data/ext/apex_ext/apex_src/vendor/cmark-gfm/CheckFileOffsetBits.cmake +43 -0
  284. data/ext/apex_ext/apex_src/vendor/cmark-gfm/FindAsan.cmake +74 -0
  285. data/ext/apex_ext/apex_src/vendor/cmark-gfm/Makefile.nmake +38 -0
  286. data/ext/apex_ext/apex_src/vendor/cmark-gfm/README.md +206 -0
  287. data/ext/apex_ext/apex_src/vendor/cmark-gfm/api_test/CMakeLists.txt +30 -0
  288. data/ext/apex_ext/apex_src/vendor/cmark-gfm/api_test/cplusplus.cpp +15 -0
  289. data/ext/apex_ext/apex_src/vendor/cmark-gfm/api_test/cplusplus.h +16 -0
  290. data/ext/apex_ext/apex_src/vendor/cmark-gfm/api_test/harness.c +111 -0
  291. data/ext/apex_ext/apex_src/vendor/cmark-gfm/api_test/harness.h +35 -0
  292. data/ext/apex_ext/apex_src/vendor/cmark-gfm/api_test/main.c +1169 -0
  293. data/ext/apex_ext/apex_src/vendor/cmark-gfm/appveyor.yml +21 -0
  294. data/ext/apex_ext/apex_src/vendor/cmark-gfm/bench/samples/block-bq-flat.md +16 -0
  295. data/ext/apex_ext/apex_src/vendor/cmark-gfm/bench/samples/block-bq-nested.md +13 -0
  296. data/ext/apex_ext/apex_src/vendor/cmark-gfm/bench/samples/block-code.md +11 -0
  297. data/ext/apex_ext/apex_src/vendor/cmark-gfm/bench/samples/block-fences.md +14 -0
  298. data/ext/apex_ext/apex_src/vendor/cmark-gfm/bench/samples/block-heading.md +9 -0
  299. data/ext/apex_ext/apex_src/vendor/cmark-gfm/bench/samples/block-hr.md +10 -0
  300. data/ext/apex_ext/apex_src/vendor/cmark-gfm/bench/samples/block-html.md +32 -0
  301. data/ext/apex_ext/apex_src/vendor/cmark-gfm/bench/samples/block-lheading.md +8 -0
  302. data/ext/apex_ext/apex_src/vendor/cmark-gfm/bench/samples/block-list-flat.md +67 -0
  303. data/ext/apex_ext/apex_src/vendor/cmark-gfm/bench/samples/block-list-nested.md +36 -0
  304. data/ext/apex_ext/apex_src/vendor/cmark-gfm/bench/samples/block-ref-flat.md +15 -0
  305. data/ext/apex_ext/apex_src/vendor/cmark-gfm/bench/samples/block-ref-nested.md +17 -0
  306. data/ext/apex_ext/apex_src/vendor/cmark-gfm/bench/samples/inline-autolink.md +14 -0
  307. data/ext/apex_ext/apex_src/vendor/cmark-gfm/bench/samples/inline-backticks.md +3 -0
  308. data/ext/apex_ext/apex_src/vendor/cmark-gfm/bench/samples/inline-em-flat.md +5 -0
  309. data/ext/apex_ext/apex_src/vendor/cmark-gfm/bench/samples/inline-em-nested.md +5 -0
  310. data/ext/apex_ext/apex_src/vendor/cmark-gfm/bench/samples/inline-em-worst.md +5 -0
  311. data/ext/apex_ext/apex_src/vendor/cmark-gfm/bench/samples/inline-entity.md +11 -0
  312. data/ext/apex_ext/apex_src/vendor/cmark-gfm/bench/samples/inline-escape.md +15 -0
  313. data/ext/apex_ext/apex_src/vendor/cmark-gfm/bench/samples/inline-html.md +44 -0
  314. data/ext/apex_ext/apex_src/vendor/cmark-gfm/bench/samples/inline-links-flat.md +23 -0
  315. data/ext/apex_ext/apex_src/vendor/cmark-gfm/bench/samples/inline-links-nested.md +13 -0
  316. data/ext/apex_ext/apex_src/vendor/cmark-gfm/bench/samples/inline-newlines.md +24 -0
  317. data/ext/apex_ext/apex_src/vendor/cmark-gfm/bench/samples/lorem1.md +13 -0
  318. data/ext/apex_ext/apex_src/vendor/cmark-gfm/bench/samples/rawtabs.md +18 -0
  319. data/ext/apex_ext/apex_src/vendor/cmark-gfm/bench/statistics.py +595 -0
  320. data/ext/apex_ext/apex_src/vendor/cmark-gfm/bench/stats.py +19 -0
  321. data/ext/apex_ext/apex_src/vendor/cmark-gfm/benchmarks.md +33 -0
  322. data/ext/apex_ext/apex_src/vendor/cmark-gfm/changelog.txt +1245 -0
  323. data/ext/apex_ext/apex_src/vendor/cmark-gfm/data/CaseFolding.txt +1495 -0
  324. data/ext/apex_ext/apex_src/vendor/cmark-gfm/extensions/CMakeLists.txt +119 -0
  325. data/ext/apex_ext/apex_src/vendor/cmark-gfm/extensions/autolink.c +508 -0
  326. data/ext/apex_ext/apex_src/vendor/cmark-gfm/extensions/autolink.h +8 -0
  327. data/ext/apex_ext/apex_src/vendor/cmark-gfm/extensions/cmark-gfm-core-extensions.h +54 -0
  328. data/ext/apex_ext/apex_src/vendor/cmark-gfm/extensions/core-extensions.c +27 -0
  329. data/ext/apex_ext/apex_src/vendor/cmark-gfm/extensions/ext_scanners.c +879 -0
  330. data/ext/apex_ext/apex_src/vendor/cmark-gfm/extensions/ext_scanners.h +24 -0
  331. data/ext/apex_ext/apex_src/vendor/cmark-gfm/extensions/ext_scanners.re +92 -0
  332. data/ext/apex_ext/apex_src/vendor/cmark-gfm/extensions/strikethrough.c +167 -0
  333. data/ext/apex_ext/apex_src/vendor/cmark-gfm/extensions/strikethrough.h +9 -0
  334. data/ext/apex_ext/apex_src/vendor/cmark-gfm/extensions/table.c +917 -0
  335. data/ext/apex_ext/apex_src/vendor/cmark-gfm/extensions/table.h +12 -0
  336. data/ext/apex_ext/apex_src/vendor/cmark-gfm/extensions/tagfilter.c +60 -0
  337. data/ext/apex_ext/apex_src/vendor/cmark-gfm/extensions/tagfilter.h +8 -0
  338. data/ext/apex_ext/apex_src/vendor/cmark-gfm/extensions/tasklist.c +156 -0
  339. data/ext/apex_ext/apex_src/vendor/cmark-gfm/extensions/tasklist.h +8 -0
  340. data/ext/apex_ext/apex_src/vendor/cmark-gfm/fuzz/CMakeLists.txt +22 -0
  341. data/ext/apex_ext/apex_src/vendor/cmark-gfm/fuzz/README.md +12 -0
  342. data/ext/apex_ext/apex_src/vendor/cmark-gfm/fuzz/fuzz_quadratic.c +91 -0
  343. data/ext/apex_ext/apex_src/vendor/cmark-gfm/fuzz/fuzz_quadratic_brackets.c +110 -0
  344. data/ext/apex_ext/apex_src/vendor/cmark-gfm/fuzz/fuzzloop.sh +28 -0
  345. data/ext/apex_ext/apex_src/vendor/cmark-gfm/man/CMakeLists.txt +10 -0
  346. data/ext/apex_ext/apex_src/vendor/cmark-gfm/man/make_man_page.py +133 -0
  347. data/ext/apex_ext/apex_src/vendor/cmark-gfm/man/man1/cmark-gfm.1 +78 -0
  348. data/ext/apex_ext/apex_src/vendor/cmark-gfm/man/man3/cmark-gfm.3 +1041 -0
  349. data/ext/apex_ext/apex_src/vendor/cmark-gfm/nmake.bat +1 -0
  350. data/ext/apex_ext/apex_src/vendor/cmark-gfm/src/CMakeLists.txt +230 -0
  351. data/ext/apex_ext/apex_src/vendor/cmark-gfm/src/arena.c +104 -0
  352. data/ext/apex_ext/apex_src/vendor/cmark-gfm/src/blocks.c +1622 -0
  353. data/ext/apex_ext/apex_src/vendor/cmark-gfm/src/buffer.c +278 -0
  354. data/ext/apex_ext/apex_src/vendor/cmark-gfm/src/buffer.h +116 -0
  355. data/ext/apex_ext/apex_src/vendor/cmark-gfm/src/case_fold_switch.inc +4327 -0
  356. data/ext/apex_ext/apex_src/vendor/cmark-gfm/src/chunk.h +135 -0
  357. data/ext/apex_ext/apex_src/vendor/cmark-gfm/src/cmark-gfm-extension_api.h +737 -0
  358. data/ext/apex_ext/apex_src/vendor/cmark-gfm/src/cmark-gfm.h +833 -0
  359. data/ext/apex_ext/apex_src/vendor/cmark-gfm/src/cmark-gfm_version.h.in +7 -0
  360. data/ext/apex_ext/apex_src/vendor/cmark-gfm/src/cmark.c +55 -0
  361. data/ext/apex_ext/apex_src/vendor/cmark-gfm/src/cmark_ctype.c +44 -0
  362. data/ext/apex_ext/apex_src/vendor/cmark-gfm/src/cmark_ctype.h +33 -0
  363. data/ext/apex_ext/apex_src/vendor/cmark-gfm/src/commonmark.c +514 -0
  364. data/ext/apex_ext/apex_src/vendor/cmark-gfm/src/config.h.in +76 -0
  365. data/ext/apex_ext/apex_src/vendor/cmark-gfm/src/entities.inc +2138 -0
  366. data/ext/apex_ext/apex_src/vendor/cmark-gfm/src/footnotes.c +63 -0
  367. data/ext/apex_ext/apex_src/vendor/cmark-gfm/src/footnotes.h +27 -0
  368. data/ext/apex_ext/apex_src/vendor/cmark-gfm/src/houdini.h +57 -0
  369. data/ext/apex_ext/apex_src/vendor/cmark-gfm/src/houdini_href_e.c +100 -0
  370. data/ext/apex_ext/apex_src/vendor/cmark-gfm/src/houdini_html_e.c +66 -0
  371. data/ext/apex_ext/apex_src/vendor/cmark-gfm/src/houdini_html_u.c +149 -0
  372. data/ext/apex_ext/apex_src/vendor/cmark-gfm/src/html.c +502 -0
  373. data/ext/apex_ext/apex_src/vendor/cmark-gfm/src/html.h +27 -0
  374. data/ext/apex_ext/apex_src/vendor/cmark-gfm/src/inlines.c +1788 -0
  375. data/ext/apex_ext/apex_src/vendor/cmark-gfm/src/inlines.h +29 -0
  376. data/ext/apex_ext/apex_src/vendor/cmark-gfm/src/iterator.c +159 -0
  377. data/ext/apex_ext/apex_src/vendor/cmark-gfm/src/iterator.h +26 -0
  378. data/ext/apex_ext/apex_src/vendor/cmark-gfm/src/latex.c +468 -0
  379. data/ext/apex_ext/apex_src/vendor/cmark-gfm/src/libcmark-gfm.pc.in +10 -0
  380. data/ext/apex_ext/apex_src/vendor/cmark-gfm/src/linked_list.c +37 -0
  381. data/ext/apex_ext/apex_src/vendor/cmark-gfm/src/main.c +328 -0
  382. data/ext/apex_ext/apex_src/vendor/cmark-gfm/src/man.c +274 -0
  383. data/ext/apex_ext/apex_src/vendor/cmark-gfm/src/map.c +129 -0
  384. data/ext/apex_ext/apex_src/vendor/cmark-gfm/src/map.h +44 -0
  385. data/ext/apex_ext/apex_src/vendor/cmark-gfm/src/node.c +1045 -0
  386. data/ext/apex_ext/apex_src/vendor/cmark-gfm/src/node.h +167 -0
  387. data/ext/apex_ext/apex_src/vendor/cmark-gfm/src/parser.h +59 -0
  388. data/ext/apex_ext/apex_src/vendor/cmark-gfm/src/plaintext.c +218 -0
  389. data/ext/apex_ext/apex_src/vendor/cmark-gfm/src/plugin.c +36 -0
  390. data/ext/apex_ext/apex_src/vendor/cmark-gfm/src/plugin.h +34 -0
  391. data/ext/apex_ext/apex_src/vendor/cmark-gfm/src/references.c +43 -0
  392. data/ext/apex_ext/apex_src/vendor/cmark-gfm/src/references.h +26 -0
  393. data/ext/apex_ext/apex_src/vendor/cmark-gfm/src/registry.c +63 -0
  394. data/ext/apex_ext/apex_src/vendor/cmark-gfm/src/registry.h +24 -0
  395. data/ext/apex_ext/apex_src/vendor/cmark-gfm/src/render.c +213 -0
  396. data/ext/apex_ext/apex_src/vendor/cmark-gfm/src/render.h +62 -0
  397. data/ext/apex_ext/apex_src/vendor/cmark-gfm/src/scanners.c +14056 -0
  398. data/ext/apex_ext/apex_src/vendor/cmark-gfm/src/scanners.h +70 -0
  399. data/ext/apex_ext/apex_src/vendor/cmark-gfm/src/scanners.re +365 -0
  400. data/ext/apex_ext/apex_src/vendor/cmark-gfm/src/syntax_extension.c +149 -0
  401. data/ext/apex_ext/apex_src/vendor/cmark-gfm/src/syntax_extension.h +34 -0
  402. data/ext/apex_ext/apex_src/vendor/cmark-gfm/src/utf8.c +317 -0
  403. data/ext/apex_ext/apex_src/vendor/cmark-gfm/src/utf8.h +35 -0
  404. data/ext/apex_ext/apex_src/vendor/cmark-gfm/src/xml.c +182 -0
  405. data/ext/apex_ext/apex_src/vendor/cmark-gfm/suppressions +10 -0
  406. data/ext/apex_ext/apex_src/vendor/cmark-gfm/test/CMakeLists.txt +114 -0
  407. data/ext/apex_ext/apex_src/vendor/cmark-gfm/test/afl_test_cases/test.md +49 -0
  408. data/ext/apex_ext/apex_src/vendor/cmark-gfm/test/cmark-fuzz.c +58 -0
  409. data/ext/apex_ext/apex_src/vendor/cmark-gfm/test/cmark.py +105 -0
  410. data/ext/apex_ext/apex_src/vendor/cmark-gfm/test/entity_tests.py +67 -0
  411. data/ext/apex_ext/apex_src/vendor/cmark-gfm/test/extensions-full-info-string.txt +0 -0
  412. data/ext/apex_ext/apex_src/vendor/cmark-gfm/test/extensions-table-prefer-style-attributes.txt +38 -0
  413. data/ext/apex_ext/apex_src/vendor/cmark-gfm/test/extensions.txt +920 -0
  414. data/ext/apex_ext/apex_src/vendor/cmark-gfm/test/fuzzing_dictionary +67 -0
  415. data/ext/apex_ext/apex_src/vendor/cmark-gfm/test/normalize.py +194 -0
  416. data/ext/apex_ext/apex_src/vendor/cmark-gfm/test/pathological_tests.py +160 -0
  417. data/ext/apex_ext/apex_src/vendor/cmark-gfm/test/regression.txt +375 -0
  418. data/ext/apex_ext/apex_src/vendor/cmark-gfm/test/roundtrip_tests.py +50 -0
  419. data/ext/apex_ext/apex_src/vendor/cmark-gfm/test/run-cmark-fuzz +4 -0
  420. data/ext/apex_ext/apex_src/vendor/cmark-gfm/test/smart_punct.txt +177 -0
  421. data/ext/apex_ext/apex_src/vendor/cmark-gfm/test/spec.txt +10212 -0
  422. data/ext/apex_ext/apex_src/vendor/cmark-gfm/test/spec_tests.py +152 -0
  423. data/ext/apex_ext/apex_src/vendor/cmark-gfm/toolchain-mingw32.cmake +17 -0
  424. data/ext/apex_ext/apex_src/vendor/cmark-gfm/tools/Dockerfile +41 -0
  425. data/ext/apex_ext/apex_src/vendor/cmark-gfm/tools/appveyor-build.bat +13 -0
  426. data/ext/apex_ext/apex_src/vendor/cmark-gfm/tools/make_entities_inc.py +32 -0
  427. data/ext/apex_ext/apex_src/vendor/cmark-gfm/tools/mkcasefold.pl +22 -0
  428. data/ext/apex_ext/apex_src/vendor/cmark-gfm/tools/xml2md.xsl +319 -0
  429. data/ext/apex_ext/apex_src/vendor/cmark-gfm/tools/xml2md_gfm.xsl +80 -0
  430. data/ext/apex_ext/apex_src/vendor/cmark-gfm/why-cmark-and-not-x.md +104 -0
  431. data/ext/apex_ext/apex_src/vendor/cmark-gfm/wrappers/wrapper.js +6 -0
  432. data/ext/apex_ext/apex_src/vendor/cmark-gfm/wrappers/wrapper.py +37 -0
  433. data/ext/apex_ext/apex_src/vendor/cmark-gfm/wrappers/wrapper.rb +15 -0
  434. data/ext/apex_ext/apex_src/vendor/cmark-gfm/wrappers/wrapper.rkt +208 -0
  435. data/ext/apex_ext/apex_src/vendor/cmark-gfm/wrappers/wrapper_ext.py +109 -0
  436. data/ext/apex_ext/apex_src/vendor/libyaml/CMakeLists.txt +160 -0
  437. data/ext/apex_ext/apex_src/vendor/libyaml/Changes +372 -0
  438. data/ext/apex_ext/apex_src/vendor/libyaml/License +20 -0
  439. data/ext/apex_ext/apex_src/vendor/libyaml/Makefile.am +51 -0
  440. data/ext/apex_ext/apex_src/vendor/libyaml/ReadMe.md +46 -0
  441. data/ext/apex_ext/apex_src/vendor/libyaml/announcement.msg +89 -0
  442. data/ext/apex_ext/apex_src/vendor/libyaml/bootstrap +3 -0
  443. data/ext/apex_ext/apex_src/vendor/libyaml/cmake/config.h.in +4 -0
  444. data/ext/apex_ext/apex_src/vendor/libyaml/configure.ac +73 -0
  445. data/ext/apex_ext/apex_src/vendor/libyaml/doc/doxygen.cfg +222 -0
  446. data/ext/apex_ext/apex_src/vendor/libyaml/docker/README.mkd +17 -0
  447. data/ext/apex_ext/apex_src/vendor/libyaml/docker/alpine-3.7 +26 -0
  448. data/ext/apex_ext/apex_src/vendor/libyaml/docker/fedora-25 +26 -0
  449. data/ext/apex_ext/apex_src/vendor/libyaml/docker/ubuntu-14.04 +29 -0
  450. data/ext/apex_ext/apex_src/vendor/libyaml/docker/ubuntu-16.04 +24 -0
  451. data/ext/apex_ext/apex_src/vendor/libyaml/examples/anchors.yaml +10 -0
  452. data/ext/apex_ext/apex_src/vendor/libyaml/examples/array.yaml +2 -0
  453. data/ext/apex_ext/apex_src/vendor/libyaml/examples/global-tag.yaml +14 -0
  454. data/ext/apex_ext/apex_src/vendor/libyaml/examples/json.yaml +1 -0
  455. data/ext/apex_ext/apex_src/vendor/libyaml/examples/mapping.yaml +2 -0
  456. data/ext/apex_ext/apex_src/vendor/libyaml/examples/numbers.yaml +1 -0
  457. data/ext/apex_ext/apex_src/vendor/libyaml/examples/strings.yaml +7 -0
  458. data/ext/apex_ext/apex_src/vendor/libyaml/examples/tags.yaml +7 -0
  459. data/ext/apex_ext/apex_src/vendor/libyaml/examples/yaml-version.yaml +3 -0
  460. data/ext/apex_ext/apex_src/vendor/libyaml/include/Makefile.am +17 -0
  461. data/ext/apex_ext/apex_src/vendor/libyaml/include/yaml.h +1999 -0
  462. data/ext/apex_ext/apex_src/vendor/libyaml/pkg/ReadMe.md +77 -0
  463. data/ext/apex_ext/apex_src/vendor/libyaml/pkg/docker/Dockerfile +32 -0
  464. data/ext/apex_ext/apex_src/vendor/libyaml/pkg/docker/output/ReadMe +1 -0
  465. data/ext/apex_ext/apex_src/vendor/libyaml/pkg/docker/scripts/libyaml-dist.sh +23 -0
  466. data/ext/apex_ext/apex_src/vendor/libyaml/regression-inputs/clusterfuzz-testcase-minimized-5607885063061504.yml +1 -0
  467. data/ext/apex_ext/apex_src/vendor/libyaml/src/Makefile.am +4 -0
  468. data/ext/apex_ext/apex_src/vendor/libyaml/src/api.c +1393 -0
  469. data/ext/apex_ext/apex_src/vendor/libyaml/src/dumper.c +394 -0
  470. data/ext/apex_ext/apex_src/vendor/libyaml/src/emitter.c +2358 -0
  471. data/ext/apex_ext/apex_src/vendor/libyaml/src/loader.c +544 -0
  472. data/ext/apex_ext/apex_src/vendor/libyaml/src/parser.c +1416 -0
  473. data/ext/apex_ext/apex_src/vendor/libyaml/src/reader.c +469 -0
  474. data/ext/apex_ext/apex_src/vendor/libyaml/src/scanner.c +3598 -0
  475. data/ext/apex_ext/apex_src/vendor/libyaml/src/writer.c +141 -0
  476. data/ext/apex_ext/apex_src/vendor/libyaml/src/yaml_private.h +684 -0
  477. data/ext/apex_ext/apex_src/vendor/libyaml/tests/CMakeLists.txt +27 -0
  478. data/ext/apex_ext/apex_src/vendor/libyaml/tests/Makefile.am +9 -0
  479. data/ext/apex_ext/apex_src/vendor/libyaml/tests/ReadMe.md +63 -0
  480. data/ext/apex_ext/apex_src/vendor/libyaml/tests/example-deconstructor-alt.c +800 -0
  481. data/ext/apex_ext/apex_src/vendor/libyaml/tests/example-deconstructor.c +1127 -0
  482. data/ext/apex_ext/apex_src/vendor/libyaml/tests/example-reformatter-alt.c +217 -0
  483. data/ext/apex_ext/apex_src/vendor/libyaml/tests/example-reformatter.c +202 -0
  484. data/ext/apex_ext/apex_src/vendor/libyaml/tests/run-all-tests.sh +29 -0
  485. data/ext/apex_ext/apex_src/vendor/libyaml/tests/run-dumper.c +314 -0
  486. data/ext/apex_ext/apex_src/vendor/libyaml/tests/run-emitter-test-suite.c +290 -0
  487. data/ext/apex_ext/apex_src/vendor/libyaml/tests/run-emitter.c +327 -0
  488. data/ext/apex_ext/apex_src/vendor/libyaml/tests/run-loader.c +63 -0
  489. data/ext/apex_ext/apex_src/vendor/libyaml/tests/run-parser-test-suite.c +196 -0
  490. data/ext/apex_ext/apex_src/vendor/libyaml/tests/run-parser.c +88 -0
  491. data/ext/apex_ext/apex_src/vendor/libyaml/tests/run-scanner.c +63 -0
  492. data/ext/apex_ext/apex_src/vendor/libyaml/tests/test-reader.c +354 -0
  493. data/ext/apex_ext/apex_src/vendor/libyaml/tests/test-version.c +29 -0
  494. data/ext/apex_ext/apex_src/vendor/libyaml/yaml-0.1.pc.in +10 -0
  495. data/ext/apex_ext/apex_src/vendor/libyaml/yamlConfig.cmake.in +16 -0
  496. data/ext/apex_ext/extconf.rb +103 -0
  497. data/lib/apex/configurable.rb +46 -0
  498. data/lib/apex/document.rb +66 -0
  499. data/lib/apex/version.rb +15 -0
  500. data/lib/apex.rb +28 -0
  501. metadata +544 -0
@@ -0,0 +1,2042 @@
1
+ /**
2
+ * Citations Extension for Apex
3
+ * Implementation
4
+ */
5
+
6
+ #include "citations.h"
7
+ #include <string.h>
8
+ #include <stdlib.h>
9
+ #include <stdio.h>
10
+ #include <ctype.h>
11
+ #include <stdbool.h>
12
+ #include <errno.h>
13
+
14
+ /* Citation placeholder prefix - we'll use a unique marker */
15
+ #define CITATION_PLACEHOLDER_PREFIX "<!--CITE:"
16
+ #define CITATION_PLACEHOLDER_SUFFIX "-->"
17
+
18
+ /**
19
+ * Check if character is valid in citation key
20
+ * Citation keys can contain: alphanumerics, _, and internal punctuation (:.#$%&-+?<>~/)
21
+ */
22
+ static bool is_valid_citation_char(char c) {
23
+ return isalnum(c) || c == '_' || c == ':' || c == '.' || c == '#' ||
24
+ c == '$' || c == '%' || c == '&' || c == '-' || c == '+' ||
25
+ c == '?' || c == '<' || c == '>' || c == '~' || c == '/';
26
+ }
27
+
28
+ /**
29
+ * Extract citation key from text starting at pos
30
+ * Returns length of key, or 0 if not valid
31
+ */
32
+ static int extract_citation_key(const char *text, int pos, int len, char **key_out) {
33
+ if (pos >= len) return 0;
34
+
35
+ const char *start = text + pos;
36
+ const char *p = start;
37
+
38
+ /* Key must start with letter, digit, or _ */
39
+ if (!isalnum(*p) && *p != '_') {
40
+ /* Check for special keys that need curly braces */
41
+ if (*p == '{') {
42
+ p++; /* Skip { */
43
+ start = p;
44
+ /* Find closing } */
45
+ while (*p && *p != '}' && p - text < len) {
46
+ if (!is_valid_citation_char(*p) && *p != '}') {
47
+ return 0; /* Invalid character */
48
+ }
49
+ p++;
50
+ }
51
+ if (*p != '}') return 0; /* No closing brace */
52
+ } else {
53
+ return 0;
54
+ }
55
+ }
56
+
57
+ /* Collect key characters */
58
+ const char *key_start = start;
59
+ while (p - text < len && is_valid_citation_char(*p)) {
60
+ p++;
61
+ }
62
+
63
+ if (p == key_start) return 0; /* No key found */
64
+
65
+ size_t key_len = p - key_start;
66
+ *key_out = malloc(key_len + 1);
67
+ if (!*key_out) return 0;
68
+
69
+ memcpy(*key_out, key_start, key_len);
70
+ (*key_out)[key_len] = '\0';
71
+
72
+ /* If we started with {, skip past it */
73
+ if (text[pos] == '{') {
74
+ return (p - text) - pos + 1; /* +1 for closing } */
75
+ }
76
+
77
+ return p - start;
78
+ }
79
+
80
+ /**
81
+ * Check if text matches RFC/BCP/STD/I-D/W3C pattern (mmark syntax)
82
+ */
83
+ static bool is_mmark_pattern(const char *text, int pos, int len) {
84
+ if (pos + 3 >= len) return false;
85
+
86
+ const char *p = text + pos;
87
+
88
+ /* Check for RFC, BCP, STD, I-D, or W3C prefix */
89
+ if (strncmp(p, "RFC", 3) == 0 && isdigit(p[3])) return true;
90
+ if (strncmp(p, "BCP", 3) == 0 && isdigit(p[3])) return true;
91
+ if (strncmp(p, "STD", 3) == 0 && isdigit(p[3])) return true;
92
+ if (strncmp(p, "I-D.", 4) == 0) return true;
93
+ if (strncmp(p, "W3C.", 4) == 0) return true;
94
+
95
+ return false;
96
+ }
97
+
98
+ /**
99
+ * Parse Pandoc citation: [@key] or @key or [see @key, pp. 33-35]
100
+ */
101
+ static int parse_pandoc_citation(const char *text, int pos, int len,
102
+ apex_citation **citation_out, const apex_options *options) {
103
+ (void)options; /* Unused for now */
104
+ if (pos >= len) return 0;
105
+
106
+ const char *p = text + pos;
107
+ bool in_brackets = false;
108
+ bool author_suppressed = false;
109
+ char *prefix = NULL;
110
+ char *locator = NULL;
111
+ char *suffix = NULL;
112
+ char *key = NULL;
113
+
114
+ /* Check for @key (author-in-text, no brackets) */
115
+ if (*p == '@') {
116
+ p++;
117
+ int key_len = extract_citation_key(text, p - text, len, &key);
118
+ if (key_len > 0 && key) {
119
+ /* Check if there's a locator after: @key [p. 33] */
120
+ const char *after_key = p + key_len;
121
+ while (after_key < text + len && isspace(*after_key)) after_key++;
122
+ if (after_key < text + len && *after_key == '[') {
123
+ /* Extract locator from [p. 33] */
124
+ const char *loc_start = after_key + 1;
125
+ const char *loc_end = strchr(loc_start, ']');
126
+ if (loc_end) {
127
+ size_t loc_len = loc_end - loc_start;
128
+ locator = malloc(loc_len + 1);
129
+ if (locator) {
130
+ memcpy(locator, loc_start, loc_len);
131
+ locator[loc_len] = '\0';
132
+ }
133
+ after_key = loc_end + 1;
134
+ }
135
+ }
136
+
137
+ apex_citation *cite = apex_citation_new(key, APEX_CITATION_PANDOC);
138
+ if (cite) {
139
+ cite->author_in_text = true;
140
+ cite->locator = locator;
141
+ *citation_out = cite;
142
+ free(key);
143
+ return after_key - (text + pos);
144
+ }
145
+ free(key);
146
+ free(locator);
147
+ return 0;
148
+ }
149
+ return 0;
150
+ }
151
+
152
+ /* Check for [-@key] (author suppressed) */
153
+ if (*p == '[' && p + 1 < text + len && p[1] == '-' && p[2] == '@') {
154
+ author_suppressed = true;
155
+ in_brackets = true;
156
+ p += 3; /* Skip [-@ */
157
+ }
158
+ /* Check for [@key] (normal citation) */
159
+ else if (*p == '[' && p + 1 < text + len && p[1] == '@') {
160
+ in_brackets = true;
161
+ p += 2; /* Skip [@ */
162
+ } else {
163
+ return 0; /* Not a Pandoc citation */
164
+ }
165
+
166
+ /* Extract key */
167
+ int key_len = extract_citation_key(text, p - text, len, &key);
168
+ if (key_len == 0 || !key) {
169
+ return 0;
170
+ }
171
+ p += key_len;
172
+
173
+ /* If we're in brackets, look for prefix, locator, suffix */
174
+ if (in_brackets) {
175
+ /* Skip whitespace */
176
+ while (p < text + len && isspace(*p)) p++;
177
+
178
+ /* Check for prefix before @ (e.g., "see @key") */
179
+ if (p > text + pos + 2) {
180
+ const char *prefix_start = text + pos + 1;
181
+ const char *at_pos = strchr(prefix_start, '@');
182
+ if (at_pos && at_pos < p) {
183
+ size_t prefix_len = at_pos - prefix_start;
184
+ while (prefix_len > 0 && isspace(prefix_start[prefix_len - 1])) prefix_len--;
185
+ if (prefix_len > 0) {
186
+ prefix = malloc(prefix_len + 1);
187
+ if (prefix) {
188
+ memcpy(prefix, prefix_start, prefix_len);
189
+ prefix[prefix_len] = '\0';
190
+ }
191
+ }
192
+ }
193
+ }
194
+
195
+ /* Look for locator and suffix after key */
196
+ while (p < text + len && *p != ']') {
197
+ if (*p == ',') {
198
+ p++;
199
+ while (p < text + len && isspace(*p)) p++;
200
+
201
+ /* Try to extract locator (e.g., "pp. 33-35") */
202
+ const char *loc_start = p;
203
+ while (p < text + len && *p != ']' && *p != ';') p++;
204
+
205
+ if (p > loc_start) {
206
+ size_t loc_len = p - loc_start;
207
+ /* Check if this looks like a locator (contains page/chapter indicators) */
208
+ const char *loc_test = loc_start;
209
+ bool looks_like_locator = false;
210
+ while (loc_test < loc_start + loc_len) {
211
+ if (strncmp(loc_test, "p.", 2) == 0 || strncmp(loc_test, "pp.", 3) == 0 ||
212
+ strncmp(loc_test, "chap.", 5) == 0 || strncmp(loc_test, "chapter", 7) == 0 ||
213
+ strncmp(loc_test, "sec.", 4) == 0 || strncmp(loc_test, "section", 7) == 0) {
214
+ looks_like_locator = true;
215
+ break;
216
+ }
217
+ loc_test++;
218
+ }
219
+
220
+ if (looks_like_locator || isdigit(*loc_start)) {
221
+ locator = malloc(loc_len + 1);
222
+ if (locator) {
223
+ memcpy(locator, loc_start, loc_len);
224
+ locator[loc_len] = '\0';
225
+ }
226
+ } else {
227
+ /* Might be suffix */
228
+ suffix = malloc(loc_len + 1);
229
+ if (suffix) {
230
+ memcpy(suffix, loc_start, loc_len);
231
+ suffix[loc_len] = '\0';
232
+ }
233
+ }
234
+ }
235
+ } else if (*p == ';') {
236
+ /* Multiple citations - for now, just handle first one */
237
+ break;
238
+ } else {
239
+ p++;
240
+ }
241
+ }
242
+
243
+ if (p < text + len && *p == ']') {
244
+ p++; /* Skip closing ] */
245
+ }
246
+ }
247
+
248
+ apex_citation *cite = apex_citation_new(key, APEX_CITATION_PANDOC);
249
+ if (cite) {
250
+ cite->author_suppressed = author_suppressed;
251
+ cite->prefix = prefix;
252
+ cite->locator = locator;
253
+ cite->suffix = suffix;
254
+ *citation_out = cite;
255
+ free(key);
256
+ return p - (text + pos);
257
+ }
258
+
259
+ free(key);
260
+ free(prefix);
261
+ free(locator);
262
+ free(suffix);
263
+ return 0;
264
+ }
265
+
266
+ /**
267
+ * Parse MultiMarkdown citation: [#key] or [p. 23][#key]
268
+ */
269
+ static int parse_mmd_citation(const char *text, int pos, int len,
270
+ apex_citation **citation_out, const apex_options *options) {
271
+ (void)options; /* Unused for now */
272
+ if (pos + 2 >= len) return 0;
273
+
274
+ const char *p = text + pos;
275
+
276
+ /* Must start with [# */
277
+ if (*p != '[' || p[1] != '#') return 0;
278
+
279
+ p += 2; /* Skip [# */
280
+
281
+ /* Extract key */
282
+ char *key = NULL;
283
+ int key_len = extract_citation_key(text, p - text, len, &key);
284
+ if (key_len == 0 || !key) {
285
+ return 0;
286
+ }
287
+ p += key_len;
288
+
289
+ /* Must end with ] */
290
+ if (p >= text + len || *p != ']') {
291
+ free(key);
292
+ return 0;
293
+ }
294
+ p++; /* Skip ] */
295
+
296
+ /* Check for locator before: [p. 23][#key] */
297
+ char *locator = NULL;
298
+ if (pos > 0 && text[pos - 1] == ']') {
299
+ /* Look backwards for opening [ */
300
+ const char *loc_start = text + pos - 1;
301
+ while (loc_start > text && *loc_start != '[') loc_start--;
302
+ if (*loc_start == '[') {
303
+ loc_start++; /* Skip [ */
304
+ size_t loc_len = (text + pos - 1) - loc_start;
305
+ if (loc_len > 0) {
306
+ locator = malloc(loc_len + 1);
307
+ if (locator) {
308
+ memcpy(locator, loc_start, loc_len);
309
+ locator[loc_len] = '\0';
310
+ }
311
+ }
312
+ }
313
+ }
314
+
315
+ apex_citation *cite = apex_citation_new(key, APEX_CITATION_MMD);
316
+ if (cite) {
317
+ cite->locator = locator;
318
+ *citation_out = cite;
319
+ free(key);
320
+ return p - (text + pos);
321
+ }
322
+
323
+ free(key);
324
+ free(locator);
325
+ return 0;
326
+ }
327
+
328
+ /**
329
+ * Parse mmark citation: [@RFC2535] or [@!RFC1034] or [@-RFC1000]
330
+ */
331
+ static int parse_mmark_citation(const char *text, int pos, int len,
332
+ apex_citation **citation_out, const apex_options *options) {
333
+ (void)options; /* Unused for now */
334
+ if (pos + 3 >= len) return 0;
335
+
336
+ const char *p = text + pos;
337
+
338
+ /* Must start with [@ */
339
+ if (*p != '[' || p[1] != '@') return 0;
340
+
341
+ p += 2; /* Skip [@ */
342
+
343
+ /* Check for modifier: ! (normative), ? (informative), - (suppressed) */
344
+ bool author_suppressed = false;
345
+ if (*p == '!') {
346
+ p++; /* Normative - for now just skip */
347
+ } else if (*p == '?') {
348
+ p++; /* Informative - for now just skip */
349
+ } else if (*p == '-') {
350
+ author_suppressed = true;
351
+ p++; /* Suppressed */
352
+ }
353
+
354
+ /* Check if it's an RFC/BCP/STD/I-D/W3C pattern */
355
+ if (!is_mmark_pattern(text, p - text, len)) {
356
+ return 0; /* Not mmark pattern, might be Pandoc */
357
+ }
358
+
359
+ /* Extract key (RFC1234, BCP123, etc.) */
360
+ const char *key_start = p;
361
+ while (p < text + len && (isalnum(*p) || *p == '.' || *p == '-' || *p == '#')) {
362
+ p++;
363
+ }
364
+
365
+ if (p == key_start) {
366
+ return 0; /* No key found */
367
+ }
368
+
369
+ size_t key_len = p - key_start;
370
+ char *key = malloc(key_len + 1);
371
+ if (!key) return 0;
372
+ memcpy(key, key_start, key_len);
373
+ key[key_len] = '\0';
374
+
375
+ /* Check for multiple citations: [@RFC1034;@RFC1035] */
376
+ /* For now, just handle first one */
377
+ if (p < text + len && *p == ';') {
378
+ p++; /* Skip ; */
379
+ }
380
+
381
+ /* Must end with ] */
382
+ if (p >= text + len || *p != ']') {
383
+ free(key);
384
+ return 0;
385
+ }
386
+ p++; /* Skip ] */
387
+
388
+ apex_citation *cite = apex_citation_new(key, APEX_CITATION_MMARK);
389
+ if (cite) {
390
+ cite->author_suppressed = author_suppressed;
391
+ *citation_out = cite;
392
+ free(key);
393
+ return p - (text + pos);
394
+ }
395
+
396
+ free(key);
397
+ return 0;
398
+ }
399
+
400
+ /**
401
+ * Process citations in text
402
+ * Returns modified text with citations replaced by placeholders
403
+ */
404
+ char *apex_process_citations(const char *text, apex_citation_registry *registry, const apex_options *options) {
405
+ if (!text || !registry || !options) return NULL;
406
+
407
+ /* Citations only enabled in certain modes */
408
+ if (options->mode != APEX_MODE_MULTIMARKDOWN &&
409
+ options->mode != APEX_MODE_UNIFIED) {
410
+ return NULL; /* Citations not enabled in this mode */
411
+ }
412
+
413
+ if (!options->enable_citations) {
414
+ return NULL; /* Citations disabled */
415
+ }
416
+
417
+ size_t len = strlen(text);
418
+
419
+ /* Quick scan: check if any citation patterns exist before processing */
420
+ /* Look for various citation patterns:
421
+ * - [@key] or [@!key] or [@?key] (Pandoc-style)
422
+ * - @key (author-in-text)
423
+ * - [#key] (MultiMarkdown-style)
424
+ */
425
+ bool has_citation_pattern = false;
426
+ const char *p = text;
427
+ while (*p && p < text + len - 1) {
428
+ if ((*p == '[' && p[1] == '@') || /* Pandoc [@...] */
429
+ (*p == '[' && p[1] == '#') || /* MultiMarkdown [#...] */
430
+ (*p == '@' && (p == text || isspace((unsigned char)p[-1]) || p[-1] == '('))) { /* Author-in-text @... */
431
+ has_citation_pattern = true;
432
+ break;
433
+ }
434
+ p++;
435
+ }
436
+
437
+ /* Early exit if no citation patterns found */
438
+ if (!has_citation_pattern) {
439
+ return NULL;
440
+ }
441
+
442
+ size_t capacity = len * 2; /* Room for placeholders */
443
+ char *output = malloc(capacity);
444
+ if (!output) return NULL;
445
+
446
+ const char *read = text;
447
+ char *write = output;
448
+ size_t remaining = capacity;
449
+ bool in_code_block = false;
450
+ bool in_inline_code = false;
451
+
452
+ while (*read && read - text < (int)len) {
453
+ /* Track code blocks */
454
+ if (*read == '`') {
455
+ if (read[1] == '`' && read[2] == '`') {
456
+ in_code_block = !in_code_block;
457
+ } else if (!in_code_block) {
458
+ in_inline_code = !in_inline_code;
459
+ }
460
+ }
461
+
462
+ /* Skip citations inside code */
463
+ if (in_code_block || in_inline_code) {
464
+ if (remaining > 0) {
465
+ *write++ = *read++;
466
+ remaining--;
467
+ } else {
468
+ read++;
469
+ }
470
+ continue;
471
+ }
472
+
473
+ int pos = read - text;
474
+ apex_citation *citation = NULL;
475
+ int consumed = 0;
476
+
477
+ /* Try mmark first (most specific pattern) */
478
+ if (options->mode == APEX_MODE_UNIFIED) {
479
+ consumed = parse_mmark_citation(text, pos, len, &citation, options);
480
+ }
481
+
482
+ /* Try MultiMarkdown */
483
+ if (!citation && (options->mode == APEX_MODE_MULTIMARKDOWN || options->mode == APEX_MODE_UNIFIED)) {
484
+ consumed = parse_mmd_citation(text, pos, len, &citation, options);
485
+ }
486
+
487
+ /* Try Pandoc (most common) */
488
+ if (!citation && (options->mode == APEX_MODE_UNIFIED)) {
489
+ consumed = parse_pandoc_citation(text, pos, len, &citation, options);
490
+ }
491
+
492
+ if (citation && consumed > 0) {
493
+ /* Add citation to registry */
494
+ citation->position = pos;
495
+ citation->next = registry->citations;
496
+ registry->citations = citation;
497
+ registry->count++;
498
+
499
+ /* Insert placeholder */
500
+ char placeholder[256];
501
+ snprintf(placeholder, sizeof(placeholder), "%s%s%s",
502
+ CITATION_PLACEHOLDER_PREFIX, citation->key, CITATION_PLACEHOLDER_SUFFIX);
503
+ size_t placeholder_len = strlen(placeholder);
504
+
505
+ if (placeholder_len < remaining) {
506
+ memcpy(write, placeholder, placeholder_len);
507
+ write += placeholder_len;
508
+ remaining -= placeholder_len;
509
+ }
510
+
511
+ read += consumed;
512
+ } else {
513
+ /* Copy character */
514
+ if (remaining > 0) {
515
+ *write++ = *read++;
516
+ remaining--;
517
+ } else {
518
+ read++;
519
+ }
520
+ }
521
+ }
522
+
523
+ *write = '\0';
524
+ return output;
525
+ }
526
+
527
+ /**
528
+ * Render citations in HTML
529
+ * Replaces placeholders with formatted HTML
530
+ */
531
+ char *apex_render_citations(const char *html, apex_citation_registry *registry, const apex_options *options) {
532
+ if (!html || !registry || !options) return NULL;
533
+
534
+ if (!options->enable_citations) {
535
+ return NULL;
536
+ }
537
+
538
+ /* For now, just replace placeholders with simple formatted citations */
539
+ size_t len = strlen(html);
540
+ size_t capacity = len * 3 + 1; /* Room for HTML tags + null terminator */
541
+ char *output = malloc(capacity);
542
+ if (!output) return NULL;
543
+
544
+ const char *read = html;
545
+ char *write = output;
546
+ size_t remaining = capacity;
547
+ size_t prefix_len = strlen(CITATION_PLACEHOLDER_PREFIX);
548
+ size_t suffix_len = strlen(CITATION_PLACEHOLDER_SUFFIX);
549
+
550
+ while (*read) {
551
+ if (strncmp(read, CITATION_PLACEHOLDER_PREFIX, prefix_len) == 0) {
552
+ /* Found placeholder */
553
+ const char *key_start = read + prefix_len;
554
+ const char *key_end = strstr(key_start, CITATION_PLACEHOLDER_SUFFIX);
555
+
556
+ if (key_end) {
557
+ size_t key_len = key_end - key_start;
558
+ char *key = malloc(key_len + 1);
559
+ if (key) {
560
+ memcpy(key, key_start, key_len);
561
+ key[key_len] = '\0';
562
+
563
+ /* Find citation in registry */
564
+ apex_citation *cite = registry->citations;
565
+ while (cite) {
566
+ if (cite->key && strcmp(cite->key, key) == 0) {
567
+ /* Try to find bibliography entry for better formatting */
568
+ apex_bibliography_entry *bib_entry = NULL;
569
+ if (registry->bibliography) {
570
+ bib_entry = apex_find_bibliography_entry(registry->bibliography, key);
571
+ }
572
+
573
+ /* Format citation HTML */
574
+ char citation_html[512];
575
+ char citation_text[256] = "";
576
+
577
+ if (bib_entry) {
578
+ /* Use bibliography data for formatting */
579
+ if (cite->author_in_text) {
580
+ if (bib_entry->author && bib_entry->year) {
581
+ snprintf(citation_text, sizeof(citation_text), "%s (%s)",
582
+ bib_entry->author, bib_entry->year);
583
+ } else if (bib_entry->author) {
584
+ strncpy(citation_text, bib_entry->author, sizeof(citation_text) - 1);
585
+ } else {
586
+ strncpy(citation_text, cite->key, sizeof(citation_text) - 1);
587
+ }
588
+ } else if (cite->author_suppressed) {
589
+ if (bib_entry->year) {
590
+ snprintf(citation_text, sizeof(citation_text), "(%s)", bib_entry->year);
591
+ } else {
592
+ snprintf(citation_text, sizeof(citation_text), "(%s)", cite->key);
593
+ }
594
+ } else {
595
+ if (bib_entry->author && bib_entry->year) {
596
+ snprintf(citation_text, sizeof(citation_text), "(%s %s)",
597
+ bib_entry->author, bib_entry->year);
598
+ } else if (bib_entry->year) {
599
+ snprintf(citation_text, sizeof(citation_text), "(%s)", bib_entry->year);
600
+ } else {
601
+ snprintf(citation_text, sizeof(citation_text), "(%s)", cite->key);
602
+ }
603
+ }
604
+ } else {
605
+ /* No bibliography entry, use simple format */
606
+ if (cite->author_in_text) {
607
+ strncpy(citation_text, cite->key, sizeof(citation_text) - 1);
608
+ } else {
609
+ snprintf(citation_text, sizeof(citation_text), "(%s)", cite->key);
610
+ }
611
+ }
612
+
613
+ /* Generate HTML with or without link */
614
+ if (options->link_citations) {
615
+ snprintf(citation_html, sizeof(citation_html),
616
+ "<a href=\"#ref-%s\" class=\"citation\" data-cites=\"%s\">%s</a>",
617
+ cite->key, cite->key, citation_text);
618
+ } else {
619
+ snprintf(citation_html, sizeof(citation_html),
620
+ "<span class=\"citation\" data-cites=\"%s\">%s</span>",
621
+ cite->key, citation_text);
622
+ }
623
+
624
+ size_t html_len = strlen(citation_html);
625
+ if (html_len < remaining) {
626
+ memcpy(write, citation_html, html_len);
627
+ write += html_len;
628
+ remaining -= html_len;
629
+ }
630
+ break;
631
+ }
632
+ cite = cite->next;
633
+ }
634
+
635
+ free(key);
636
+ }
637
+
638
+ read = key_end + suffix_len;
639
+ continue;
640
+ }
641
+ }
642
+
643
+ /* Copy character */
644
+ if (remaining > 1) { /* Reserve 1 byte for null terminator */
645
+ *write++ = *read++;
646
+ remaining--;
647
+ } else {
648
+ read++;
649
+ }
650
+ }
651
+
652
+ /* Null terminate - ensure we have space */
653
+ if (remaining > 0) {
654
+ *write = '\0';
655
+ } else {
656
+ /* Should never happen, but be safe */
657
+ free(output);
658
+ return strdup(html);
659
+ }
660
+ return output;
661
+ }
662
+
663
+ /**
664
+ * Format a bibliography entry as HTML
665
+ */
666
+ static char *format_bibliography_entry(apex_bibliography_entry *entry) {
667
+ if (!entry) return NULL;
668
+
669
+ size_t capacity = 2048; /* Larger capacity for full entries */
670
+ char *html = malloc(capacity);
671
+ if (!html) return NULL;
672
+
673
+ char *write = html;
674
+ size_t remaining = capacity;
675
+ int written;
676
+
677
+ /* Start entry div */
678
+ written = snprintf(write, remaining, "<div id=\"ref-%s\" class=\"csl-entry\">", entry->id ? entry->id : "");
679
+ if (written < 0 || (size_t)written >= remaining) {
680
+ free(html);
681
+ return NULL;
682
+ }
683
+ write += written;
684
+ remaining -= written;
685
+
686
+ /* Format entry content - author-date style */
687
+ bool has_content = false;
688
+
689
+ /* Author */
690
+ if (entry->author && entry->author[0] != '\0') {
691
+ written = snprintf(write, remaining, "%s", entry->author);
692
+ if (written > 0 && (size_t)written < remaining) {
693
+ write += written;
694
+ remaining -= written;
695
+ has_content = true;
696
+ }
697
+ }
698
+
699
+ /* Year */
700
+ if (entry->year && entry->year[0] != '\0') {
701
+ if (has_content) {
702
+ written = snprintf(write, remaining, " %s", entry->year);
703
+ } else {
704
+ written = snprintf(write, remaining, "%s", entry->year);
705
+ }
706
+ if (written > 0 && (size_t)written < remaining) {
707
+ write += written;
708
+ remaining -= written;
709
+ has_content = true;
710
+ }
711
+ }
712
+
713
+ /* Title */
714
+ if (entry->title && entry->title[0] != '\0') {
715
+ if (has_content) {
716
+ written = snprintf(write, remaining, ". ");
717
+ } else {
718
+ written = snprintf(write, remaining, "");
719
+ }
720
+ if (written > 0 && (size_t)written < remaining) {
721
+ write += written;
722
+ remaining -= written;
723
+ }
724
+ written = snprintf(write, remaining, "<em>%s</em>", entry->title);
725
+ if (written > 0 && (size_t)written < remaining) {
726
+ write += written;
727
+ remaining -= written;
728
+ has_content = true;
729
+ }
730
+ }
731
+
732
+ /* Container title (journal) */
733
+ if (entry->container_title && entry->container_title[0] != '\0') {
734
+ if (has_content) {
735
+ written = snprintf(write, remaining, ". ");
736
+ } else {
737
+ written = snprintf(write, remaining, "");
738
+ }
739
+ if (written > 0 && (size_t)written < remaining) {
740
+ write += written;
741
+ remaining -= written;
742
+ }
743
+ written = snprintf(write, remaining, "<em>%s</em>", entry->container_title);
744
+ if (written > 0 && (size_t)written < remaining) {
745
+ write += written;
746
+ remaining -= written;
747
+ has_content = true;
748
+ }
749
+ }
750
+
751
+ /* Volume and pages */
752
+ if (entry->volume && entry->volume[0] != '\0') {
753
+ if (has_content) {
754
+ written = snprintf(write, remaining, " ");
755
+ } else {
756
+ written = snprintf(write, remaining, "");
757
+ }
758
+ if (written > 0 && (size_t)written < remaining) {
759
+ write += written;
760
+ remaining -= written;
761
+ }
762
+ written = snprintf(write, remaining, "%s", entry->volume);
763
+ if (written > 0 && (size_t)written < remaining) {
764
+ write += written;
765
+ remaining -= written;
766
+ has_content = true;
767
+ }
768
+ }
769
+
770
+ if (entry->page && entry->page[0] != '\0') {
771
+ if (has_content) {
772
+ written = snprintf(write, remaining, ": %s", entry->page);
773
+ } else {
774
+ written = snprintf(write, remaining, "%s", entry->page);
775
+ }
776
+ if (written > 0 && (size_t)written < remaining) {
777
+ write += written;
778
+ remaining -= written;
779
+ has_content = true;
780
+ }
781
+ }
782
+
783
+ /* Publisher */
784
+ if (entry->publisher && entry->publisher[0] != '\0') {
785
+ if (has_content) {
786
+ written = snprintf(write, remaining, ". ");
787
+ } else {
788
+ written = snprintf(write, remaining, "");
789
+ }
790
+ if (written > 0 && (size_t)written < remaining) {
791
+ write += written;
792
+ remaining -= written;
793
+ }
794
+ written = snprintf(write, remaining, "%s", entry->publisher);
795
+ if (written > 0 && (size_t)written < remaining) {
796
+ write += written;
797
+ remaining -= written;
798
+ has_content = true;
799
+ }
800
+ }
801
+
802
+ /* Close entry div */
803
+ written = snprintf(write, remaining, "</div>\n");
804
+ if (written > 0 && (size_t)written < remaining) {
805
+ write += written;
806
+ }
807
+
808
+ return html;
809
+ }
810
+
811
+ /**
812
+ * Generate bibliography HTML from cited entries
813
+ */
814
+ char *apex_generate_bibliography(apex_citation_registry *registry, const apex_options *options) {
815
+ if (!registry || !options) return NULL;
816
+
817
+ if (options->suppress_bibliography) {
818
+ return NULL;
819
+ }
820
+
821
+ if (!registry->bibliography) {
822
+ return NULL;
823
+ }
824
+
825
+ if (registry->bibliography->count == 0) {
826
+ return NULL;
827
+ }
828
+
829
+ /* Collect unique cited entries */
830
+ size_t cited_count = 0;
831
+ size_t cited_capacity = 16;
832
+ apex_bibliography_entry **cited_entries = malloc(cited_capacity * sizeof(apex_bibliography_entry*));
833
+ if (!cited_entries) {
834
+ return NULL;
835
+ }
836
+
837
+ /* Build list of cited entries */
838
+ apex_citation *cite = registry->citations;
839
+ while (cite) {
840
+ if (cite->key) {
841
+ apex_bibliography_entry *entry = apex_find_bibliography_entry(registry->bibliography, cite->key);
842
+ if (entry) {
843
+ /* Check if already in list */
844
+ bool already_added = false;
845
+ for (size_t i = 0; i < cited_count; i++) {
846
+ if (cited_entries[i] == entry) {
847
+ already_added = true;
848
+ break;
849
+ }
850
+ }
851
+ if (!already_added) {
852
+ /* Add to list */
853
+ if (cited_count >= cited_capacity) {
854
+ cited_capacity *= 2;
855
+ apex_bibliography_entry **new_entries = realloc(cited_entries, cited_capacity * sizeof(apex_bibliography_entry*));
856
+ if (!new_entries) {
857
+ free(cited_entries);
858
+ return NULL;
859
+ }
860
+ cited_entries = new_entries;
861
+ }
862
+ cited_entries[cited_count++] = entry;
863
+ }
864
+ }
865
+ }
866
+ cite = cite->next;
867
+ }
868
+
869
+ if (cited_count == 0) {
870
+ if (cited_entries) free(cited_entries);
871
+ /* No cited entries found - bibliography might not match citations */
872
+ /* Return NULL so insertion doesn't add empty bibliography */
873
+ return NULL;
874
+ }
875
+
876
+ /* Generate bibliography HTML */
877
+ size_t capacity = 4096;
878
+ char *html = malloc(capacity);
879
+ if (!html) {
880
+ free(cited_entries);
881
+ return NULL;
882
+ }
883
+
884
+ char *write = html;
885
+ size_t remaining = capacity;
886
+
887
+ /* Start bibliography div */
888
+ snprintf(write, remaining, "<div id=\"refs\" class=\"references csl-bib-body\">\n");
889
+ size_t len = strlen(write);
890
+ write += len;
891
+ remaining -= len;
892
+
893
+ /* Add each entry */
894
+ for (size_t i = 0; i < cited_count; i++) {
895
+ if (!cited_entries[i]) {
896
+ continue;
897
+ }
898
+ char *entry_html = format_bibliography_entry(cited_entries[i]);
899
+ if (entry_html) {
900
+ size_t entry_len = strlen(entry_html);
901
+ if (entry_len >= remaining) {
902
+ size_t used = write - html;
903
+ capacity = (used + entry_len + 1) * 2;
904
+ char *new_html = realloc(html, capacity);
905
+ if (!new_html) {
906
+ free(html);
907
+ free(entry_html);
908
+ free(cited_entries);
909
+ return NULL;
910
+ }
911
+ html = new_html;
912
+ write = html + used;
913
+ remaining = capacity - used;
914
+ }
915
+ memcpy(write, entry_html, entry_len);
916
+ write += entry_len;
917
+ remaining -= entry_len;
918
+ free(entry_html);
919
+ }
920
+ }
921
+
922
+ /* Close bibliography div */
923
+ snprintf(write, remaining, "</div>\n");
924
+ write += strlen(write);
925
+
926
+ free(cited_entries);
927
+ return html;
928
+ }
929
+
930
+ /**
931
+ * Insert bibliography at <!-- REFERENCES --> marker or end of document
932
+ */
933
+ char *apex_insert_bibliography(const char *html, apex_citation_registry *registry, const apex_options *options) {
934
+ if (!html || !registry || !options) return NULL;
935
+
936
+ if (options->suppress_bibliography) {
937
+ return strdup(html);
938
+ }
939
+
940
+ /* Generate bibliography */
941
+ char *bibliography_html = apex_generate_bibliography(registry, options);
942
+ if (!bibliography_html) {
943
+ return strdup(html); /* No bibliography to insert */
944
+ }
945
+
946
+ size_t bib_len = strlen(bibliography_html);
947
+ size_t html_len = strlen(html);
948
+ size_t output_capacity = html_len + bib_len + 100;
949
+ char *output = malloc(output_capacity);
950
+ if (!output) {
951
+ free(bibliography_html);
952
+ return strdup(html);
953
+ }
954
+
955
+ /* Look for <!-- REFERENCES --> marker */
956
+ const char *refs_marker = strstr(html, "<!-- REFERENCES -->");
957
+ if (refs_marker) {
958
+ /* Insert at marker */
959
+ size_t before_len = refs_marker - html;
960
+ const char *after = refs_marker + strlen("<!-- REFERENCES -->");
961
+ size_t after_len = strlen(after);
962
+
963
+ memcpy(output, html, before_len);
964
+ memcpy(output + before_len, bibliography_html, bib_len);
965
+ memcpy(output + before_len + bib_len, after, after_len + 1);
966
+ } else {
967
+ /* Look for {backmatter} marker (mmark style) */
968
+ const char *backmatter = strstr(html, "{backmatter}");
969
+ if (backmatter) {
970
+ size_t before_len = backmatter - html;
971
+ const char *after = backmatter + strlen("{backmatter}");
972
+ size_t after_len = strlen(after);
973
+
974
+ memcpy(output, html, before_len);
975
+ memcpy(output + before_len, bibliography_html, bib_len);
976
+ memcpy(output + before_len + bib_len, after, after_len + 1);
977
+ } else {
978
+ /* Look for <div id="refs"> */
979
+ const char *refs_div = strstr(html, "<div id=\"refs\">");
980
+ if (refs_div) {
981
+ /* Insert before closing </div> */
982
+ const char *div_end = strstr(refs_div, "</div>");
983
+ if (div_end) {
984
+ size_t before_len = div_end - html;
985
+ size_t after_len = strlen(div_end);
986
+
987
+ memcpy(output, html, before_len);
988
+ memcpy(output + before_len, bibliography_html, bib_len);
989
+ memcpy(output + before_len + bib_len, div_end, after_len + 1);
990
+ } else {
991
+ /* No closing div, append to end */
992
+ memcpy(output, html, html_len);
993
+ memcpy(output + html_len, bibliography_html, bib_len + 1);
994
+ }
995
+ } else {
996
+ /* Append to end of document */
997
+ memcpy(output, html, html_len);
998
+ memcpy(output + html_len, bibliography_html, bib_len + 1);
999
+ }
1000
+ }
1001
+ }
1002
+
1003
+ free(bibliography_html);
1004
+ return output;
1005
+ }
1006
+
1007
+ /**
1008
+ * Create citation extension (stub - uses preprocessing)
1009
+ */
1010
+ cmark_syntax_extension *create_citations_extension(void) {
1011
+ return NULL; /* Citations handled via preprocessing */
1012
+ }
1013
+
1014
+ /**
1015
+ * Create a new citation
1016
+ */
1017
+ apex_citation *apex_citation_new(const char *key, apex_citation_syntax_t syntax_type) {
1018
+ if (!key) return NULL;
1019
+
1020
+ apex_citation *cite = malloc(sizeof(apex_citation));
1021
+ if (!cite) return NULL;
1022
+
1023
+ cite->key = strdup(key);
1024
+ cite->prefix = NULL;
1025
+ cite->locator = NULL;
1026
+ cite->suffix = NULL;
1027
+ cite->author_suppressed = false;
1028
+ cite->author_in_text = false;
1029
+ cite->syntax_type = syntax_type;
1030
+ cite->position = 0;
1031
+ cite->next = NULL;
1032
+
1033
+ return cite;
1034
+ }
1035
+
1036
+ /**
1037
+ * Free a citation
1038
+ */
1039
+ void apex_citation_free(apex_citation *citation) {
1040
+ if (!citation) return;
1041
+
1042
+ free(citation->key);
1043
+ free(citation->prefix);
1044
+ free(citation->locator);
1045
+ free(citation->suffix);
1046
+ free(citation);
1047
+ }
1048
+
1049
+ /**
1050
+ * Free citation registry
1051
+ */
1052
+ void apex_free_citation_registry(apex_citation_registry *registry) {
1053
+ if (!registry) return;
1054
+
1055
+ apex_citation *cite = registry->citations;
1056
+ while (cite) {
1057
+ apex_citation *next = cite->next;
1058
+ apex_citation_free(cite);
1059
+ cite = next;
1060
+ }
1061
+
1062
+ if (registry->bibliography) {
1063
+ apex_free_bibliography_registry(registry->bibliography);
1064
+ }
1065
+
1066
+ registry->citations = NULL;
1067
+ registry->count = 0;
1068
+ registry->bibliography = NULL;
1069
+ }
1070
+
1071
+ /**
1072
+ * Read file into buffer
1073
+ */
1074
+ static char *read_bibliography_file(const char *filepath) {
1075
+ if (!filepath) return NULL;
1076
+
1077
+ FILE *fp = fopen(filepath, "r");
1078
+ if (!fp) return NULL;
1079
+
1080
+ /* Get file size */
1081
+ fseek(fp, 0, SEEK_END);
1082
+ long file_size = ftell(fp);
1083
+ fseek(fp, 0, SEEK_SET);
1084
+
1085
+ if (file_size < 0 || file_size > 10 * 1024 * 1024) { /* Max 10MB */
1086
+ fclose(fp);
1087
+ return NULL;
1088
+ }
1089
+
1090
+ char *buffer = malloc(file_size + 1);
1091
+ if (!buffer) {
1092
+ fclose(fp);
1093
+ return NULL;
1094
+ }
1095
+
1096
+ size_t bytes_read = fread(buffer, 1, file_size, fp);
1097
+ buffer[bytes_read] = '\0';
1098
+ fclose(fp);
1099
+
1100
+ return buffer;
1101
+ }
1102
+
1103
+ /**
1104
+ * Resolve bibliography file path relative to base directory
1105
+ */
1106
+ static char *resolve_bibliography_path(const char *filepath, const char *base_directory) {
1107
+ if (!filepath) return NULL;
1108
+
1109
+ /* If absolute path or starts with ./ or ../, use as-is */
1110
+ if (filepath[0] == '/' || (filepath[0] == '.' && (filepath[1] == '/' || filepath[1] == '.'))) {
1111
+ return strdup(filepath);
1112
+ }
1113
+
1114
+ /* If base_directory is provided, resolve relative to it */
1115
+ if (base_directory && base_directory[0] != '\0') {
1116
+ size_t base_len = strlen(base_directory);
1117
+ size_t file_len = strlen(filepath);
1118
+ char *resolved = malloc(base_len + file_len + 2);
1119
+ if (resolved) {
1120
+ strcpy(resolved, base_directory);
1121
+ if (resolved[base_len - 1] != '/') {
1122
+ resolved[base_len] = '/';
1123
+ base_len++;
1124
+ }
1125
+ strcpy(resolved + base_len, filepath);
1126
+ }
1127
+ return resolved;
1128
+ }
1129
+
1130
+ return strdup(filepath);
1131
+ }
1132
+
1133
+ /**
1134
+ * Detect bibliography format from file extension
1135
+ */
1136
+ typedef enum {
1137
+ BIB_FORMAT_BIBTEX,
1138
+ BIB_FORMAT_CSL_JSON,
1139
+ BIB_FORMAT_CSL_YAML,
1140
+ BIB_FORMAT_UNKNOWN
1141
+ } bibliography_format_t;
1142
+
1143
+ static bibliography_format_t detect_bibliography_format(const char *filepath) {
1144
+ if (!filepath) return BIB_FORMAT_UNKNOWN;
1145
+
1146
+ const char *ext = strrchr(filepath, '.');
1147
+ if (!ext) return BIB_FORMAT_UNKNOWN;
1148
+
1149
+ ext++; /* Skip the dot */
1150
+
1151
+ if (strcasecmp(ext, "bib") == 0 || strcasecmp(ext, "bibtex") == 0) {
1152
+ return BIB_FORMAT_BIBTEX;
1153
+ } else if (strcasecmp(ext, "json") == 0) {
1154
+ return BIB_FORMAT_CSL_JSON;
1155
+ } else if (strcasecmp(ext, "yaml") == 0 || strcasecmp(ext, "yml") == 0) {
1156
+ return BIB_FORMAT_CSL_YAML;
1157
+ }
1158
+
1159
+ return BIB_FORMAT_UNKNOWN;
1160
+ }
1161
+
1162
+ /**
1163
+ * Create a new bibliography entry
1164
+ */
1165
+ static apex_bibliography_entry *bibliography_entry_new(const char *id) {
1166
+ if (!id) return NULL;
1167
+
1168
+ apex_bibliography_entry *entry = malloc(sizeof(apex_bibliography_entry));
1169
+ if (!entry) return NULL;
1170
+
1171
+ entry->id = strdup(id);
1172
+ entry->type = NULL;
1173
+ entry->title = NULL;
1174
+ entry->author = NULL;
1175
+ entry->year = NULL;
1176
+ entry->container_title = NULL;
1177
+ entry->publisher = NULL;
1178
+ entry->volume = NULL;
1179
+ entry->page = NULL;
1180
+ entry->raw_data = NULL;
1181
+ entry->next = NULL;
1182
+
1183
+ return entry;
1184
+ }
1185
+
1186
+ /**
1187
+ * Free bibliography entry
1188
+ */
1189
+ void apex_bibliography_entry_free(apex_bibliography_entry *entry) {
1190
+ if (!entry) return;
1191
+
1192
+ free(entry->id);
1193
+ free(entry->type);
1194
+ free(entry->title);
1195
+ free(entry->author);
1196
+ free(entry->year);
1197
+ free(entry->container_title);
1198
+ free(entry->publisher);
1199
+ free(entry->volume);
1200
+ free(entry->page);
1201
+ free(entry->raw_data);
1202
+ free(entry);
1203
+ }
1204
+
1205
+ /**
1206
+ * Free bibliography registry
1207
+ */
1208
+ void apex_free_bibliography_registry(apex_bibliography_registry *registry) {
1209
+ if (!registry) return;
1210
+
1211
+ apex_bibliography_entry *entry = registry->entries;
1212
+ while (entry) {
1213
+ apex_bibliography_entry *next = entry->next;
1214
+ apex_bibliography_entry_free(entry);
1215
+ entry = next;
1216
+ }
1217
+
1218
+ registry->entries = NULL;
1219
+ registry->count = 0;
1220
+ }
1221
+
1222
+ /**
1223
+ * Find bibliography entry by ID
1224
+ */
1225
+ apex_bibliography_entry *apex_find_bibliography_entry(apex_bibliography_registry *registry, const char *id) {
1226
+ if (!registry || !id) return NULL;
1227
+
1228
+ apex_bibliography_entry *entry = registry->entries;
1229
+ while (entry) {
1230
+ if (entry->id && strcmp(entry->id, id) == 0) {
1231
+ return entry;
1232
+ }
1233
+ entry = entry->next;
1234
+ }
1235
+
1236
+ return NULL;
1237
+ }
1238
+
1239
+ /**
1240
+ * Trim whitespace from string (in-place)
1241
+ */
1242
+ static char *trim_string(char *str) {
1243
+ if (!str) return NULL;
1244
+
1245
+ /* Trim leading whitespace */
1246
+ while (isspace((unsigned char)*str)) str++;
1247
+
1248
+ if (*str == 0) return str;
1249
+
1250
+ /* Trim trailing whitespace */
1251
+ char *end = str + strlen(str) - 1;
1252
+ while (end > str && isspace((unsigned char)*end)) end--;
1253
+ end[1] = '\0';
1254
+
1255
+ return str;
1256
+ }
1257
+
1258
+ /**
1259
+ * Remove braces from BibTeX value
1260
+ */
1261
+ static char *remove_braces(const char *value) {
1262
+ if (!value) return NULL;
1263
+
1264
+ size_t len = strlen(value);
1265
+ char *result = malloc(len + 1);
1266
+ if (!result) return NULL;
1267
+
1268
+ const char *src = value;
1269
+ char *dst = result;
1270
+ while (*src) {
1271
+ if (*src != '{' && *src != '}') {
1272
+ *dst++ = *src;
1273
+ }
1274
+ src++;
1275
+ }
1276
+ *dst = '\0';
1277
+
1278
+ return trim_string(result);
1279
+ }
1280
+
1281
+ /**
1282
+ * Parse BibTeX entry type
1283
+ */
1284
+ static const char *parse_bibtex_entry_type(const char *text, const char **entry_start) {
1285
+ if (!text) return NULL;
1286
+
1287
+ const char *p = text;
1288
+
1289
+ /* Skip whitespace */
1290
+ while (*p && isspace(*p)) p++;
1291
+
1292
+ /* Must start with @ */
1293
+ if (*p != '@') return NULL;
1294
+ p++;
1295
+
1296
+ /* Extract entry type (between @ and {) */
1297
+ const char *type_start = p;
1298
+ while (*p && *p != '{' && !isspace(*p)) p++;
1299
+
1300
+ if (*p != '{') return NULL;
1301
+
1302
+ /* Extract entry type */
1303
+ const char *type_end = p; /* At { */
1304
+
1305
+ size_t type_len = type_end - type_start;
1306
+ if (type_len == 0) return NULL;
1307
+
1308
+ char *type = malloc(type_len + 1);
1309
+ if (!type) return NULL;
1310
+ memcpy(type, type_start, type_len);
1311
+ type[type_len] = '\0';
1312
+
1313
+ /* Convert to lowercase */
1314
+ for (char *t = type; *t; t++) {
1315
+ *t = tolower(*t);
1316
+ }
1317
+
1318
+ if (entry_start) *entry_start = p + 1; /* After { */
1319
+
1320
+ return type;
1321
+ }
1322
+
1323
+ /**
1324
+ * Parse BibTeX field: key = {value} or key = value
1325
+ * Returns true if successful, and sets *end_pos to position after the field
1326
+ */
1327
+ static bool parse_bibtex_field(const char *text, char **key_out, char **value_out, const char **end_pos) {
1328
+ if (!text) return false;
1329
+
1330
+ const char *p = text;
1331
+
1332
+ /* Skip whitespace */
1333
+ while (*p && isspace(*p)) p++;
1334
+ if (!*p) return false;
1335
+
1336
+ /* Find = */
1337
+ const char *key_start = p;
1338
+ const char *equals = strchr(p, '=');
1339
+ if (!equals) return false;
1340
+
1341
+ /* Extract key */
1342
+ const char *key_end = equals;
1343
+ while (key_end > key_start && isspace(key_end[-1])) key_end--;
1344
+
1345
+ size_t key_len = key_end - key_start;
1346
+ if (key_len == 0) return false;
1347
+
1348
+ *key_out = malloc(key_len + 1);
1349
+ if (!*key_out) return false;
1350
+ memcpy(*key_out, key_start, key_len);
1351
+ (*key_out)[key_len] = '\0';
1352
+ trim_string(*key_out);
1353
+
1354
+ /* Find value */
1355
+ p = equals + 1;
1356
+ while (*p && isspace(*p)) p++;
1357
+
1358
+ if (*p == '{') {
1359
+ /* Braced value */
1360
+ p++;
1361
+ const char *value_start = p;
1362
+ int brace_depth = 1;
1363
+
1364
+ while (*p && brace_depth > 0) {
1365
+ if (*p == '{') brace_depth++;
1366
+ else if (*p == '}') brace_depth--;
1367
+ p++;
1368
+ }
1369
+
1370
+ if (brace_depth == 0) {
1371
+ size_t value_len = (p - 1) - value_start; /* Exclude closing } */
1372
+ *value_out = malloc(value_len + 1);
1373
+ if (*value_out) {
1374
+ memcpy(*value_out, value_start, value_len);
1375
+ (*value_out)[value_len] = '\0';
1376
+ char *cleaned = remove_braces(*value_out);
1377
+ if (cleaned) {
1378
+ free(*value_out);
1379
+ *value_out = cleaned;
1380
+ }
1381
+ }
1382
+ if (end_pos) {
1383
+ *end_pos = p; /* Position after closing } */
1384
+ }
1385
+ } else {
1386
+ /* Unmatched braces, return failure */
1387
+ if (end_pos) *end_pos = NULL;
1388
+ return false;
1389
+ }
1390
+ } else {
1391
+ /* Unbraced value (until comma or closing brace) */
1392
+ const char *value_start = p;
1393
+ while (*p && *p != ',' && *p != '}') p++;
1394
+
1395
+ size_t value_len = p - value_start;
1396
+ while (value_len > 0 && isspace(value_start[value_len - 1])) value_len--;
1397
+
1398
+ *value_out = malloc(value_len + 1);
1399
+ if (*value_out) {
1400
+ memcpy(*value_out, value_start, value_len);
1401
+ (*value_out)[value_len] = '\0';
1402
+ trim_string(*value_out);
1403
+ }
1404
+ if (end_pos) {
1405
+ *end_pos = p; /* Position after the field value */
1406
+ }
1407
+ }
1408
+
1409
+ return (*value_out != NULL);
1410
+
1411
+ return (*value_out != NULL);
1412
+ }
1413
+
1414
+ /**
1415
+ * Map BibTeX entry type to CSL type
1416
+ */
1417
+ static const char *bibtex_to_csl_type(const char *bibtex_type) {
1418
+ if (!bibtex_type) return "article";
1419
+
1420
+ /* Common BibTeX types */
1421
+ if (strcmp(bibtex_type, "article") == 0) return "article-journal";
1422
+ if (strcmp(bibtex_type, "book") == 0) return "book";
1423
+ if (strcmp(bibtex_type, "inbook") == 0) return "chapter";
1424
+ if (strcmp(bibtex_type, "incollection") == 0) return "chapter";
1425
+ if (strcmp(bibtex_type, "inproceedings") == 0) return "paper-conference";
1426
+ if (strcmp(bibtex_type, "phdthesis") == 0) return "thesis";
1427
+ if (strcmp(bibtex_type, "mastersthesis") == 0) return "thesis";
1428
+ if (strcmp(bibtex_type, "techreport") == 0) return "report";
1429
+
1430
+ return "article"; /* Default */
1431
+ }
1432
+
1433
+ /**
1434
+ * Parse BibTeX file
1435
+ */
1436
+ apex_bibliography_registry *apex_parse_bibtex(const char *content) {
1437
+ if (!content) return NULL;
1438
+
1439
+ apex_bibliography_registry *registry = malloc(sizeof(apex_bibliography_registry));
1440
+ if (!registry) return NULL;
1441
+ registry->entries = NULL;
1442
+ registry->count = 0;
1443
+
1444
+ const char *p = content;
1445
+
1446
+ while (*p) {
1447
+ /* Find @ entry */
1448
+ while (*p && *p != '@') p++;
1449
+ if (!*p) break;
1450
+
1451
+ const char *entry_start;
1452
+ const char *type_str = parse_bibtex_entry_type(p, &entry_start);
1453
+ if (!type_str) {
1454
+ p++;
1455
+ continue;
1456
+ }
1457
+
1458
+ /* Find entry key (after {) */
1459
+ const char *key_start = entry_start;
1460
+ while (*key_start && (isspace(*key_start) || *key_start == ',')) key_start++;
1461
+
1462
+ const char *key_end = key_start;
1463
+ while (*key_end && *key_end != ',' && *key_end != '}') key_end++;
1464
+
1465
+ if (key_end == key_start) {
1466
+ free((void*)type_str);
1467
+ p++;
1468
+ continue;
1469
+ }
1470
+
1471
+ size_t key_len = key_end - key_start;
1472
+ char *entry_id = malloc(key_len + 1);
1473
+ if (!entry_id) {
1474
+ free((void*)type_str);
1475
+ p++;
1476
+ continue;
1477
+ }
1478
+ memcpy(entry_id, key_start, key_len);
1479
+ entry_id[key_len] = '\0';
1480
+ trim_string(entry_id);
1481
+
1482
+ /* Create entry */
1483
+ apex_bibliography_entry *entry = bibliography_entry_new(entry_id);
1484
+ free(entry_id);
1485
+
1486
+ if (entry) {
1487
+ entry->type = strdup(bibtex_to_csl_type(type_str));
1488
+
1489
+ /* Parse fields */
1490
+ const char *field_start = key_end;
1491
+ while (*field_start && *field_start != '}') {
1492
+ if (*field_start == ',') field_start++;
1493
+ while (*field_start && isspace(*field_start)) field_start++;
1494
+ if (*field_start == '}') break;
1495
+
1496
+ char *field_key = NULL;
1497
+ char *field_value = NULL;
1498
+ const char *field_end = NULL;
1499
+
1500
+ if (parse_bibtex_field(field_start, &field_key, &field_value, &field_end)) {
1501
+ if (field_key && field_value) {
1502
+ /* Map BibTeX fields to CSL fields */
1503
+ if (strcasecmp(field_key, "title") == 0) {
1504
+ entry->title = field_value;
1505
+ field_value = NULL; /* Don't free, we're using it */
1506
+ } else if (strcasecmp(field_key, "author") == 0) {
1507
+ entry->author = field_value;
1508
+ field_value = NULL;
1509
+ } else if (strcasecmp(field_key, "year") == 0) {
1510
+ entry->year = field_value;
1511
+ field_value = NULL;
1512
+ } else if (strcasecmp(field_key, "journal") == 0) {
1513
+ entry->container_title = field_value;
1514
+ field_value = NULL;
1515
+ } else if (strcasecmp(field_key, "publisher") == 0) {
1516
+ entry->publisher = field_value;
1517
+ field_value = NULL;
1518
+ } else if (strcasecmp(field_key, "volume") == 0) {
1519
+ entry->volume = field_value;
1520
+ field_value = NULL;
1521
+ } else if (strcasecmp(field_key, "pages") == 0) {
1522
+ entry->page = field_value;
1523
+ field_value = NULL;
1524
+ }
1525
+
1526
+ if (field_value) free(field_value);
1527
+ }
1528
+ free(field_key);
1529
+
1530
+ /* Advance past this field */
1531
+ if (field_end) {
1532
+ field_start = field_end;
1533
+ /* Skip comma if present */
1534
+ while (*field_start && (*field_start == ',' || isspace(*field_start))) {
1535
+ field_start++;
1536
+ }
1537
+ } else {
1538
+ /* Fallback: find next comma or closing brace */
1539
+ field_start = strchr(field_start, ',');
1540
+ if (field_start) {
1541
+ field_start++;
1542
+ } else {
1543
+ field_start = strchr(field_start ? field_start : content, '}');
1544
+ break; /* End of entry */
1545
+ }
1546
+ }
1547
+ } else {
1548
+ break; /* Can't parse more fields */
1549
+ }
1550
+ }
1551
+
1552
+ /* Add to registry */
1553
+ entry->next = registry->entries;
1554
+ registry->entries = entry;
1555
+ registry->count++;
1556
+ }
1557
+
1558
+ free((void*)type_str);
1559
+
1560
+ /* Find next entry */
1561
+ p = strchr(p, '}');
1562
+ if (p) p++;
1563
+ }
1564
+
1565
+ return registry;
1566
+ }
1567
+
1568
+ /**
1569
+ * Parse CSL JSON file (basic implementation)
1570
+ * For now, just extract id, type, title, author, year
1571
+ */
1572
+ apex_bibliography_registry *apex_parse_csl_json(const char *content) {
1573
+ if (!content) return NULL;
1574
+
1575
+ /* Very basic JSON parsing - just look for "id" fields */
1576
+ /* For a full implementation, we'd use a JSON library like cJSON */
1577
+ /* This is a minimal implementation for MVP */
1578
+
1579
+ apex_bibliography_registry *registry = malloc(sizeof(apex_bibliography_registry));
1580
+ if (!registry) return NULL;
1581
+ registry->entries = NULL;
1582
+ registry->count = 0;
1583
+
1584
+ /* TODO: Implement proper JSON parsing */
1585
+ /* For now, return empty registry - will be enhanced later */
1586
+
1587
+ return registry;
1588
+ }
1589
+
1590
+ /**
1591
+ * Get indentation level (number of leading spaces)
1592
+ */
1593
+ static int get_indent_level(const char *line) {
1594
+ int indent = 0;
1595
+ while (*line == ' ') {
1596
+ indent++;
1597
+ line++;
1598
+ }
1599
+ return indent;
1600
+ }
1601
+
1602
+ /**
1603
+ * Extract YAML value (handles quoted and unquoted strings)
1604
+ */
1605
+ static char *extract_yaml_value(const char *value_str) {
1606
+ if (!value_str) return NULL;
1607
+
1608
+ /* Skip leading whitespace */
1609
+ while (*value_str && isspace(*value_str)) value_str++;
1610
+ if (!*value_str) return NULL;
1611
+
1612
+ /* Check for quotes */
1613
+ char quote_char = 0;
1614
+ if (*value_str == '"' || *value_str == '\'') {
1615
+ quote_char = *value_str;
1616
+ value_str++;
1617
+ }
1618
+
1619
+ const char *value_start = value_str;
1620
+ const char *value_end = value_str;
1621
+
1622
+ if (quote_char) {
1623
+ /* Find closing quote */
1624
+ while (*value_end && *value_end != quote_char) {
1625
+ if (*value_end == '\\' && value_end[1]) value_end++; /* Skip escaped chars */
1626
+ value_end++;
1627
+ }
1628
+ } else {
1629
+ /* Find end of value (newline or comment) */
1630
+ while (*value_end && *value_end != '\n' && *value_end != '\r' && *value_end != '#') {
1631
+ value_end++;
1632
+ }
1633
+ /* Trim trailing whitespace */
1634
+ while (value_end > value_start && isspace(value_end[-1])) value_end--;
1635
+ }
1636
+
1637
+ size_t value_len = value_end - value_start;
1638
+ if (value_len == 0) return NULL;
1639
+
1640
+ char *value = malloc(value_len + 1);
1641
+ if (!value) return NULL;
1642
+ memcpy(value, value_start, value_len);
1643
+ value[value_len] = '\0';
1644
+
1645
+ return trim_string(value);
1646
+ }
1647
+
1648
+ /**
1649
+ * Parse author from YAML structure
1650
+ * Handles: author: {family: Doe, given: John} or author: [{family: Doe, given: John}]
1651
+ * Also handles multi-line format:
1652
+ * author:
1653
+ * - family: Doe
1654
+ * given: John
1655
+ */
1656
+ static char *parse_yaml_author(const char *content, int base_indent) {
1657
+ if (!content) return NULL;
1658
+
1659
+ const char *p = content;
1660
+ char *family = NULL;
1661
+ char *given = NULL;
1662
+
1663
+ /* Skip whitespace and opening brace/bracket */
1664
+ while (*p && (isspace(*p) || *p == '{' || *p == '[')) p++;
1665
+
1666
+ /* Look for family: and given: fields */
1667
+ while (*p) {
1668
+ /* Check for list item marker */
1669
+ if (*p == '-' && isspace(p[1])) {
1670
+ p++;
1671
+ while (*p && isspace(*p)) p++;
1672
+ }
1673
+
1674
+ if (strncmp(p, "family:", 7) == 0) {
1675
+ p += 7;
1676
+ while (*p && isspace(*p)) p++;
1677
+ family = extract_yaml_value(p);
1678
+ if (family) {
1679
+ /* Find end of this line */
1680
+ while (*p && *p != '\n') p++;
1681
+ if (*p == '\n') p++;
1682
+ }
1683
+ } else if (strncmp(p, "given:", 6) == 0) {
1684
+ p += 6;
1685
+ while (*p && isspace(*p)) p++;
1686
+ given = extract_yaml_value(p);
1687
+ if (given) {
1688
+ while (*p && *p != '\n') p++;
1689
+ if (*p == '\n') p++;
1690
+ }
1691
+ } else if (*p == '\n') {
1692
+ /* Check next line for continuation */
1693
+ p++;
1694
+ int next_indent = get_indent_level(p);
1695
+ if (next_indent <= base_indent) {
1696
+ break; /* End of author block */
1697
+ }
1698
+ /* Continue parsing on next line */
1699
+ } else if (*p == '}' || *p == ']') {
1700
+ break; /* End of structure */
1701
+ } else {
1702
+ p++;
1703
+ }
1704
+ }
1705
+
1706
+ /* Format author name */
1707
+ if (family || given) {
1708
+ size_t len = (family ? strlen(family) : 0) + (given ? strlen(given) : 0) + 3;
1709
+ char *author = malloc(len);
1710
+ if (author) {
1711
+ if (family && given) {
1712
+ snprintf(author, len, "%s, %s", family, given);
1713
+ } else if (family) {
1714
+ strcpy(author, family);
1715
+ } else {
1716
+ strcpy(author, given);
1717
+ }
1718
+ }
1719
+ free(family);
1720
+ free(given);
1721
+ return author;
1722
+ }
1723
+
1724
+ free(family);
1725
+ free(given);
1726
+ return NULL;
1727
+ }
1728
+
1729
+ /**
1730
+ * Parse date from YAML structure
1731
+ * Handles: issued: {date-parts: [[1999]]} or year: 1999
1732
+ * Also handles multi-line format:
1733
+ * issued:
1734
+ * date-parts:
1735
+ * - - 1999
1736
+ */
1737
+ static char *parse_yaml_date(const char *content, int base_indent) {
1738
+ if (!content) return NULL;
1739
+
1740
+ const char *p = content;
1741
+ bool in_date_parts = false;
1742
+ int list_depth = 0;
1743
+
1744
+ /* Look for date-parts: */
1745
+ while (*p) {
1746
+ if (strncmp(p, "date-parts:", 11) == 0) {
1747
+ in_date_parts = true;
1748
+ p += 11;
1749
+ while (*p && isspace(*p) && *p != '\n') p++;
1750
+ if (*p == '\n') {
1751
+ p++;
1752
+ /* Multi-line format, continue on next line */
1753
+ }
1754
+ } else if (in_date_parts) {
1755
+ if (*p == '[') {
1756
+ list_depth++;
1757
+ p++;
1758
+ } else if (*p == ']') {
1759
+ list_depth--;
1760
+ p++;
1761
+ if (list_depth == 0) break;
1762
+ } else if (*p == '-' && isspace(p[1]) && list_depth >= 1) {
1763
+ /* List item in date-parts */
1764
+ p++;
1765
+ while (*p && isspace(*p)) p++;
1766
+ if (*p == '-') {
1767
+ /* Nested list item (year) */
1768
+ p++;
1769
+ while (*p && isspace(*p)) p++;
1770
+ const char *year_start = p;
1771
+ while (*p && isdigit(*p)) p++;
1772
+ size_t year_len = p - year_start;
1773
+ if (year_len > 0) {
1774
+ char *year = malloc(year_len + 1);
1775
+ if (year) {
1776
+ memcpy(year, year_start, year_len);
1777
+ year[year_len] = '\0';
1778
+ return year;
1779
+ }
1780
+ }
1781
+ }
1782
+ } else if (*p == '\n') {
1783
+ p++;
1784
+ int next_indent = get_indent_level(p);
1785
+ if (next_indent <= base_indent && list_depth == 0) {
1786
+ break; /* End of date block */
1787
+ }
1788
+ } else {
1789
+ p++;
1790
+ }
1791
+ } else if (strncmp(p, "year:", 5) == 0) {
1792
+ /* Simple year field */
1793
+ p += 5;
1794
+ while (*p && isspace(*p)) p++;
1795
+ const char *year_start = p;
1796
+ while (*p && isdigit(*p)) p++;
1797
+ size_t year_len = p - year_start;
1798
+ if (year_len > 0) {
1799
+ char *year = malloc(year_len + 1);
1800
+ if (year) {
1801
+ memcpy(year, year_start, year_len);
1802
+ year[year_len] = '\0';
1803
+ return year;
1804
+ }
1805
+ }
1806
+ break;
1807
+ } else {
1808
+ p++;
1809
+ }
1810
+ }
1811
+
1812
+ return NULL;
1813
+ }
1814
+
1815
+ /**
1816
+ * Parse CSL YAML file
1817
+ */
1818
+ apex_bibliography_registry *apex_parse_csl_yaml(const char *content) {
1819
+ if (!content) return NULL;
1820
+
1821
+ apex_bibliography_registry *registry = malloc(sizeof(apex_bibliography_registry));
1822
+ if (!registry) return NULL;
1823
+ registry->entries = NULL;
1824
+ registry->count = 0;
1825
+
1826
+ const char *p = content;
1827
+ apex_bibliography_entry *current_entry = NULL;
1828
+ bool in_entry = false;
1829
+
1830
+ /* Parse line by line */
1831
+ while (*p) {
1832
+ const char *line_start = p;
1833
+ const char *line_end = strchr(p, '\n');
1834
+ if (!line_end) line_end = p + strlen(p);
1835
+
1836
+ size_t line_len = line_end - line_start;
1837
+ char line[1024];
1838
+ if (line_len >= sizeof(line)) line_len = sizeof(line) - 1;
1839
+ memcpy(line, line_start, line_len);
1840
+ line[line_len] = '\0';
1841
+
1842
+ char *trimmed = trim_string(line);
1843
+ if (!trimmed || *trimmed == '\0' || *trimmed == '#') {
1844
+ p = line_end + 1;
1845
+ continue;
1846
+ }
1847
+
1848
+ int indent = get_indent_level(line);
1849
+
1850
+ /* Check for list item start (new entry) */
1851
+ if (trimmed[0] == '-' && indent == 0) {
1852
+ /* Start new entry */
1853
+ if (current_entry && current_entry->id) {
1854
+ /* Add previous entry to registry */
1855
+ current_entry->next = registry->entries;
1856
+ registry->entries = current_entry;
1857
+ registry->count++;
1858
+ }
1859
+ current_entry = NULL;
1860
+ in_entry = true;
1861
+ p = line_end + 1;
1862
+ continue;
1863
+ }
1864
+
1865
+ if (!in_entry) {
1866
+ p = line_end + 1;
1867
+ continue;
1868
+ }
1869
+
1870
+ /* Parse key: value pairs */
1871
+ char *colon = strchr(trimmed, ':');
1872
+ if (colon) {
1873
+ *colon = '\0';
1874
+ char *key = trim_string(trimmed);
1875
+ char *value_str = trim_string(colon + 1);
1876
+
1877
+ if (!current_entry && strcmp(key, "id") == 0) {
1878
+ /* Create new entry with ID */
1879
+ char *id_value = extract_yaml_value(value_str);
1880
+ if (id_value) {
1881
+ current_entry = bibliography_entry_new(id_value);
1882
+ free(id_value);
1883
+ }
1884
+ } else if (current_entry) {
1885
+ /* Set entry fields */
1886
+ if (strcmp(key, "type") == 0) {
1887
+ char *type_value = extract_yaml_value(value_str);
1888
+ if (type_value) {
1889
+ current_entry->type = type_value;
1890
+ }
1891
+ } else if (strcmp(key, "title") == 0) {
1892
+ char *title_value = extract_yaml_value(value_str);
1893
+ if (title_value) {
1894
+ current_entry->title = title_value;
1895
+ }
1896
+ } else if (strcmp(key, "author") == 0) {
1897
+ /* Parse author structure - may be multi-line */
1898
+ const char *author_start = value_str;
1899
+ if (*author_start == '\0' && line_end[1]) {
1900
+ /* Multi-line author, look at next line */
1901
+ const char *next_line = line_end + 1;
1902
+ if (*next_line == '-' || (*next_line == ' ' && next_line[1] == ' ')) {
1903
+ author_start = next_line;
1904
+ }
1905
+ }
1906
+ char *author = parse_yaml_author(author_start, indent);
1907
+ if (author) {
1908
+ current_entry->author = author;
1909
+ }
1910
+ } else if (strcmp(key, "issued") == 0 || strcmp(key, "year") == 0) {
1911
+ /* Parse date - may be multi-line */
1912
+ const char *date_start = value_str;
1913
+ if (*date_start == '\0' && line_end[1]) {
1914
+ const char *next_line = line_end + 1;
1915
+ if (*next_line == ' ' || *next_line == '-') {
1916
+ date_start = next_line;
1917
+ }
1918
+ }
1919
+ char *year = parse_yaml_date(date_start, indent);
1920
+ if (year) {
1921
+ current_entry->year = year;
1922
+ }
1923
+ } else if (strcmp(key, "container-title") == 0) {
1924
+ char *container_value = extract_yaml_value(value_str);
1925
+ if (container_value) {
1926
+ current_entry->container_title = container_value;
1927
+ }
1928
+ } else if (strcmp(key, "publisher") == 0) {
1929
+ char *publisher_value = extract_yaml_value(value_str);
1930
+ if (publisher_value) {
1931
+ current_entry->publisher = publisher_value;
1932
+ }
1933
+ } else if (strcmp(key, "volume") == 0) {
1934
+ char *volume_value = extract_yaml_value(value_str);
1935
+ if (volume_value) {
1936
+ current_entry->volume = volume_value;
1937
+ }
1938
+ } else if (strcmp(key, "page") == 0) {
1939
+ char *page_value = extract_yaml_value(value_str);
1940
+ if (page_value) {
1941
+ current_entry->page = page_value;
1942
+ }
1943
+ }
1944
+ }
1945
+ }
1946
+
1947
+ p = line_end + 1;
1948
+ }
1949
+
1950
+ /* Add last entry if exists */
1951
+ if (current_entry && current_entry->id) {
1952
+ current_entry->next = registry->entries;
1953
+ registry->entries = current_entry;
1954
+ registry->count++;
1955
+ }
1956
+
1957
+ return registry;
1958
+ }
1959
+
1960
+ /**
1961
+ * Load bibliography from a single file
1962
+ */
1963
+ apex_bibliography_registry *apex_load_bibliography_file(const char *filepath) {
1964
+ if (!filepath) return NULL;
1965
+
1966
+ char *content = read_bibliography_file(filepath);
1967
+ if (!content) return NULL;
1968
+
1969
+ bibliography_format_t format = detect_bibliography_format(filepath);
1970
+ apex_bibliography_registry *registry = NULL;
1971
+
1972
+ switch (format) {
1973
+ case BIB_FORMAT_BIBTEX:
1974
+ registry = apex_parse_bibtex(content);
1975
+ break;
1976
+ case BIB_FORMAT_CSL_JSON:
1977
+ registry = apex_parse_csl_json(content);
1978
+ break;
1979
+ case BIB_FORMAT_CSL_YAML:
1980
+ registry = apex_parse_csl_yaml(content);
1981
+ break;
1982
+ default:
1983
+ /* Try to auto-detect from content */
1984
+ if (strstr(content, "@") && strstr(content, "{")) {
1985
+ registry = apex_parse_bibtex(content);
1986
+ } else if (strstr(content, "[") && strstr(content, "\"id\"")) {
1987
+ registry = apex_parse_csl_json(content);
1988
+ }
1989
+ break;
1990
+ }
1991
+
1992
+ free(content);
1993
+ return registry;
1994
+ }
1995
+
1996
+ /**
1997
+ * Load bibliography from multiple files
1998
+ */
1999
+ apex_bibliography_registry *apex_load_bibliography(const char **files, const char *base_directory) {
2000
+ if (!files) return NULL;
2001
+
2002
+ apex_bibliography_registry *merged_registry = malloc(sizeof(apex_bibliography_registry));
2003
+ if (!merged_registry) return NULL;
2004
+ merged_registry->entries = NULL;
2005
+ merged_registry->count = 0;
2006
+
2007
+ /* Load each file and merge entries */
2008
+ for (int i = 0; files[i] != NULL; i++) {
2009
+ char *resolved_path = resolve_bibliography_path(files[i], base_directory);
2010
+ if (!resolved_path) continue;
2011
+
2012
+ apex_bibliography_registry *file_registry = apex_load_bibliography_file(resolved_path);
2013
+ free(resolved_path);
2014
+
2015
+ if (file_registry) {
2016
+ /* Merge entries into merged_registry */
2017
+ apex_bibliography_entry *entry = file_registry->entries;
2018
+ while (entry) {
2019
+ apex_bibliography_entry *next = entry->next;
2020
+
2021
+ /* Check if entry with same ID already exists */
2022
+ apex_bibliography_entry *existing = apex_find_bibliography_entry(merged_registry, entry->id);
2023
+ if (!existing) {
2024
+ /* Add to merged registry */
2025
+ entry->next = merged_registry->entries;
2026
+ merged_registry->entries = entry;
2027
+ merged_registry->count++;
2028
+ } else {
2029
+ /* Entry already exists, skip (or could update) */
2030
+ apex_bibliography_entry_free(entry);
2031
+ }
2032
+
2033
+ entry = next;
2034
+ }
2035
+
2036
+ /* Free the file registry structure (entries were moved) */
2037
+ free(file_registry);
2038
+ }
2039
+ }
2040
+
2041
+ return merged_registry;
2042
+ }