apex-ruby 1.0.17 → 1.0.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +17 -3
- data/ext/apex_ext/apex_ext.c +260 -91
- data/ext/apex_ext/apex_src/CHANGELOG.md +440 -0
- data/ext/apex_ext/apex_src/CMakeLists.txt +15 -1
- data/ext/apex_ext/apex_src/Formula/apex.rb +7 -2
- data/ext/apex_ext/apex_src/Package.swift +15 -4
- data/ext/apex_ext/apex_src/README.md +94 -35
- data/ext/apex_ext/apex_src/VERSION +1 -1
- data/ext/apex_ext/apex_src/apex-debug.log +4 -0
- data/ext/apex_ext/apex_src/apex.code-workspace +38 -0
- data/ext/apex_ext/apex_src/apex.taskpaper +12 -0
- data/ext/apex_ext/apex_src/apex_deflist_debug.log +25 -0
- data/ext/apex_ext/apex_src/buildnotes.md +1435 -0
- data/ext/apex_ext/apex_src/cli/main.c +262 -33
- data/ext/apex_ext/apex_src/debug.log +7 -0
- data/ext/apex_ext/apex_src/docs/CITATIONS_EXAMPLES.md +274 -0
- data/ext/apex_ext/apex_src/docs/CITATIONS_IMPLEMENTATION_PLAN.md +749 -0
- data/ext/apex_ext/apex_src/docs/CITATIONS_PLAN.md +744 -0
- data/ext/apex_ext/apex_src/docs/INDEX_IMPLEMENTATION_ANALYSIS.md +382 -0
- data/ext/apex_ext/apex_src/docs/plans/2025-02-27-man-page-output-implementation.md +351 -0
- data/ext/apex_ext/apex_src/docs/superpowers/plans/2026-07-12-toc-output-format.md +745 -0
- data/ext/apex_ext/apex_src/docs/superpowers/plans/2026-07-18-bear-image-attributes.md +829 -0
- data/ext/apex_ext/apex_src/docs/superpowers/plans/2026-07-19-apex-node-npm.md +1148 -0
- data/ext/apex_ext/apex_src/docs/superpowers/specs/2026-07-12-toc-output-format-design.md +161 -0
- data/ext/apex_ext/apex_src/docs/superpowers/specs/2026-07-18-bear-image-attributes-design.md +184 -0
- data/ext/apex_ext/apex_src/docs/superpowers/specs/2026-07-19-apex-node-npm-design.md +211 -0
- data/ext/apex_ext/apex_src/documentation/app-settings-table.md +67 -0
- data/ext/apex_ext/apex_src/documentation/app-transformed/Citations.md +340 -0
- data/ext/apex_ext/apex_src/documentation/app-transformed/Credits.md +11 -0
- data/ext/apex_ext/apex_src/documentation/app-transformed/Header-IDs.md +234 -0
- data/ext/apex_ext/apex_src/documentation/app-transformed/Inline-Attribute-Lists.md +516 -0
- data/ext/apex_ext/apex_src/documentation/app-transformed/Metadata-Transforms.md +461 -0
- data/ext/apex_ext/apex_src/documentation/app-transformed/Modes.md +193 -0
- data/ext/apex_ext/apex_src/documentation/app-transformed/Multi-File-Documents.md +201 -0
- data/ext/apex_ext/apex_src/documentation/app-transformed/Plugins.md +678 -0
- data/ext/apex_ext/apex_src/documentation/app-transformed/Syntax.md +1367 -0
- data/ext/apex_ext/apex_src/documentation/docsets/Apex Command Line Options.cheatsheet.txt +6 -0
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/C-API.html +774 -300
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Callouts.html +1194 -0
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Citations.html +416 -216
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Command-Line-Options.html +3544 -1094
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Configuration.html +371 -97
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Credits.html +153 -23
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Examples.html +358 -124
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Filters.html +1656 -0
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Generating-Man-Pages.html +1226 -0
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Getting-Started.html +201 -56
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Header-IDs.html +279 -109
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Home.html +200 -53
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Indices.html +2327 -0
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Inline-Attribute-Lists.html +690 -271
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Installation.html +201 -55
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Limitations-and-Roadmap.html +238 -68
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Metadata-Transforms.html +421 -221
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Modes.html +340 -78
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Multi-File-Documents.html +251 -86
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Multi-Format-Images.html +1362 -0
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Pandoc-Integration.html +277 -90
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Plugins.html +673 -346
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Quarto-Mode.html +1405 -0
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Rendering-Markdown-In-Terminal.html +1902 -0
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Syntax.html +1939 -1698
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Tables.html +1781 -0
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Troubleshooting.html +315 -140
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Usage.html +307 -113
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Using-Apex-with-Jekyll.html +1132 -0
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Writing-Tests.html +1754 -0
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Xcode-Integration.html +872 -376
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/docSet.dsidx +0 -0
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/optimizedIndex.dsidx +0 -0
- data/ext/apex_ext/apex_src/documentation/html/apex-app-docs.html +8746 -0
- data/ext/apex_ext/apex_src/documentation/shared_styles.css +20 -15
- data/ext/apex_ext/apex_src/documentation/transform_for_app.md +55 -0
- data/ext/apex_ext/apex_src/escaping-repro.md +103 -0
- data/ext/apex_ext/apex_src/examples/quick-actions/Markdown to RTF.workflow/Contents/Info.plist +8 -0
- data/ext/apex_ext/apex_src/examples/quick-actions/Markdown to RTF.workflow/Contents/QuickLook/Preview.png +0 -0
- data/ext/apex_ext/apex_src/examples/quick-actions/Markdown to RTF.workflow/Contents/document.wflow +547 -0
- data/ext/apex_ext/apex_src/examples/quick-actions/Markdown-to-RTF.workflow.zip +0 -0
- data/ext/apex_ext/apex_src/examples/quick-actions/README.md +33 -0
- data/ext/apex_ext/apex_src/include/apex/apex.h +92 -6
- data/ext/apex_ext/apex_src/include/apex/ast_rtf.h +23 -0
- data/ext/apex_ext/apex_src/include/apex/ast_terminal.h +6 -0
- data/ext/apex_ext/apex_src/include/apex/module.modulemap +5 -0
- data/ext/apex_ext/apex_src/include/apex/plugins.h +105 -0
- data/ext/apex_ext/apex_src/include/apexc.h +1 -0
- data/ext/apex_ext/apex_src/man/apex.1 +82 -78
- data/ext/apex_ext/apex_src/man/apex.1.md +81 -4
- data/ext/apex_ext/apex_src/objc/Apex.swift +73 -0
- data/ext/apex_ext/apex_src/objc/ApexPlugins.swift +79 -0
- data/ext/apex_ext/apex_src/objc/NSString+Apex.h +83 -5
- data/ext/apex_ext/apex_src/objc/NSString+Apex.m +244 -1
- data/ext/apex_ext/apex_src/objc/NSString+ApexPlugins.h +76 -0
- data/ext/apex_ext/apex_src/objc/NSString+ApexPlugins.m +169 -0
- data/ext/apex_ext/apex_src/output.html +87 -0
- data/ext/apex_ext/apex_src/plans/quarto-mode.md +442 -0
- data/ext/apex_ext/apex_src/src/_README.md +85 -32
- data/ext/apex_ext/apex_src/src/apex.c +770 -200
- data/ext/apex_ext/apex_src/src/ast_rtf.c +848 -0
- data/ext/apex_ext/apex_src/src/ast_terminal.c +184 -7
- data/ext/apex_ext/apex_src/src/extensions/abbreviations.c +10 -3
- data/ext/apex_ext/apex_src/src/extensions/advanced_tables.c +7 -11
- data/ext/apex_ext/apex_src/src/extensions/bear_image_attrs.c +467 -0
- data/ext/apex_ext/apex_src/src/extensions/bear_image_attrs.h +27 -0
- data/ext/apex_ext/apex_src/src/extensions/callouts.c +400 -4
- data/ext/apex_ext/apex_src/src/extensions/callouts.h +15 -5
- data/ext/apex_ext/apex_src/src/extensions/citations.c +26 -5
- data/ext/apex_ext/apex_src/src/extensions/code_fence_attrs.c +632 -0
- data/ext/apex_ext/apex_src/src/extensions/code_fence_attrs.h +22 -0
- data/ext/apex_ext/apex_src/src/extensions/concordance.c +632 -0
- data/ext/apex_ext/apex_src/src/extensions/definition_list.c +129 -15
- data/ext/apex_ext/apex_src/src/extensions/grid_tables.c +1050 -121
- data/ext/apex_ext/apex_src/src/extensions/html_markdown.c +569 -16
- data/ext/apex_ext/apex_src/src/extensions/ial.c +502 -52
- data/ext/apex_ext/apex_src/src/extensions/ial.h +2 -1
- data/ext/apex_ext/apex_src/src/extensions/includes.c +532 -33
- data/ext/apex_ext/apex_src/src/extensions/includes.h +7 -1
- data/ext/apex_ext/apex_src/src/extensions/index.c +1427 -140
- data/ext/apex_ext/apex_src/src/extensions/index.h +28 -0
- data/ext/apex_ext/apex_src/src/extensions/metadata.c +200 -161
- data/ext/apex_ext/apex_src/src/extensions/quarto_diagrams.c +251 -0
- data/ext/apex_ext/apex_src/src/extensions/quarto_diagrams.h +22 -0
- data/ext/apex_ext/apex_src/src/extensions/quarto_lists.c +624 -0
- data/ext/apex_ext/apex_src/src/extensions/quarto_lists.h +35 -0
- data/ext/apex_ext/apex_src/src/extensions/quarto_polish.c +264 -0
- data/ext/apex_ext/apex_src/src/extensions/quarto_polish.h +23 -0
- data/ext/apex_ext/apex_src/src/extensions/quarto_shortcodes.c +307 -0
- data/ext/apex_ext/apex_src/src/extensions/quarto_shortcodes.h +21 -0
- data/ext/apex_ext/apex_src/src/extensions/raw_content.c +351 -0
- data/ext/apex_ext/apex_src/src/extensions/raw_content.h +20 -0
- data/ext/apex_ext/apex_src/src/extensions/relaxed_tables.c +117 -3
- data/ext/apex_ext/apex_src/src/extensions/special_markers.c +53 -0
- data/ext/apex_ext/apex_src/src/extensions/syntax_highlight.c +23 -1
- data/ext/apex_ext/apex_src/src/extensions/table_html_postprocess.c +83 -92
- data/ext/apex_ext/apex_src/src/extensions/toc.c +446 -25
- data/ext/apex_ext/apex_src/src/extensions/toc.h +26 -1
- data/ext/apex_ext/apex_src/src/extensions/wiki_links.c +28 -0
- data/ext/apex_ext/apex_src/src/html_renderer.c +387 -17
- data/ext/apex_ext/apex_src/src/plugin_catalog.c +517 -0
- data/ext/apex_ext/apex_src/src/plugins_remote.c +88 -14
- data/ext/apex_ext/apex_src/src/plugins_remote.h +34 -0
- data/ext/apex_ext/apex_src/src/pretty_html.c +6 -1
- data/ext/apex_ext/apex_src/tests/fixtures/comprehensive_test.md +21 -0
- data/ext/apex_ext/apex_src/tests/fixtures/images/bear_image_attributes.md +10 -0
- data/ext/apex_ext/apex_src/tests/fixtures/includes/sections.md +19 -0
- data/ext/apex_ext/apex_src/tests/fixtures/includes/sections.txt +11 -0
- data/ext/apex_ext/apex_src/tests/fixtures/minimal_pb.md +2 -0
- data/ext/apex_ext/apex_src/tests/fixtures/obsidian-callouts.md +46 -0
- data/ext/apex_ext/apex_src/tests/fixtures/pagebreak_html_repro.md +5 -0
- data/ext/apex_ext/apex_src/tests/fixtures/python-callouts.md +47 -0
- data/ext/apex_ext/apex_src/tests/fixtures/quarto/callouts.md +3 -0
- data/ext/apex_ext/apex_src/tests/fixtures/quarto/code-attrs.md +3 -0
- data/ext/apex_ext/apex_src/tests/fixtures/quarto/diagrams.md +8 -0
- data/ext/apex_ext/apex_src/tests/fixtures/quarto/lists.md +19 -0
- data/ext/apex_ext/apex_src/tests/fixtures/quarto/raw-content.md +5 -0
- data/ext/apex_ext/apex_src/tests/fixtures/quarto/shortcodes.md +7 -0
- data/ext/apex_ext/apex_src/tests/fixtures/quarto/smoke.md +81 -0
- data/ext/apex_ext/apex_src/tests/fixtures/quarto/spans.md +5 -0
- data/ext/apex_ext/apex_src/tests/fixtures/quarto-callouts.md +47 -0
- data/ext/apex_ext/apex_src/tests/fixtures/speed.md +192 -0
- data/ext/apex_ext/apex_src/tests/fixtures/tables/grid-tables.md +37 -0
- data/ext/apex_ext/apex_src/tests/paginate_cli_test.sh +16 -0
- data/ext/apex_ext/apex_src/tests/test_bear_image_attrs.c +301 -0
- data/ext/apex_ext/apex_src/tests/test_concordance.tsv +4 -0
- data/ext/apex_ext/apex_src/tests/test_escaping_repro.c +82 -0
- data/ext/apex_ext/apex_src/tests/test_extensions.c +821 -1
- data/ext/apex_ext/apex_src/tests/test_ial.c +7 -0
- data/ext/apex_ext/apex_src/tests/test_marked_integration.c +25 -0
- data/ext/apex_ext/apex_src/tests/test_metadata.c +129 -0
- data/ext/apex_ext/apex_src/tests/test_output.c +568 -2
- data/ext/apex_ext/apex_src/tests/test_runner.c +11 -0
- data/ext/apex_ext/apex_src/tests/test_tables.c +420 -0
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/CMakeLists.txt +1 -1
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/extensions/autolink.c +77 -1
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/extensions/table.c +135 -5
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/src/blocks.c +56 -1
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/src/config.h +38 -0
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/src/main.c +1 -1
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/test/__pycache__/cmark.cpython-314.pyc +0 -0
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/test/__pycache__/normalize.cpython-314.pyc +0 -0
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/test/extensions.txt +69 -0
- data/ext/apex_ext/apex_src/xcode_build/CMakeScripts/ReRunCMake.make +74 -0
- data/ext/apex_ext/apex_src/xcode_build/CTestTestfile.cmake +22 -0
- data/ext/apex_ext/apex_src/xcode_build/Debug/Apex.framework/Versions/A/Apex +0 -0
- data/ext/apex_ext/apex_src/xcode_build/Debug/Apex.framework/Versions/A/Resources/Info.plist +44 -0
- data/ext/apex_ext/apex_src/xcode_build/Debug/Apex.framework/Versions/A/_CodeSignature/CodeResources +128 -0
- data/ext/apex_ext/apex_src/xcode_build/apex.xcodeproj/project.pbxproj +5118 -0
- data/ext/apex_ext/apex_src/xcode_build/apex.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +8 -0
- data/ext/apex_ext/apex_src/xcode_build/apex.xcodeproj/project.xcworkspace/xcuserdata/ttscoff.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- data/ext/apex_ext/apex_src/xcode_build/apex.xcodeproj/project.xcworkspace/xcuserdata/ttscoff.xcuserdatad/WorkspaceSettings.xcsettings +8 -0
- data/ext/apex_ext/apex_src/xcode_build/apex.xcodeproj/xcuserdata/ttscoff.xcuserdatad/xcschemes/xcschememanagement.plist +99 -0
- data/ext/apex_ext/apex_src/xcode_build/cmake_install.cmake +452 -0
- data/ext/apex_ext/apex_src/xcode_build/vendor/cmark-gfm/CMakeScripts/ReRunCMake.make +52 -0
- data/ext/apex_ext/apex_src/xcode_build/vendor/cmark-gfm/cmake_install.cmake +55 -0
- data/ext/apex_ext/apex_src/xcode_build/vendor/cmark-gfm/cmark-gfm.xcodeproj/project.pbxproj +2455 -0
- data/ext/apex_ext/apex_src/xcode_build/vendor/cmark-gfm/cmark-gfm.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +8 -0
- data/ext/apex_ext/apex_src/xcode_build/vendor/cmark-gfm/extensions/cmake_install.cmake +171 -0
- data/ext/apex_ext/apex_src/xcode_build/vendor/cmark-gfm/man/cmake_install.cmake +55 -0
- data/ext/apex_ext/apex_src/xcode_build/vendor/cmark-gfm/src/cmake_install.cmake +220 -0
- data/ext/apex_ext/apex_src/xcode_build/vendor/cmark-gfm/src/cmark-gfm_export.h +43 -0
- data/ext/apex_ext/apex_src/xcode_build/vendor/cmark-gfm/src/cmark-gfm_version.h +7 -0
- data/ext/apex_ext/apex_src/xcode_build/vendor/cmark-gfm/src/config.h +76 -0
- data/ext/apex_ext/apex_src/xcode_build/vendor/cmark-gfm/src/libcmark-gfm.pc +10 -0
- data/ext/apex_ext/extconf.rb +35 -0
- data/lib/apex/version.rb +2 -2
- metadata +117 -2
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/tempOptimizedIndex.dsidx +0 -0
|
@@ -16,18 +16,24 @@
|
|
|
16
16
|
#define MAX_COLUMNS 64
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
|
-
* Check if a line starts
|
|
20
|
-
*
|
|
19
|
+
* Check if a line starts a grid table block (separator row: +---+ or +===+).
|
|
20
|
+
* Bare lines starting with '+' alone are not grid tables.
|
|
21
21
|
*/
|
|
22
22
|
static bool is_grid_table_start(const char *line) {
|
|
23
23
|
if (!line) return false;
|
|
24
24
|
|
|
25
|
-
/* Skip whitespace */
|
|
26
25
|
while (*line == ' ' || *line == '\t') {
|
|
27
26
|
line++;
|
|
28
27
|
}
|
|
29
28
|
|
|
30
|
-
|
|
29
|
+
if (*line != '+') return false;
|
|
30
|
+
line++;
|
|
31
|
+
|
|
32
|
+
while (*line == ' ' || *line == '\t') {
|
|
33
|
+
line++;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return *line == '-' || *line == '=';
|
|
31
37
|
}
|
|
32
38
|
|
|
33
39
|
/**
|
|
@@ -97,6 +103,24 @@ static bool is_grid_table_separator(const char *line) {
|
|
|
97
103
|
return has_dash_or_equal;
|
|
98
104
|
}
|
|
99
105
|
|
|
106
|
+
static bool grid_block_has_separator(char **lines, size_t line_count) {
|
|
107
|
+
for (size_t i = 0; i < line_count; i++) {
|
|
108
|
+
if (lines[i] && is_grid_table_separator(lines[i])) {
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
static bool grid_block_has_content_rows(char **lines, size_t line_count) {
|
|
116
|
+
for (size_t i = 0; i < line_count; i++) {
|
|
117
|
+
if (lines[i] && strchr(lines[i], '|') && !is_grid_table_separator(lines[i])) {
|
|
118
|
+
return true;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
|
|
100
124
|
/**
|
|
101
125
|
* Check if a line is a header separator (contains = characters)
|
|
102
126
|
*/
|
|
@@ -395,6 +419,28 @@ static bool has_same_column_structure(const char *line1, const char *line2) {
|
|
|
395
419
|
return pipe_count1 > 0 && pipe_count1 == pipe_count2;
|
|
396
420
|
}
|
|
397
421
|
|
|
422
|
+
static char *str_append(char *dest, const char *add);
|
|
423
|
+
|
|
424
|
+
/**
|
|
425
|
+
* Append a line to a string with newline separator
|
|
426
|
+
*/
|
|
427
|
+
static char *str_append_line(char *dest, const char *line) {
|
|
428
|
+
if (!line) return dest;
|
|
429
|
+
if (!dest) return str_append(NULL, line);
|
|
430
|
+
dest = str_append(dest, "\n");
|
|
431
|
+
return str_append(dest, line);
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
static char *str_append(char *dest, const char *add) {
|
|
435
|
+
if (!add) return dest;
|
|
436
|
+
size_t old_len = dest ? strlen(dest) : 0;
|
|
437
|
+
size_t add_len = strlen(add);
|
|
438
|
+
char *new_str = realloc(dest, old_len + add_len + 1);
|
|
439
|
+
if (!new_str) return dest;
|
|
440
|
+
memcpy(new_str + old_len, add, add_len + 1);
|
|
441
|
+
return new_str;
|
|
442
|
+
}
|
|
443
|
+
|
|
398
444
|
/**
|
|
399
445
|
* Extract cell content from grid table row lines
|
|
400
446
|
* Grid table rows can span multiple lines until the next separator
|
|
@@ -415,6 +461,13 @@ static char **extract_cells_from_row_lines(char **row_lines, size_t row_line_cou
|
|
|
415
461
|
|
|
416
462
|
/* Skip separator lines - they shouldn't be in row_lines, but double-check */
|
|
417
463
|
if (is_grid_table_separator(line)) {
|
|
464
|
+
/* Nested grid separator inside colspan cell: preserve as literal content */
|
|
465
|
+
if (*cell_count > 0) {
|
|
466
|
+
size_t idx = (*cell_count > 0) ? 0 : 0;
|
|
467
|
+
if (cells[idx]) {
|
|
468
|
+
cells[idx] = str_append_line(cells[idx], line);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
418
471
|
continue;
|
|
419
472
|
}
|
|
420
473
|
|
|
@@ -582,12 +635,491 @@ static char *create_pipe_separator(int *alignments, size_t column_count) {
|
|
|
582
635
|
return sep;
|
|
583
636
|
}
|
|
584
637
|
|
|
638
|
+
static bool is_nested_grid_separator(const char *line, size_t table_cols) {
|
|
639
|
+
if (!line || !is_grid_table_separator(line)) return false;
|
|
640
|
+
if (is_header_separator(line)) return false;
|
|
641
|
+
size_t sep_cols = 0;
|
|
642
|
+
int tmp[MAX_COLUMNS] = {0};
|
|
643
|
+
parse_alignment(line, tmp, &sep_cols);
|
|
644
|
+
return sep_cols > table_cols;
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
/**
|
|
648
|
+
* Check if table contains partial separators (|...+...+...) that require HTML output.
|
|
649
|
+
*/
|
|
650
|
+
static bool table_has_nested_grid(char **lines, size_t line_count, size_t table_cols) {
|
|
651
|
+
if (table_cols == 0) return false;
|
|
652
|
+
for (size_t i = 0; i < line_count; i++) {
|
|
653
|
+
if (lines[i] && is_nested_grid_separator(lines[i], table_cols)) return true;
|
|
654
|
+
}
|
|
655
|
+
return false;
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
static bool table_has_partial_separators(char **lines, size_t line_count) {
|
|
659
|
+
for (size_t i = 0; i < line_count; i++) {
|
|
660
|
+
if (!lines[i]) continue;
|
|
661
|
+
const char *p = lines[i];
|
|
662
|
+
while (*p == ' ' || *p == '\t') p++;
|
|
663
|
+
if (*p != '+' && *p != '|') continue;
|
|
664
|
+
if (is_grid_table_separator(lines[i])) continue;
|
|
665
|
+
/* Mixed row: starts with | and contains internal +---+ pattern */
|
|
666
|
+
if (*p == '|') {
|
|
667
|
+
bool seen_pipe = false;
|
|
668
|
+
for (const char *q = p; *q && *q != '\n'; q++) {
|
|
669
|
+
if (*q == '|') seen_pipe = true;
|
|
670
|
+
if (seen_pipe && *q == '+' && q[1] == '-') return true;
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
return false;
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
static bool table_has_multiline_cells(char **lines, size_t line_count) {
|
|
678
|
+
for (size_t i = 0; i + 1 < line_count; i++) {
|
|
679
|
+
if (!lines[i] || !lines[i + 1]) continue;
|
|
680
|
+
if (is_grid_table_separator(lines[i]) || is_grid_table_separator(lines[i + 1])) continue;
|
|
681
|
+
if (strchr(lines[i], '|') && strchr(lines[i + 1], '|') &&
|
|
682
|
+
has_same_column_structure(lines[i], lines[i + 1])) {
|
|
683
|
+
return true;
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
return false;
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
static bool is_full_row_colspan_line(const char *line) {
|
|
690
|
+
return line && count_cells_in_line(line) == 1;
|
|
691
|
+
}
|
|
692
|
+
|
|
585
693
|
/**
|
|
586
|
-
*
|
|
587
|
-
* Groups lines between separators into rows and handles multi-line cells
|
|
694
|
+
* Parse | c0 | c1 | c2 | ... into cell strings (trimmed). Returns cell count.
|
|
588
695
|
*/
|
|
696
|
+
static size_t parse_grid_row_cells(const char *line, char **cells, size_t max_cells) {
|
|
697
|
+
if (!line || !cells || max_cells == 0) return 0;
|
|
698
|
+
|
|
699
|
+
const char *p = line;
|
|
700
|
+
while (*p == ' ' || *p == '\t') p++;
|
|
701
|
+
if (*p == '+') return 0;
|
|
702
|
+
|
|
703
|
+
size_t count = 0;
|
|
704
|
+
const char *cell_start = NULL;
|
|
705
|
+
bool first_pipe = true;
|
|
706
|
+
|
|
707
|
+
while (*p && count < max_cells) {
|
|
708
|
+
if (*p == '|') {
|
|
709
|
+
if (cell_start) {
|
|
710
|
+
const char *cell_end = p;
|
|
711
|
+
while (cell_end > cell_start && isspace((unsigned char)cell_end[-1])) cell_end--;
|
|
712
|
+
size_t len = (size_t)(cell_end - cell_start);
|
|
713
|
+
cells[count] = malloc(len + 1);
|
|
714
|
+
if (cells[count]) {
|
|
715
|
+
memcpy(cells[count], cell_start, len);
|
|
716
|
+
cells[count][len] = '\0';
|
|
717
|
+
}
|
|
718
|
+
count++;
|
|
719
|
+
} else if (!first_pipe) {
|
|
720
|
+
cells[count] = strdup("");
|
|
721
|
+
if (cells[count]) count++;
|
|
722
|
+
}
|
|
723
|
+
first_pipe = false;
|
|
724
|
+
cell_start = p + 1;
|
|
725
|
+
}
|
|
726
|
+
p++;
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
if (cell_start && count < max_cells) {
|
|
730
|
+
const char *cell_end = p;
|
|
731
|
+
while (cell_end > cell_start && isspace((unsigned char)cell_end[-1])) cell_end--;
|
|
732
|
+
if (cell_end > cell_start) {
|
|
733
|
+
size_t len = (size_t)(cell_end - cell_start);
|
|
734
|
+
cells[count] = malloc(len + 1);
|
|
735
|
+
if (cells[count]) {
|
|
736
|
+
memcpy(cells[count], cell_start, len);
|
|
737
|
+
cells[count][len] = '\0';
|
|
738
|
+
count++;
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
return count;
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
static bool line_is_partial_col0_separator(const char *line) {
|
|
747
|
+
if (!line) return false;
|
|
748
|
+
const char *p = line;
|
|
749
|
+
while (*p == ' ' || *p == '\t') p++;
|
|
750
|
+
if (*p != '|') return false;
|
|
751
|
+
p++;
|
|
752
|
+
while (*p == ' ' || *p == '\t') p++;
|
|
753
|
+
if (*p == '+' && p[1] == '-') return true;
|
|
754
|
+
return false;
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
static char *extract_partial_col0_text(const char *line) {
|
|
758
|
+
if (!line) return NULL;
|
|
759
|
+
const char *p = line;
|
|
760
|
+
while (*p == ' ' || *p == '\t') p++;
|
|
761
|
+
if (*p != '|') return NULL;
|
|
762
|
+
p++;
|
|
763
|
+
while (*p == ' ' || *p == '\t') p++;
|
|
764
|
+
|
|
765
|
+
const char *start = p;
|
|
766
|
+
const char *plus = NULL;
|
|
767
|
+
for (const char *q = p; *q && *q != '\n'; q++) {
|
|
768
|
+
if (*q == '+' && q[1] == '-') {
|
|
769
|
+
plus = q;
|
|
770
|
+
break;
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
if (!plus) return NULL;
|
|
774
|
+
|
|
775
|
+
const char *end = plus;
|
|
776
|
+
while (end > start && isspace((unsigned char)end[-1])) end--;
|
|
777
|
+
if (end <= start) return NULL;
|
|
778
|
+
|
|
779
|
+
size_t len = (size_t)(end - start);
|
|
780
|
+
char *out = malloc(len + 1);
|
|
781
|
+
if (!out) return NULL;
|
|
782
|
+
memcpy(out, start, len);
|
|
783
|
+
out[len] = '\0';
|
|
784
|
+
return out;
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
static char *append_col0_label(char *label, const char *add) {
|
|
788
|
+
if (!add || !*add) return label;
|
|
789
|
+
if (!label) return strdup(add);
|
|
790
|
+
size_t old = strlen(label);
|
|
791
|
+
size_t add_len = strlen(add);
|
|
792
|
+
char *out = realloc(label, old + add_len + 2);
|
|
793
|
+
if (!out) return label;
|
|
794
|
+
if (old > 0) {
|
|
795
|
+
out[old] = ' ';
|
|
796
|
+
out[old + 1] = '\0';
|
|
797
|
+
old++;
|
|
798
|
+
}
|
|
799
|
+
strcat(out, add);
|
|
800
|
+
return out;
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
static void write_html_cell(char **p, size_t *remaining, char **output, size_t *total_size,
|
|
804
|
+
const char *tag, const char *content, size_t colspan, size_t rowspan,
|
|
805
|
+
bool markdown) {
|
|
806
|
+
char open[256];
|
|
807
|
+
if (colspan > 1 && rowspan > 1) {
|
|
808
|
+
snprintf(open, sizeof(open), "<%s%s colspan=\"%zu\" rowspan=\"%zu\">\n\n",
|
|
809
|
+
tag, markdown ? " markdown=\"1\"" : "", colspan, rowspan);
|
|
810
|
+
} else if (colspan > 1) {
|
|
811
|
+
snprintf(open, sizeof(open), "<%s%s colspan=\"%zu\">\n\n",
|
|
812
|
+
tag, markdown ? " markdown=\"1\"" : "", colspan);
|
|
813
|
+
} else if (rowspan > 1) {
|
|
814
|
+
snprintf(open, sizeof(open), "<%s%s rowspan=\"%zu\">\n\n",
|
|
815
|
+
tag, markdown ? " markdown=\"1\"" : "", rowspan);
|
|
816
|
+
} else {
|
|
817
|
+
snprintf(open, sizeof(open), "<%s%s>\n\n", tag, markdown ? " markdown=\"1\"" : "");
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
size_t need = strlen(open) + (content ? strlen(content) : 0) + 32;
|
|
821
|
+
char *base = *output;
|
|
822
|
+
size_t used = (size_t)(*p - base);
|
|
823
|
+
if (*remaining < need) {
|
|
824
|
+
*total_size = (used + need + 1) * 2;
|
|
825
|
+
char *grown = realloc(base, *total_size);
|
|
826
|
+
if (!grown) return;
|
|
827
|
+
*output = grown;
|
|
828
|
+
*p = grown + used;
|
|
829
|
+
*remaining = *total_size - used;
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
size_t ol = strlen(open);
|
|
833
|
+
memcpy(*p, open, ol);
|
|
834
|
+
*p += ol;
|
|
835
|
+
*remaining -= ol;
|
|
836
|
+
|
|
837
|
+
if (content && *content) {
|
|
838
|
+
size_t cl = strlen(content);
|
|
839
|
+
memcpy(*p, content, cl);
|
|
840
|
+
*p += cl;
|
|
841
|
+
*remaining -= cl;
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
char close[16];
|
|
845
|
+
snprintf(close, sizeof(close), "\n\n</%s>\n", tag);
|
|
846
|
+
size_t cll = strlen(close);
|
|
847
|
+
memcpy(*p, close, cll);
|
|
848
|
+
*p += cll;
|
|
849
|
+
*remaining -= cll;
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
/**
|
|
853
|
+
* Convert grid tables with in-row partial separators (Pandoc-style).
|
|
854
|
+
*/
|
|
855
|
+
static char *convert_partial_grid_table_to_html(char **lines, size_t line_count,
|
|
856
|
+
size_t column_count) {
|
|
857
|
+
if (!lines || line_count == 0 || column_count == 0) return NULL;
|
|
858
|
+
|
|
859
|
+
size_t header_sep_idx = line_count;
|
|
860
|
+
for (size_t i = 0; i < line_count; i++) {
|
|
861
|
+
if (lines[i] && is_header_separator(lines[i])) {
|
|
862
|
+
header_sep_idx = i;
|
|
863
|
+
break;
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
size_t total_size = 4096;
|
|
868
|
+
char *output = malloc(total_size);
|
|
869
|
+
if (!output) return NULL;
|
|
870
|
+
char *p = output;
|
|
871
|
+
size_t remaining = total_size;
|
|
872
|
+
|
|
873
|
+
memcpy(p, "\n\n<table>\n", 10);
|
|
874
|
+
p += 10;
|
|
875
|
+
remaining -= 10;
|
|
876
|
+
|
|
877
|
+
/* Header rows before +===+ separator */
|
|
878
|
+
for (size_t i = 0; i < header_sep_idx; i++) {
|
|
879
|
+
if (!lines[i] || is_grid_table_separator(lines[i]) || !strchr(lines[i], '|')) continue;
|
|
880
|
+
|
|
881
|
+
char *cells[MAX_COLUMNS] = {0};
|
|
882
|
+
size_t ncells = parse_grid_row_cells(lines[i], cells, MAX_COLUMNS);
|
|
883
|
+
if (ncells == 0) continue;
|
|
884
|
+
|
|
885
|
+
const char *thead = "<thead>\n<tr>\n";
|
|
886
|
+
size_t tl = strlen(thead);
|
|
887
|
+
memcpy(p, thead, tl);
|
|
888
|
+
p += tl;
|
|
889
|
+
remaining -= tl;
|
|
890
|
+
|
|
891
|
+
size_t first_span = (ncells < column_count) ? (column_count - ncells + 1) : 1;
|
|
892
|
+
for (size_t c = 0; c < ncells; c++) {
|
|
893
|
+
size_t span = (ncells < column_count && c == 0) ? first_span : 1;
|
|
894
|
+
write_html_cell(&p, &remaining, &output, &total_size, "th", cells[c], span, 1, true);
|
|
895
|
+
free(cells[c]);
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
const char *end = "</tr>\n</thead>\n<tbody>\n";
|
|
899
|
+
size_t el = strlen(end);
|
|
900
|
+
memcpy(p, end, el);
|
|
901
|
+
p += el;
|
|
902
|
+
remaining -= el;
|
|
903
|
+
break;
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
if (header_sep_idx >= line_count) {
|
|
907
|
+
const char *tbody = "<tbody>\n";
|
|
908
|
+
size_t bl = strlen(tbody);
|
|
909
|
+
memcpy(p, tbody, bl);
|
|
910
|
+
p += bl;
|
|
911
|
+
remaining -= bl;
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
/* Body rows after header separator */
|
|
915
|
+
char *col0_label = NULL;
|
|
916
|
+
char **row_c1 = NULL;
|
|
917
|
+
char **row_c2 = NULL;
|
|
918
|
+
size_t row_count = 0;
|
|
919
|
+
size_t row_cap = 8;
|
|
920
|
+
|
|
921
|
+
row_c1 = malloc(row_cap * sizeof(char*));
|
|
922
|
+
row_c2 = malloc(row_cap * sizeof(char*));
|
|
923
|
+
if (!row_c1 || !row_c2) {
|
|
924
|
+
free(col0_label);
|
|
925
|
+
free(row_c1);
|
|
926
|
+
free(row_c2);
|
|
927
|
+
free(output);
|
|
928
|
+
return NULL;
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
for (size_t i = header_sep_idx + 1; i < line_count; i++) {
|
|
932
|
+
if (!lines[i] || is_grid_table_separator(lines[i])) continue;
|
|
933
|
+
|
|
934
|
+
if (line_is_partial_col0_separator(lines[i])) continue;
|
|
935
|
+
|
|
936
|
+
char *partial = extract_partial_col0_text(lines[i]);
|
|
937
|
+
if (partial) {
|
|
938
|
+
col0_label = append_col0_label(col0_label, partial);
|
|
939
|
+
free(partial);
|
|
940
|
+
continue;
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
char *cells[MAX_COLUMNS] = {0};
|
|
944
|
+
size_t ncells = parse_grid_row_cells(lines[i], cells, column_count);
|
|
945
|
+
if (ncells == 0) {
|
|
946
|
+
continue;
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
if (ncells >= 1 && cells[0] && *cells[0]) {
|
|
950
|
+
col0_label = append_col0_label(col0_label, cells[0]);
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
if (ncells >= 3) {
|
|
954
|
+
if (row_count >= row_cap) {
|
|
955
|
+
row_cap *= 2;
|
|
956
|
+
row_c1 = realloc(row_c1, row_cap * sizeof(char*));
|
|
957
|
+
row_c2 = realloc(row_c2, row_cap * sizeof(char*));
|
|
958
|
+
}
|
|
959
|
+
row_c1[row_count] = cells[1] ? strdup(cells[1]) : strdup("");
|
|
960
|
+
row_c2[row_count] = cells[2] ? strdup(cells[2]) : strdup("");
|
|
961
|
+
free(cells[0]);
|
|
962
|
+
free(cells[1]);
|
|
963
|
+
free(cells[2]);
|
|
964
|
+
row_count++;
|
|
965
|
+
} else {
|
|
966
|
+
for (size_t c = 0; c < ncells; c++) free(cells[c]);
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
for (size_t r = 0; r < row_count; r++) {
|
|
971
|
+
const char *tr = "<tr>\n";
|
|
972
|
+
size_t trl = strlen(tr);
|
|
973
|
+
if (trl < remaining) {
|
|
974
|
+
memcpy(p, tr, trl);
|
|
975
|
+
p += trl;
|
|
976
|
+
remaining -= trl;
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
if (r == 0 && col0_label && *col0_label) {
|
|
980
|
+
write_html_cell(&p, &remaining, &output, &total_size, "td", col0_label,
|
|
981
|
+
1, row_count, true);
|
|
982
|
+
} else if (r > 0) {
|
|
983
|
+
/* rowspan covers later rows */
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
write_html_cell(&p, &remaining, &output, &total_size, "td", row_c1[r], 1, 1, true);
|
|
987
|
+
write_html_cell(&p, &remaining, &output, &total_size, "td", row_c2[r], 1, 1, true);
|
|
988
|
+
|
|
989
|
+
const char *endtr = "</tr>\n";
|
|
990
|
+
size_t etl = strlen(endtr);
|
|
991
|
+
if (etl < remaining) {
|
|
992
|
+
memcpy(p, endtr, etl);
|
|
993
|
+
p += etl;
|
|
994
|
+
remaining -= etl;
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
free(col0_label);
|
|
999
|
+
for (size_t r = 0; r < row_count; r++) {
|
|
1000
|
+
free(row_c1[r]);
|
|
1001
|
+
free(row_c2[r]);
|
|
1002
|
+
}
|
|
1003
|
+
free(row_c1);
|
|
1004
|
+
free(row_c2);
|
|
1005
|
+
|
|
1006
|
+
const char *close = "</tbody>\n</table>\n\n";
|
|
1007
|
+
size_t cl = strlen(close);
|
|
1008
|
+
if (cl < remaining) {
|
|
1009
|
+
memcpy(p, close, cl);
|
|
1010
|
+
p += cl;
|
|
1011
|
+
remaining -= cl;
|
|
1012
|
+
}
|
|
1013
|
+
*p = '\0';
|
|
1014
|
+
return output;
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
char *apex_preprocess_grid_tables(const char *text);
|
|
1018
|
+
|
|
1019
|
+
static char *preprocess_grid_text(const char *text, bool embed_as_html);
|
|
1020
|
+
static char *convert_collected_grid_table(char **table_lines, size_t table_line_count,
|
|
1021
|
+
bool force_html);
|
|
1022
|
+
|
|
1023
|
+
static char *preprocess_cell_grid_content(const char *content) {
|
|
1024
|
+
if (!content || !strchr(content, '+')) return NULL;
|
|
1025
|
+
/* Pipe tables are not parsed inside markdown="1" cells; emit HTML for embedded grids. */
|
|
1026
|
+
return preprocess_grid_text(content, true);
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
static char *escape_pipes_in_multiline_cell(const char *content) {
|
|
1030
|
+
if (!content || !strchr(content, '\n')) return NULL;
|
|
1031
|
+
if (!strchr(content, '|') && !strchr(content, '-')) return NULL;
|
|
1032
|
+
|
|
1033
|
+
size_t len = strlen(content);
|
|
1034
|
+
char *out = malloc(len * 6 + 1);
|
|
1035
|
+
if (!out) return NULL;
|
|
1036
|
+
|
|
1037
|
+
char *p = out;
|
|
1038
|
+
for (const char *s = content; *s; s++) {
|
|
1039
|
+
if (*s == '|') {
|
|
1040
|
+
memcpy(p, "|", 6);
|
|
1041
|
+
p += 6;
|
|
1042
|
+
} else if (*s == '\n') {
|
|
1043
|
+
memcpy(p, "<br>", 4);
|
|
1044
|
+
p += 4;
|
|
1045
|
+
} else {
|
|
1046
|
+
*p++ = *s;
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
*p = '\0';
|
|
1050
|
+
return out;
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
static char *trim_grid_cell_border_pipes(char *content) {
|
|
1054
|
+
if (!content) return content;
|
|
1055
|
+
char *start = content;
|
|
1056
|
+
while (*start == ' ' || *start == '\t') start++;
|
|
1057
|
+
char *end = content + strlen(content);
|
|
1058
|
+
while (end > start && (end[-1] == ' ' || end[-1] == '\t' || end[-1] == '\n' || end[-1] == '\r')) end--;
|
|
1059
|
+
if (*start == '|') start++;
|
|
1060
|
+
while (end > start && (end[-1] == ' ' || end[-1] == '\t')) end--;
|
|
1061
|
+
if (end > start && end[-1] == '|') end--;
|
|
1062
|
+
while (end > start && end[-1] == ' ') end--;
|
|
1063
|
+
size_t len = (size_t)(end - start);
|
|
1064
|
+
char *out = malloc(len + 1);
|
|
1065
|
+
if (!out) return content;
|
|
1066
|
+
memcpy(out, start, len);
|
|
1067
|
+
out[len] = '\0';
|
|
1068
|
+
free(content);
|
|
1069
|
+
return out;
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
/**
|
|
1073
|
+
* Trim border pipes from each grid row line before colspan cell escaping.
|
|
1074
|
+
*/
|
|
1075
|
+
static char *normalize_colspan_merged_content(const char *merged) {
|
|
1076
|
+
if (!merged) return NULL;
|
|
1077
|
+
|
|
1078
|
+
char *result = NULL;
|
|
1079
|
+
const char *s = merged;
|
|
1080
|
+
while (*s) {
|
|
1081
|
+
const char *line_end = strchr(s, '\n');
|
|
1082
|
+
if (!line_end) line_end = s + strlen(s);
|
|
1083
|
+
|
|
1084
|
+
size_t line_len = (size_t)(line_end - s);
|
|
1085
|
+
char *line = malloc(line_len + 1);
|
|
1086
|
+
if (!line) break;
|
|
1087
|
+
|
|
1088
|
+
memcpy(line, s, line_len);
|
|
1089
|
+
line[line_len] = '\0';
|
|
1090
|
+
|
|
1091
|
+
if (strchr(line, '|') && !is_grid_table_separator(line) &&
|
|
1092
|
+
count_cells_in_line(line) <= 1) {
|
|
1093
|
+
line = trim_grid_cell_border_pipes(line);
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1096
|
+
result = str_append_line(result, line);
|
|
1097
|
+
free(line);
|
|
1098
|
+
|
|
1099
|
+
s = *line_end ? line_end + 1 : line_end;
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
return result;
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1105
|
+
static char *convert_grid_table(char **lines, size_t line_count,
|
|
1106
|
+
int *alignments, size_t column_count,
|
|
1107
|
+
bool as_html);
|
|
589
1108
|
static char *convert_grid_table_to_pipe(char **lines, size_t line_count,
|
|
590
1109
|
int *alignments, size_t column_count) {
|
|
1110
|
+
return convert_grid_table(lines, line_count, alignments, column_count, false);
|
|
1111
|
+
}
|
|
1112
|
+
static char *convert_grid_table_to_html(char **lines, size_t line_count,
|
|
1113
|
+
int *alignments, size_t column_count) {
|
|
1114
|
+
return convert_grid_table(lines, line_count, alignments, column_count, true);
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
/**
|
|
1118
|
+
* Convert grid table rows to pipe or HTML format
|
|
1119
|
+
*/
|
|
1120
|
+
static char *convert_grid_table(char **lines, size_t line_count,
|
|
1121
|
+
int *alignments, size_t column_count,
|
|
1122
|
+
bool as_html) {
|
|
591
1123
|
if (!lines || line_count == 0 || !alignments || column_count == 0) {
|
|
592
1124
|
return NULL;
|
|
593
1125
|
}
|
|
@@ -599,7 +1131,7 @@ static char *convert_grid_table_to_pipe(char **lines, size_t line_count,
|
|
|
599
1131
|
total_size += strlen(lines[i]) + column_count * 4 + 10;
|
|
600
1132
|
}
|
|
601
1133
|
}
|
|
602
|
-
total_size +=
|
|
1134
|
+
total_size += as_html ? 4096 : 200;
|
|
603
1135
|
|
|
604
1136
|
char *output = malloc(total_size);
|
|
605
1137
|
if (!output) return NULL;
|
|
@@ -607,14 +1139,22 @@ static char *convert_grid_table_to_pipe(char **lines, size_t line_count,
|
|
|
607
1139
|
char *p = output;
|
|
608
1140
|
size_t remaining = total_size;
|
|
609
1141
|
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
1142
|
+
if (as_html) {
|
|
1143
|
+
memcpy(p, "\n\n<table>\n", 10);
|
|
1144
|
+
p += 10;
|
|
1145
|
+
remaining -= 10;
|
|
1146
|
+
} else {
|
|
1147
|
+
/* Add blank lines at start of table for proper recognition */
|
|
1148
|
+
*p++ = '\n';
|
|
1149
|
+
*p++ = '\n';
|
|
1150
|
+
remaining -= 2;
|
|
1151
|
+
}
|
|
614
1152
|
|
|
615
1153
|
bool separator_processed = false;
|
|
616
1154
|
bool header_written = false;
|
|
617
|
-
bool rows_written = false;
|
|
1155
|
+
bool rows_written = false;
|
|
1156
|
+
bool in_thead = false;
|
|
1157
|
+
bool pipe_header_promoted = false;
|
|
618
1158
|
|
|
619
1159
|
/* First, find the header separator to identify header vs body */
|
|
620
1160
|
size_t header_sep_idx = 0;
|
|
@@ -634,8 +1174,8 @@ static char *convert_grid_table_to_pipe(char **lines, size_t line_count,
|
|
|
634
1174
|
const char *line = lines[i];
|
|
635
1175
|
if (!line) continue;
|
|
636
1176
|
|
|
637
|
-
/* Check if this is a separator line */
|
|
638
|
-
if (is_grid_table_separator(line)) {
|
|
1177
|
+
/* Check if this is a separator line (table boundary, not nested grid in cell) */
|
|
1178
|
+
if (is_grid_table_separator(line) && !is_nested_grid_separator(line, column_count)) {
|
|
639
1179
|
bool is_header_sep = is_header_separator(line);
|
|
640
1180
|
bool is_footer_sep = false;
|
|
641
1181
|
|
|
@@ -669,27 +1209,35 @@ static char *convert_grid_table_to_pipe(char **lines, size_t line_count,
|
|
|
669
1209
|
|
|
670
1210
|
/* Collect lines that form a single logical row */
|
|
671
1211
|
const char *first_line = NULL;
|
|
1212
|
+
bool colspan_block = false;
|
|
672
1213
|
for (size_t j = line_idx; j < i; j++) {
|
|
673
|
-
if (
|
|
1214
|
+
if (!lines[j]) continue;
|
|
1215
|
+
|
|
1216
|
+
if (is_grid_table_separator(lines[j])) {
|
|
1217
|
+
if (is_nested_grid_separator(lines[j], column_count)) {
|
|
1218
|
+
if (first_line) row_line_count++;
|
|
1219
|
+
continue;
|
|
1220
|
+
}
|
|
1221
|
+
if (first_line) break;
|
|
1222
|
+
continue;
|
|
1223
|
+
}
|
|
1224
|
+
|
|
1225
|
+
if (strchr(lines[j], '|')) {
|
|
674
1226
|
if (!first_line) {
|
|
675
1227
|
first_line = lines[j];
|
|
676
1228
|
row_line_start = j;
|
|
677
1229
|
row_line_count = 1;
|
|
1230
|
+
colspan_block = is_full_row_colspan_line(first_line);
|
|
1231
|
+
} else if (colspan_block) {
|
|
1232
|
+
row_line_count++;
|
|
678
1233
|
} else if (has_same_column_structure(first_line, lines[j])) {
|
|
679
|
-
/* Same column structure - part of multi-line cell */
|
|
680
1234
|
row_line_count++;
|
|
681
1235
|
} else {
|
|
682
|
-
/* Different column structure - new row */
|
|
683
|
-
/* Note: Lines with nested separators (like +-------+ within cells) will be
|
|
684
|
-
* treated as separate rows, which is correct. The nested separator will be
|
|
685
|
-
* included as literal text in the cell content. */
|
|
686
1236
|
break;
|
|
687
1237
|
}
|
|
688
1238
|
} else if (first_line) {
|
|
689
|
-
|
|
690
|
-
break;
|
|
1239
|
+
row_line_count++;
|
|
691
1240
|
} else {
|
|
692
|
-
/* Skip blank lines before first content line */
|
|
693
1241
|
line_idx++;
|
|
694
1242
|
}
|
|
695
1243
|
}
|
|
@@ -703,38 +1251,230 @@ static char *convert_grid_table_to_pipe(char **lines, size_t line_count,
|
|
|
703
1251
|
}
|
|
704
1252
|
|
|
705
1253
|
size_t cell_count = 0;
|
|
706
|
-
char **cells =
|
|
1254
|
+
char **cells = NULL;
|
|
1255
|
+
bool colspan_group = is_full_row_colspan_line(row_lines_array[0]);
|
|
1256
|
+
|
|
1257
|
+
if (colspan_group) {
|
|
1258
|
+
cells = malloc(MAX_COLUMNS * sizeof(char*));
|
|
1259
|
+
if (cells) {
|
|
1260
|
+
char *merged = NULL;
|
|
1261
|
+
for (size_t j = 0; j < row_line_count; j++) {
|
|
1262
|
+
merged = str_append_line(merged, row_lines_array[j]);
|
|
1263
|
+
}
|
|
1264
|
+
cells[0] = merged;
|
|
1265
|
+
cell_count = 1;
|
|
1266
|
+
}
|
|
1267
|
+
if (cells && cells[0] && cell_count == 1 && column_count > 1) {
|
|
1268
|
+
cells[0] = trim_grid_cell_border_pipes(cells[0]);
|
|
1269
|
+
}
|
|
1270
|
+
} else {
|
|
1271
|
+
cells = extract_cells_from_row_lines(row_lines_array, row_line_count, &cell_count);
|
|
1272
|
+
}
|
|
707
1273
|
|
|
708
1274
|
if (cells && cell_count > 0) {
|
|
1275
|
+
bool force_colspan = (row_line_count > 0 && row_lines_array[0] &&
|
|
1276
|
+
is_full_row_colspan_line(row_lines_array[0]));
|
|
709
1277
|
/* Write header separator after first header row, before first body row */
|
|
710
1278
|
if (is_header_section && !header_written && !separator_processed && !is_footer_sep) {
|
|
711
1279
|
header_written = true;
|
|
712
1280
|
} else if (!is_header_section && !separator_processed && !is_footer_sep) {
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
1281
|
+
if (!as_html && has_header_sep) {
|
|
1282
|
+
char *sep = create_pipe_separator(alignments, column_count);
|
|
1283
|
+
if (sep) {
|
|
1284
|
+
size_t sep_len = strlen(sep);
|
|
1285
|
+
if (sep_len < remaining) {
|
|
1286
|
+
memcpy(p, sep, sep_len);
|
|
1287
|
+
p += sep_len;
|
|
1288
|
+
remaining -= sep_len;
|
|
1289
|
+
*p++ = '\n';
|
|
1290
|
+
remaining--;
|
|
1291
|
+
}
|
|
1292
|
+
free(sep);
|
|
723
1293
|
}
|
|
724
|
-
|
|
1294
|
+
separator_processed = true;
|
|
725
1295
|
}
|
|
726
|
-
separator_processed = true;
|
|
727
1296
|
}
|
|
728
1297
|
|
|
729
|
-
/* Convert to pipe table row */
|
|
1298
|
+
/* Convert to pipe or HTML table row */
|
|
730
1299
|
if (remaining > column_count * 50) {
|
|
1300
|
+
bool use_colspan = force_colspan;
|
|
1301
|
+
if (use_colspan && force_colspan && cells && cell_count > 0) {
|
|
1302
|
+
/* Collapse to single cell for colspan row */
|
|
1303
|
+
char *merged_one = NULL;
|
|
1304
|
+
for (size_t ci = 0; ci < cell_count; ci++) {
|
|
1305
|
+
if (cells[ci]) {
|
|
1306
|
+
const char *t = cells[ci];
|
|
1307
|
+
while (*t && isspace((unsigned char)*t)) t++;
|
|
1308
|
+
if (*t) {
|
|
1309
|
+
merged_one = strdup(cells[ci]);
|
|
1310
|
+
break;
|
|
1311
|
+
}
|
|
1312
|
+
}
|
|
1313
|
+
}
|
|
1314
|
+
if (!merged_one && cells[0]) merged_one = strdup(cells[0]);
|
|
1315
|
+
if (merged_one) {
|
|
1316
|
+
for (size_t ci = 0; ci < cell_count; ci++) free(cells[ci]);
|
|
1317
|
+
cells[0] = trim_grid_cell_border_pipes(merged_one);
|
|
1318
|
+
cell_count = 1;
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1321
|
+
|
|
1322
|
+
char *colspan_processed = NULL;
|
|
1323
|
+
if (as_html && use_colspan && cells[0]) {
|
|
1324
|
+
colspan_processed = preprocess_cell_grid_content(cells[0]);
|
|
1325
|
+
}
|
|
1326
|
+
|
|
1327
|
+
if (as_html) {
|
|
1328
|
+
if (is_header_section && !separator_processed && !in_thead) {
|
|
1329
|
+
const char *thead = "<thead>\n";
|
|
1330
|
+
size_t tl = strlen(thead);
|
|
1331
|
+
if (tl < remaining) {
|
|
1332
|
+
memcpy(p, thead, tl);
|
|
1333
|
+
p += tl;
|
|
1334
|
+
remaining -= tl;
|
|
1335
|
+
in_thead = true;
|
|
1336
|
+
}
|
|
1337
|
+
} else if (!is_header_section && in_thead) {
|
|
1338
|
+
const char *end = "</thead>\n<tbody>\n";
|
|
1339
|
+
size_t el = strlen(end);
|
|
1340
|
+
if (el < remaining) {
|
|
1341
|
+
memcpy(p, end, el);
|
|
1342
|
+
p += el;
|
|
1343
|
+
remaining -= el;
|
|
1344
|
+
in_thead = false;
|
|
1345
|
+
}
|
|
1346
|
+
} else if (!is_header_section && !in_thead && !rows_written) {
|
|
1347
|
+
const char *tbody = "<tbody>\n";
|
|
1348
|
+
size_t bl = strlen(tbody);
|
|
1349
|
+
if (bl < remaining) {
|
|
1350
|
+
memcpy(p, tbody, bl);
|
|
1351
|
+
p += bl;
|
|
1352
|
+
remaining -= bl;
|
|
1353
|
+
}
|
|
1354
|
+
}
|
|
1355
|
+
|
|
1356
|
+
const char *tr = "<tr>\n";
|
|
1357
|
+
size_t trl = strlen(tr);
|
|
1358
|
+
if (trl < remaining) {
|
|
1359
|
+
memcpy(p, tr, trl);
|
|
1360
|
+
p += trl;
|
|
1361
|
+
remaining -= trl;
|
|
1362
|
+
}
|
|
1363
|
+
|
|
1364
|
+
size_t cells_to_write = use_colspan ? 1 : cell_count;
|
|
1365
|
+
for (size_t k = 0; k < cells_to_write; k++) {
|
|
1366
|
+
const char *cell_content = (k < cell_count && cells[k]) ? cells[k] : "";
|
|
1367
|
+
if (use_colspan) {
|
|
1368
|
+
cell_content = colspan_processed ? colspan_processed : cells[0];
|
|
1369
|
+
}
|
|
1370
|
+
|
|
1371
|
+
const char *tag = (is_header_section && !separator_processed) ? "th" : "td";
|
|
1372
|
+
size_t span = 1;
|
|
1373
|
+
if (use_colspan && column_count > 1) {
|
|
1374
|
+
span = column_count;
|
|
1375
|
+
} else if (!use_colspan && cell_count < column_count && k == 0) {
|
|
1376
|
+
span = column_count - cell_count + 1;
|
|
1377
|
+
}
|
|
1378
|
+
|
|
1379
|
+
char open[256];
|
|
1380
|
+
if (span > 1) {
|
|
1381
|
+
snprintf(open, sizeof(open),
|
|
1382
|
+
"<%s markdown=\"1\" colspan=\"%zu\">\n\n",
|
|
1383
|
+
tag, span);
|
|
1384
|
+
} else {
|
|
1385
|
+
snprintf(open, sizeof(open),
|
|
1386
|
+
"<%s markdown=\"1\">\n\n", tag);
|
|
1387
|
+
}
|
|
1388
|
+
size_t ol = strlen(open);
|
|
1389
|
+
if (ol < remaining) {
|
|
1390
|
+
memcpy(p, open, ol);
|
|
1391
|
+
p += ol;
|
|
1392
|
+
remaining -= ol;
|
|
1393
|
+
}
|
|
1394
|
+
size_t cl = strlen(cell_content);
|
|
1395
|
+
if (cl < remaining) {
|
|
1396
|
+
memcpy(p, cell_content, cl);
|
|
1397
|
+
p += cl;
|
|
1398
|
+
remaining -= cl;
|
|
1399
|
+
}
|
|
1400
|
+
char close[16];
|
|
1401
|
+
snprintf(close, sizeof(close), "\n\n</%s>\n", tag);
|
|
1402
|
+
size_t cll = strlen(close);
|
|
1403
|
+
if (cll < remaining) {
|
|
1404
|
+
memcpy(p, close, cll);
|
|
1405
|
+
p += cll;
|
|
1406
|
+
remaining -= cll;
|
|
1407
|
+
}
|
|
1408
|
+
}
|
|
1409
|
+
|
|
1410
|
+
const char *endtr = "</tr>\n";
|
|
1411
|
+
size_t etl = strlen(endtr);
|
|
1412
|
+
if (etl < remaining) {
|
|
1413
|
+
memcpy(p, endtr, etl);
|
|
1414
|
+
p += etl;
|
|
1415
|
+
remaining -= etl;
|
|
1416
|
+
}
|
|
1417
|
+
|
|
1418
|
+
if (colspan_processed) free(colspan_processed);
|
|
1419
|
+
rows_written = true;
|
|
1420
|
+
} else {
|
|
1421
|
+
if (use_colspan && cells[0]) {
|
|
1422
|
+
char *tmp_trim = strdup(cells[0]);
|
|
1423
|
+
if (tmp_trim) tmp_trim = trim_grid_cell_border_pipes(tmp_trim);
|
|
1424
|
+
const char *content = tmp_trim ? tmp_trim : cells[0];
|
|
1425
|
+
|
|
1426
|
+
*p++ = '|';
|
|
1427
|
+
remaining--;
|
|
1428
|
+
*p++ = ' ';
|
|
1429
|
+
remaining--;
|
|
1430
|
+
size_t cl = strlen(content);
|
|
1431
|
+
if (cl < remaining - 10) {
|
|
1432
|
+
memcpy(p, content, cl);
|
|
1433
|
+
p += cl;
|
|
1434
|
+
remaining -= cl;
|
|
1435
|
+
}
|
|
1436
|
+
*p++ = ' ';
|
|
1437
|
+
remaining--;
|
|
1438
|
+
*p++ = '|';
|
|
1439
|
+
remaining--;
|
|
1440
|
+
const char *marker = " << |";
|
|
1441
|
+
size_t ml = strlen(marker);
|
|
1442
|
+
if (ml < remaining) {
|
|
1443
|
+
memcpy(p, marker, ml);
|
|
1444
|
+
p += ml;
|
|
1445
|
+
remaining -= ml;
|
|
1446
|
+
}
|
|
1447
|
+
*p++ = '\n';
|
|
1448
|
+
remaining--;
|
|
1449
|
+
if (tmp_trim) free(tmp_trim);
|
|
1450
|
+
rows_written = true;
|
|
1451
|
+
} else {
|
|
731
1452
|
*p++ = '|';
|
|
732
1453
|
remaining--;
|
|
733
1454
|
|
|
734
1455
|
for (size_t k = 0; k < column_count; k++) {
|
|
735
|
-
const char *cell_content =
|
|
1456
|
+
const char *cell_content = "";
|
|
1457
|
+
char *escaped = NULL;
|
|
1458
|
+
char *tmp_trim = NULL;
|
|
1459
|
+
if (use_colspan) {
|
|
1460
|
+
cell_content = (k == 0) ? cells[0] : "<<";
|
|
1461
|
+
} else if (k < cell_count && cells[k]) {
|
|
1462
|
+
cell_content = cells[k];
|
|
1463
|
+
}
|
|
1464
|
+
|
|
1465
|
+
if (use_colspan && k == 0 && cell_content && *cell_content) {
|
|
1466
|
+
tmp_trim = strdup(cell_content);
|
|
1467
|
+
if (tmp_trim) {
|
|
1468
|
+
tmp_trim = trim_grid_cell_border_pipes(tmp_trim);
|
|
1469
|
+
cell_content = tmp_trim;
|
|
1470
|
+
}
|
|
1471
|
+
}
|
|
1472
|
+
|
|
1473
|
+
if (cell_content && !use_colspan && strchr(cell_content, '\n')) {
|
|
1474
|
+
escaped = escape_pipes_in_multiline_cell(cell_content);
|
|
1475
|
+
if (escaped) cell_content = escaped;
|
|
1476
|
+
}
|
|
736
1477
|
|
|
737
|
-
/* Trim leading/trailing whitespace from cell content */
|
|
738
1478
|
const char *content_start = cell_content;
|
|
739
1479
|
const char *content_end = cell_content + strlen(cell_content);
|
|
740
1480
|
while (content_start < content_end && isspace((unsigned char)*content_start)) {
|
|
@@ -745,26 +1485,44 @@ static char *convert_grid_table_to_pipe(char **lines, size_t line_count,
|
|
|
745
1485
|
}
|
|
746
1486
|
size_t content_len = content_end - content_start;
|
|
747
1487
|
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
1488
|
+
*p++ = ' ';
|
|
1489
|
+
remaining--;
|
|
1490
|
+
if (content_len > 0 && content_len < remaining - 2) {
|
|
751
1491
|
memcpy(p, content_start, content_len);
|
|
752
1492
|
p += content_len;
|
|
753
1493
|
remaining -= content_len;
|
|
754
|
-
*p++ = ' ';
|
|
755
|
-
remaining--;
|
|
756
|
-
} else {
|
|
757
|
-
*p++ = ' ';
|
|
758
|
-
remaining--;
|
|
759
1494
|
}
|
|
760
|
-
|
|
1495
|
+
*p++ = ' ';
|
|
1496
|
+
remaining--;
|
|
761
1497
|
*p++ = '|';
|
|
762
1498
|
remaining--;
|
|
1499
|
+
if (escaped) free(escaped);
|
|
1500
|
+
if (tmp_trim) free(tmp_trim);
|
|
763
1501
|
}
|
|
764
1502
|
|
|
765
1503
|
*p++ = '\n';
|
|
766
1504
|
remaining--;
|
|
767
|
-
rows_written = true;
|
|
1505
|
+
rows_written = true;
|
|
1506
|
+
|
|
1507
|
+
if (!has_header_sep && !pipe_header_promoted &&
|
|
1508
|
+
!is_header_section && !use_colspan) {
|
|
1509
|
+
char *sep = create_pipe_separator(alignments, column_count);
|
|
1510
|
+
if (sep) {
|
|
1511
|
+
size_t sep_len = strlen(sep);
|
|
1512
|
+
if (sep_len < remaining) {
|
|
1513
|
+
memcpy(p, sep, sep_len);
|
|
1514
|
+
p += sep_len;
|
|
1515
|
+
remaining -= sep_len;
|
|
1516
|
+
*p++ = '\n';
|
|
1517
|
+
remaining--;
|
|
1518
|
+
}
|
|
1519
|
+
free(sep);
|
|
1520
|
+
}
|
|
1521
|
+
pipe_header_promoted = true;
|
|
1522
|
+
separator_processed = true;
|
|
1523
|
+
}
|
|
1524
|
+
}
|
|
1525
|
+
}
|
|
768
1526
|
}
|
|
769
1527
|
|
|
770
1528
|
/* Free cells */
|
|
@@ -786,23 +1544,7 @@ static char *convert_grid_table_to_pipe(char **lines, size_t line_count,
|
|
|
786
1544
|
|
|
787
1545
|
/* Write separator after header rows if this is the header separator */
|
|
788
1546
|
if (is_header_sep && !is_footer_sep && !separator_processed && header_written) {
|
|
789
|
-
|
|
790
|
-
if (sep) {
|
|
791
|
-
size_t sep_len = strlen(sep);
|
|
792
|
-
if (sep_len < remaining) {
|
|
793
|
-
memcpy(p, sep, sep_len);
|
|
794
|
-
p += sep_len;
|
|
795
|
-
remaining -= sep_len;
|
|
796
|
-
*p++ = '\n';
|
|
797
|
-
remaining--;
|
|
798
|
-
}
|
|
799
|
-
free(sep);
|
|
800
|
-
}
|
|
801
|
-
separator_processed = true;
|
|
802
|
-
} else if (!is_header_sep && !is_footer_sep && !separator_processed && rows_written) {
|
|
803
|
-
/* Regular separator (not header) - write separator after rows have been written */
|
|
804
|
-
/* But only if we haven't written header separator yet */
|
|
805
|
-
if (!has_header_sep || i > header_sep_idx) {
|
|
1547
|
+
if (!as_html) {
|
|
806
1548
|
char *sep = create_pipe_separator(alignments, column_count);
|
|
807
1549
|
if (sep) {
|
|
808
1550
|
size_t sep_len = strlen(sep);
|
|
@@ -815,6 +1557,25 @@ static char *convert_grid_table_to_pipe(char **lines, size_t line_count,
|
|
|
815
1557
|
}
|
|
816
1558
|
free(sep);
|
|
817
1559
|
}
|
|
1560
|
+
}
|
|
1561
|
+
separator_processed = true;
|
|
1562
|
+
} else if (!is_header_sep && !is_footer_sep && !separator_processed && rows_written &&
|
|
1563
|
+
has_header_sep) {
|
|
1564
|
+
if (i > header_sep_idx) {
|
|
1565
|
+
if (!as_html) {
|
|
1566
|
+
char *sep = create_pipe_separator(alignments, column_count);
|
|
1567
|
+
if (sep) {
|
|
1568
|
+
size_t sep_len = strlen(sep);
|
|
1569
|
+
if (sep_len < remaining) {
|
|
1570
|
+
memcpy(p, sep, sep_len);
|
|
1571
|
+
p += sep_len;
|
|
1572
|
+
remaining -= sep_len;
|
|
1573
|
+
*p++ = '\n';
|
|
1574
|
+
remaining--;
|
|
1575
|
+
}
|
|
1576
|
+
free(sep);
|
|
1577
|
+
}
|
|
1578
|
+
}
|
|
818
1579
|
separator_processed = true;
|
|
819
1580
|
}
|
|
820
1581
|
}
|
|
@@ -826,6 +1587,98 @@ static char *convert_grid_table_to_pipe(char **lines, size_t line_count,
|
|
|
826
1587
|
|
|
827
1588
|
/* Process final rows after last separator */
|
|
828
1589
|
if (row_start < line_count) {
|
|
1590
|
+
size_t first_content = row_start;
|
|
1591
|
+
for (; first_content < line_count; first_content++) {
|
|
1592
|
+
if (lines[first_content] && strchr(lines[first_content], '|')) break;
|
|
1593
|
+
}
|
|
1594
|
+
|
|
1595
|
+
bool final_colspan = (first_content < line_count &&
|
|
1596
|
+
is_full_row_colspan_line(lines[first_content]));
|
|
1597
|
+
|
|
1598
|
+
if (final_colspan) {
|
|
1599
|
+
char *merged = NULL;
|
|
1600
|
+
for (size_t j = row_start; j < line_count; j++) {
|
|
1601
|
+
if (lines[j]) merged = str_append_line(merged, lines[j]);
|
|
1602
|
+
}
|
|
1603
|
+
|
|
1604
|
+
if (merged && remaining > column_count * 50) {
|
|
1605
|
+
char *normalized = normalize_colspan_merged_content(merged);
|
|
1606
|
+
if (normalized) {
|
|
1607
|
+
free(merged);
|
|
1608
|
+
merged = normalized;
|
|
1609
|
+
}
|
|
1610
|
+
|
|
1611
|
+
char *escaped = NULL;
|
|
1612
|
+
const char *content = merged;
|
|
1613
|
+
char *processed = NULL;
|
|
1614
|
+
if (as_html) {
|
|
1615
|
+
processed = preprocess_cell_grid_content(merged);
|
|
1616
|
+
if (processed) content = processed;
|
|
1617
|
+
} else {
|
|
1618
|
+
escaped = escape_pipes_in_multiline_cell(merged);
|
|
1619
|
+
if (escaped) content = escaped;
|
|
1620
|
+
}
|
|
1621
|
+
|
|
1622
|
+
if (as_html) {
|
|
1623
|
+
if (in_thead) {
|
|
1624
|
+
const char *end = "</thead>\n<tbody>\n";
|
|
1625
|
+
size_t el = strlen(end);
|
|
1626
|
+
if (el < remaining) {
|
|
1627
|
+
memcpy(p, end, el);
|
|
1628
|
+
p += el;
|
|
1629
|
+
remaining -= el;
|
|
1630
|
+
in_thead = false;
|
|
1631
|
+
}
|
|
1632
|
+
} else if (!rows_written) {
|
|
1633
|
+
const char *tbody = "<tbody>\n";
|
|
1634
|
+
size_t bl = strlen(tbody);
|
|
1635
|
+
if (bl < remaining) {
|
|
1636
|
+
memcpy(p, tbody, bl);
|
|
1637
|
+
p += bl;
|
|
1638
|
+
remaining -= bl;
|
|
1639
|
+
}
|
|
1640
|
+
}
|
|
1641
|
+
|
|
1642
|
+
char open[64];
|
|
1643
|
+
snprintf(open, sizeof(open), "<tr>\n<td markdown=\"1\" colspan=\"%zu\">\n\n", column_count);
|
|
1644
|
+
size_t ol = strlen(open);
|
|
1645
|
+
if (ol < remaining) { memcpy(p, open, ol); p += ol; remaining -= ol; }
|
|
1646
|
+
size_t cl = strlen(content);
|
|
1647
|
+
if (cl < remaining) { memcpy(p, content, cl); p += cl; remaining -= cl; }
|
|
1648
|
+
const char *close = "\n\n</td>\n</tr>\n";
|
|
1649
|
+
size_t cll = strlen(close);
|
|
1650
|
+
if (cll < remaining) { memcpy(p, close, cll); p += cll; remaining -= cll; }
|
|
1651
|
+
rows_written = true;
|
|
1652
|
+
} else {
|
|
1653
|
+
*p++ = '|';
|
|
1654
|
+
remaining--;
|
|
1655
|
+
*p++ = ' ';
|
|
1656
|
+
remaining--;
|
|
1657
|
+
size_t cl = strlen(content);
|
|
1658
|
+
if (cl < remaining - 10) {
|
|
1659
|
+
memcpy(p, content, cl);
|
|
1660
|
+
p += cl;
|
|
1661
|
+
remaining -= cl;
|
|
1662
|
+
}
|
|
1663
|
+
*p++ = ' ';
|
|
1664
|
+
remaining--;
|
|
1665
|
+
*p++ = '|';
|
|
1666
|
+
remaining--;
|
|
1667
|
+
const char *marker = " << |";
|
|
1668
|
+
size_t ml = strlen(marker);
|
|
1669
|
+
if (ml < remaining) {
|
|
1670
|
+
memcpy(p, marker, ml);
|
|
1671
|
+
p += ml;
|
|
1672
|
+
remaining -= ml;
|
|
1673
|
+
}
|
|
1674
|
+
*p++ = '\n';
|
|
1675
|
+
remaining--;
|
|
1676
|
+
}
|
|
1677
|
+
if (escaped) free(escaped);
|
|
1678
|
+
if (processed) free(processed);
|
|
1679
|
+
free(merged);
|
|
1680
|
+
}
|
|
1681
|
+
} else {
|
|
829
1682
|
char **row_lines = malloc((line_count - row_start) * sizeof(char*));
|
|
830
1683
|
if (row_lines) {
|
|
831
1684
|
size_t row_line_count = 0;
|
|
@@ -888,24 +1741,109 @@ static char *convert_grid_table_to_pipe(char **lines, size_t line_count,
|
|
|
888
1741
|
}
|
|
889
1742
|
free(row_lines);
|
|
890
1743
|
}
|
|
1744
|
+
}
|
|
891
1745
|
}
|
|
892
1746
|
|
|
893
|
-
|
|
894
|
-
|
|
1747
|
+
if (as_html) {
|
|
1748
|
+
if (in_thead) {
|
|
1749
|
+
const char *end = "</thead>\n<tbody>\n";
|
|
1750
|
+
size_t el = strlen(end);
|
|
1751
|
+
if (el < remaining) {
|
|
1752
|
+
memcpy(p, end, el);
|
|
1753
|
+
p += el;
|
|
1754
|
+
remaining -= el;
|
|
1755
|
+
}
|
|
1756
|
+
}
|
|
1757
|
+
const char *close = "</tbody>\n</table>\n\n";
|
|
1758
|
+
size_t cl = strlen(close);
|
|
1759
|
+
if (cl < remaining) {
|
|
1760
|
+
memcpy(p, close, cl);
|
|
1761
|
+
p += cl;
|
|
1762
|
+
remaining -= cl;
|
|
1763
|
+
}
|
|
1764
|
+
} else {
|
|
1765
|
+
/* Add blank line at end of table */
|
|
1766
|
+
if (p > output && p[-1] != '\n') {
|
|
1767
|
+
*p++ = '\n';
|
|
1768
|
+
remaining--;
|
|
1769
|
+
}
|
|
895
1770
|
*p++ = '\n';
|
|
896
1771
|
remaining--;
|
|
897
1772
|
}
|
|
898
|
-
*p++ = '\n';
|
|
899
|
-
remaining--;
|
|
900
1773
|
|
|
901
1774
|
*p = '\0';
|
|
902
1775
|
return output;
|
|
903
1776
|
}
|
|
904
1777
|
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
1778
|
+
static char *convert_collected_grid_table(char **table_lines, size_t table_line_count,
|
|
1779
|
+
bool force_html) {
|
|
1780
|
+
if (!table_lines || table_line_count == 0) return NULL;
|
|
1781
|
+
|
|
1782
|
+
int alignments[MAX_COLUMNS] = {0};
|
|
1783
|
+
size_t column_count = 0;
|
|
1784
|
+
size_t first_sep_cols = 0;
|
|
1785
|
+
|
|
1786
|
+
for (size_t i = 0; i < table_line_count; i++) {
|
|
1787
|
+
if (is_grid_table_separator(table_lines[i]) && first_sep_cols == 0) {
|
|
1788
|
+
parse_alignment(table_lines[i], alignments, &first_sep_cols);
|
|
1789
|
+
column_count = first_sep_cols;
|
|
1790
|
+
}
|
|
1791
|
+
}
|
|
1792
|
+
|
|
1793
|
+
for (size_t i = 0; i < table_line_count; i++) {
|
|
1794
|
+
if (is_header_separator(table_lines[i])) {
|
|
1795
|
+
size_t sep_cols = 0;
|
|
1796
|
+
int sep_alignments[MAX_COLUMNS] = {0};
|
|
1797
|
+
parse_alignment(table_lines[i], sep_alignments, &sep_cols);
|
|
1798
|
+
if (sep_cols > column_count) {
|
|
1799
|
+
column_count = sep_cols;
|
|
1800
|
+
memcpy(alignments, sep_alignments, sizeof(sep_alignments));
|
|
1801
|
+
}
|
|
1802
|
+
break;
|
|
1803
|
+
}
|
|
1804
|
+
}
|
|
1805
|
+
|
|
1806
|
+
if (column_count == 0) {
|
|
1807
|
+
for (size_t i = 0; i < table_line_count; i++) {
|
|
1808
|
+
if (is_grid_table_separator(table_lines[i])) {
|
|
1809
|
+
parse_alignment(table_lines[i], alignments, &column_count);
|
|
1810
|
+
if (column_count > 0) break;
|
|
1811
|
+
}
|
|
1812
|
+
}
|
|
1813
|
+
}
|
|
1814
|
+
|
|
1815
|
+
if (column_count == 0) {
|
|
1816
|
+
for (size_t i = 0; i < table_line_count; i++) {
|
|
1817
|
+
if (strchr(table_lines[i], '|') && !is_grid_table_separator(table_lines[i])) {
|
|
1818
|
+
size_t row_cols = count_cells_in_line(table_lines[i]);
|
|
1819
|
+
if (row_cols > column_count) {
|
|
1820
|
+
column_count = row_cols;
|
|
1821
|
+
}
|
|
1822
|
+
}
|
|
1823
|
+
}
|
|
1824
|
+
for (size_t j = 0; j < column_count && j < MAX_COLUMNS; j++) {
|
|
1825
|
+
alignments[j] = 0;
|
|
1826
|
+
}
|
|
1827
|
+
}
|
|
1828
|
+
|
|
1829
|
+
if (column_count == 0) return NULL;
|
|
1830
|
+
|
|
1831
|
+
if (table_has_partial_separators(table_lines, table_line_count)) {
|
|
1832
|
+
return convert_partial_grid_table_to_html(table_lines, table_line_count, column_count);
|
|
1833
|
+
}
|
|
1834
|
+
|
|
1835
|
+
if (force_html ||
|
|
1836
|
+
table_has_nested_grid(table_lines, table_line_count, column_count) ||
|
|
1837
|
+
table_has_multiline_cells(table_lines, table_line_count)) {
|
|
1838
|
+
return convert_grid_table_to_html(table_lines, table_line_count,
|
|
1839
|
+
alignments, column_count);
|
|
1840
|
+
}
|
|
1841
|
+
|
|
1842
|
+
return convert_grid_table_to_pipe(table_lines, table_line_count,
|
|
1843
|
+
alignments, column_count);
|
|
1844
|
+
}
|
|
1845
|
+
|
|
1846
|
+
static char *preprocess_grid_text(const char *text, bool embed_as_html) {
|
|
909
1847
|
if (!text) return NULL;
|
|
910
1848
|
|
|
911
1849
|
size_t len = strlen(text);
|
|
@@ -1005,48 +1943,16 @@ char *apex_preprocess_grid_tables(const char *text) {
|
|
|
1005
1943
|
}
|
|
1006
1944
|
|
|
1007
1945
|
if (table_line_count > 0) {
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
for (size_t i = 0; i < table_line_count; i++) {
|
|
1014
|
-
if (is_grid_table_separator(table_lines[i])) {
|
|
1015
|
-
size_t sep_cols = 0;
|
|
1016
|
-
int sep_alignments[MAX_COLUMNS] = {0};
|
|
1017
|
-
parse_alignment(table_lines[i], sep_alignments, &sep_cols);
|
|
1018
|
-
if (sep_cols > column_count) {
|
|
1019
|
-
column_count = sep_cols;
|
|
1020
|
-
/* Use alignments from the separator with most columns */
|
|
1021
|
-
memcpy(alignments, sep_alignments, sizeof(sep_alignments));
|
|
1022
|
-
}
|
|
1023
|
-
}
|
|
1024
|
-
}
|
|
1025
|
-
|
|
1026
|
-
/* If no separator found, try to infer from content rows */
|
|
1027
|
-
if (column_count == 0) {
|
|
1028
|
-
for (size_t i = 0; i < table_line_count; i++) {
|
|
1029
|
-
if (strchr(table_lines[i], '|') && !is_grid_table_separator(table_lines[i])) {
|
|
1030
|
-
size_t row_cols = count_cells_in_line(table_lines[i]);
|
|
1031
|
-
if (row_cols > column_count) {
|
|
1032
|
-
column_count = row_cols;
|
|
1033
|
-
}
|
|
1034
|
-
}
|
|
1035
|
-
}
|
|
1036
|
-
/* Set default alignments */
|
|
1037
|
-
for (size_t j = 0; j < column_count && j < MAX_COLUMNS; j++) {
|
|
1038
|
-
alignments[j] = 0; /* Default left */
|
|
1039
|
-
}
|
|
1946
|
+
char *table_output = NULL;
|
|
1947
|
+
if (grid_block_has_separator(table_lines, table_line_count) &&
|
|
1948
|
+
grid_block_has_content_rows(table_lines, table_line_count)) {
|
|
1949
|
+
table_output = convert_collected_grid_table(table_lines, table_line_count,
|
|
1950
|
+
embed_as_html);
|
|
1040
1951
|
}
|
|
1041
1952
|
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
alignments, column_count);
|
|
1953
|
+
if (table_output) {
|
|
1954
|
+
size_t pipe_len = strlen(table_output);
|
|
1045
1955
|
|
|
1046
|
-
if (pipe_table) {
|
|
1047
|
-
size_t pipe_len = strlen(pipe_table);
|
|
1048
|
-
|
|
1049
|
-
/* Ensure we have enough space */
|
|
1050
1956
|
if (remaining < pipe_len) {
|
|
1051
1957
|
size_t written = write - output;
|
|
1052
1958
|
cap = (written + pipe_len + 100) * 2;
|
|
@@ -1058,17 +1964,40 @@ char *apex_preprocess_grid_tables(const char *text) {
|
|
|
1058
1964
|
}
|
|
1059
1965
|
}
|
|
1060
1966
|
|
|
1061
|
-
/* Write the table (it already has blank lines at start and end) */
|
|
1062
1967
|
if (pipe_len <= remaining) {
|
|
1063
|
-
memcpy(write,
|
|
1968
|
+
memcpy(write, table_output, pipe_len);
|
|
1064
1969
|
write += pipe_len;
|
|
1065
1970
|
remaining -= pipe_len;
|
|
1066
1971
|
}
|
|
1067
1972
|
|
|
1068
|
-
free(
|
|
1973
|
+
free(table_output);
|
|
1974
|
+
} else {
|
|
1975
|
+
/* Not a valid grid or conversion failed: preserve source lines */
|
|
1976
|
+
for (size_t i = 0; i < table_line_count; i++) {
|
|
1977
|
+
if (!table_lines[i]) continue;
|
|
1978
|
+
size_t tl = strlen(table_lines[i]);
|
|
1979
|
+
if (remaining < tl + 2) {
|
|
1980
|
+
size_t written = write - output;
|
|
1981
|
+
cap = (written + tl + 100) * 2;
|
|
1982
|
+
char *new_output = realloc(output, cap);
|
|
1983
|
+
if (new_output) {
|
|
1984
|
+
output = new_output;
|
|
1985
|
+
write = output + written;
|
|
1986
|
+
remaining = cap - written;
|
|
1987
|
+
}
|
|
1988
|
+
}
|
|
1989
|
+
if (tl <= remaining) {
|
|
1990
|
+
memcpy(write, table_lines[i], tl);
|
|
1991
|
+
write += tl;
|
|
1992
|
+
remaining -= tl;
|
|
1993
|
+
}
|
|
1994
|
+
if (remaining > 0) {
|
|
1995
|
+
*write++ = '\n';
|
|
1996
|
+
remaining--;
|
|
1997
|
+
}
|
|
1998
|
+
}
|
|
1069
1999
|
}
|
|
1070
2000
|
|
|
1071
|
-
/* Free table lines */
|
|
1072
2001
|
for (size_t i = 0; i < table_line_count; i++) {
|
|
1073
2002
|
if (table_lines[i]) free(table_lines[i]);
|
|
1074
2003
|
}
|
|
@@ -1076,9 +2005,6 @@ char *apex_preprocess_grid_tables(const char *text) {
|
|
|
1076
2005
|
|
|
1077
2006
|
free(table_lines);
|
|
1078
2007
|
|
|
1079
|
-
/* read is already pointing past all the table lines we just processed
|
|
1080
|
-
* (it was updated in the loop at line 1009) */
|
|
1081
|
-
/* Skip the blank line if present */
|
|
1082
2008
|
if (*read == '\n') {
|
|
1083
2009
|
read++;
|
|
1084
2010
|
}
|
|
@@ -1095,7 +2021,6 @@ char *apex_preprocess_grid_tables(const char *text) {
|
|
|
1095
2021
|
remaining--;
|
|
1096
2022
|
}
|
|
1097
2023
|
} else {
|
|
1098
|
-
/* Need to reallocate */
|
|
1099
2024
|
size_t written = write - output;
|
|
1100
2025
|
cap = (written + line_len + 1) * 2;
|
|
1101
2026
|
char *new_output = realloc(output, cap);
|
|
@@ -1119,3 +2044,7 @@ char *apex_preprocess_grid_tables(const char *text) {
|
|
|
1119
2044
|
*write = '\0';
|
|
1120
2045
|
return output;
|
|
1121
2046
|
}
|
|
2047
|
+
|
|
2048
|
+
char *apex_preprocess_grid_tables(const char *text) {
|
|
2049
|
+
return preprocess_grid_text(text, false);
|
|
2050
|
+
}
|