. "$BST_LIB" # MD4C 0.5.2 is a SAX-like Markdown parser in C: md_parse() walks the document # and calls back into the caller for every block, span and run of text, handing # each one back as a (pointer, length) slice INTO the caller's own buffer. It # ships here as two static archives (libmd4c.a, libmd4c-html.a), their headers # and pkg-config files, and the md2html(1) CLI that links them. # # It is the SECOND Markdown implementation on this image, beside cmark (76), and # the two are deliberately numbered together. They are not redundant: cmark is # the CommonMark reference implementation and renders a document tree, while # MD4C never builds a tree at all -- it is a streaming parser whose entire output # is indirect calls through a caller-supplied struct of seven function pointers. # That difference is the reason this suite is shaped the way it is. # # --------------------------------------------------------------------------- # WHY THIS SUITE RUNS A PROBE AS WELL AS THE CLI # # md2html exercises the library end to end and is asserted directly below, but # it only ever passes MD4C *its own* renderer. Two properties that matter on this # arm are invisible from the CLI: # # 1. THE SLICES. Every text run MD4C reports is a pointer into the input plus # a length that MD4C computed. BRINTOS-217 is the precedent: oniguruma # compiled and packaged perfectly on this arm and then returned silently # wrong offsets to every caller. A wrong slice here is not a crash -- it is # quietly wrong output -- and md2html would render it without complaint. # 2. THE CALLBACKS. MD_PARSER is seven stored function pointers. On this # coroutinized arm those are reached through a table: the build log shows # `FPCAST-DYN-MARSHAL fn=md_parse site_sig=Fvii arms=3`, i.e. md_parse's # indirect call sites are dynamically marshalled with an open-world # fallback that aborts LOUDLY on an unresolved target. Whether every arm # dispatches, and whether values come back OUT of them, is exactly what a # caller-supplied renderer measures and md2html does not. # # So data/md4c-probe is cross-linked with wasmcc-glibc against THE VERY ARCHIVES # THAT SHIP (/usr/lib/libmd4c.a, /usr/lib/libmd4c-html.a), staged to $TMPDIR and # run here. Its source is src/md4c_probe.c. EVERY EXPECTED VALUE IN IT IS THE # VALUE A NATIVE x86_64 BUILD OF THE IDENTICAL SOURCE PRODUCES: the probe scored # 186/186 natively before this suite was written, and getting it there corrected # 18 wrong expectations of mine -- so a red below is a platform difference, not a # guess of mine. The probe compares internally and prints its own verdict, and on # a failure prints a DIAG line carrying what it actually got. # # --------------------------------------------------------------------------- # TWO THINGS THE NATIVE CONTROL SETTLED, RECORDED HERE BECAUSE THEY LOOK LIKE # TARGET BUGS AND ARE NOT # # a) MD4C DOES NOT HONOUR ITS OWN ABORT CONTRACT. md4c.h promises that any # rendering callback may abort the parse by returning non-zero and that # md_parse() then returns that value. Measured, on x86_64: only leave_block # keeps the promise. enter_block and enter_span stop the walk but md_parse # still returns 0, and the text callback's return is ignored outright. That # is upstream's defect; the probe pins all four to what they ACTUALLY do so # a change in either direction is caught, and leave_block is the case that # carries the platform signal (a value surviving an indirect call). # # b) SOME SLICES LEGITIMATELY POINT OUTSIDE THE INPUT. MD4C emits line # terminators from the static literal _T("\n") (md4c.c:4472, 4486, 4738) and # the NULLCHAR placeholder from _T(""), so a size-1 "\n"/"\0" is not a slice # of the caller's buffer. The probe counts those separately rather than # exempting a whole text type, which makes the range check SHARPER: every # other byte MD4C reports must lie inside the input. # # --------------------------------------------------------------------------- # The distro is static-only, so there is no libmd4c.so and none is expected. # Unlike libconfuse (73) the main package is NOT empty here -- md2html gives it # real payload -- so all three of md4c, md4c-dev and md4c-staticdev are named in # the image. require md2html md4c/present tmp=${TMPDIR:-/tmp}/bst-md4c.$$ mkdir -p "$tmp" 2>/dev/null # --------------------------------------------------------------- payload shape check md4c/lib-parser test -f /usr/lib/libmd4c.a check md4c/lib-html test -f /usr/lib/libmd4c-html.a check md4c/hdr-parser test -f /usr/include/md4c.h check md4c/hdr-html test -f /usr/include/md4c-html.h check md4c/pc-parser test -f /usr/lib/pkgconfig/md4c.pc check md4c/pc-html test -f /usr/lib/pkgconfig/md4c-html.pc check md4c/cmake-config test -f /usr/lib/cmake/md4c/md4cConfig.cmake check md4c/bin-present test -f /usr/bin/md2html check md4c/bin-executable test -x /usr/bin/md2html # Static-only distro: a shared object here would mean the bbappend's # BUILD_SHARED_LIBS=OFF stopped taking effect. check_rc md4c/no-shared 1 test -e /usr/lib/libmd4c.so # The archives must carry the object files, not just exist. libmd4c-html.a is the # one that carries entity.c -- the ~2100-entry HTML entity table the renderer # searches -- so its absence would be invisible until an &entity; came out wrong. # Read the member names straight out of the ar header rather than unpacking: this # image ships no binutils ar (the first boot scored these rc=127), and a member # name short enough for ar's 16-byte name field is stored as plain text, so a # binary-mode grep sees it. check_out md4c/ar-parser "md4c.c.o" grep -a -o 'md4c\.c\.o' /usr/lib/libmd4c.a check_out md4c/ar-html "md4c-html.c.o" grep -a -o 'md4c-html\.c\.o' /usr/lib/libmd4c-html.a check_out md4c/ar-entity "entity.c.o" grep -a -o 'entity\.c\.o' /usr/lib/libmd4c-html.a # pkg-config text must describe the TARGET layout, not a build host's. Only # prefix is written absolute; libdir stays the unexpanded ${exec_prefix}/lib, so # the RESOLVED value is what has to be asserted -- ask pkg-config to expand it # rather than grepping the file for a literal that is not in it. check_out md4c/pc-prefix "prefix=/usr" cat /usr/lib/pkgconfig/md4c.pc check_out md4c/pc-libdir "/usr/lib" pkg-config --variable=libdir md4c check_out md4c/pc-lname "-lmd4c" cat /usr/lib/pkgconfig/md4c.pc check_out md4c/pc-html-lname "-lmd4c-html" cat /usr/lib/pkgconfig/md4c-html.pc # --------------------------------------------------------------- md2html(1) check_out md4c/cli-version "0.5.2" md2html --version check_out md4c/cli-help "--github" md2html --help # Core rendering, from a file and from stdin. These are CommonMark, and they are # the same bytes the native control emits. printf '# Title\n\nHello *world*.\n' > "$tmp/a.md" check_out md4c/cli-h1 "

Title

" md2html "$tmp/a.md" check_out md4c/cli-em "

Hello world.

" md2html "$tmp/a.md" out=$(printf '**b**\n' | md2html 2>&1) check_eq md4c/cli-stdin "

b

" "$out" # -o writes a file rather than stdout. printf 'x\n' > "$tmp/b.md" md2html -o "$tmp/b.html" "$tmp/b.md" 2>/dev/null check md4c/cli-o-created test -s "$tmp/b.html" check_eq md4c/cli-o-content "

x

" "$(cat "$tmp/b.html" 2>/dev/null)" # --full-html wraps the body; --xhtml self-closes void elements. check_out md4c/cli-full-doctype "" md2html --full-html "$tmp/b.md" check_out md4c/cli-full-body "" md2html --full-html "$tmp/b.md" check_out md4c/cli-html-title "MyTitle" md2html --full-html --html-title=MyTitle "$tmp/b.md" printf -- '---\n' > "$tmp/hr.md" check_eq md4c/cli-hr "
" "$(md2html "$tmp/hr.md" 2>&1)" check_eq md4c/cli-hr-xhtml "
" "$(md2html --xhtml "$tmp/hr.md" 2>&1)" # Extension flags: each must be OFF by default and ON when asked for. This pair # shape is what catches a flag that is wired to the wrong MD_FLAG_ bit. printf '|a|\n|-|\n|b|\n' > "$tmp/t.md" check_rc md4c/cli-table-off 1 sh -c 'md2html '"$tmp"'/t.md | grep -q ""' check_out md4c/cli-table-on "
" md2html --ftables "$tmp/t.md" printf '~~s~~\n' > "$tmp/s.md" check_eq md4c/cli-strike-off "

~~s~~

" "$(md2html "$tmp/s.md" 2>&1)" check_eq md4c/cli-strike-on "

s

" "$(md2html --fstrikethrough "$tmp/s.md" 2>&1)" printf 'www.e.com\n' > "$tmp/w.md" check_rc md4c/cli-www-off 1 sh -c 'md2html '"$tmp"'/w.md | grep -q " "$tmp/k.md" check_out md4c/cli-tasklists 'type="checkbox"' md2html --ftasklists "$tmp/k.md" printf '$e$\n' > "$tmp/m.md" check_out md4c/cli-latex '' md2html --flatex-math "$tmp/m.md" printf '[[w]]\n' > "$tmp/wl.md" check_out md4c/cli-wikilinks '` filtering. Asserted in both # directions so the difference between the two ports stays deliberate. printf '
x
\n' > "$tmp/h.md" check_eq md4c/cli-raw-html-kept "
x
" "$(md2html "$tmp/h.md" 2>&1)" check_rc md4c/cli-raw-html-dropped 1 sh -c 'md2html --fno-html '"$tmp"'/h.md | grep -q "
"' # Entities resolve through entity.c's table; --fverbatim-entities turns that off. printf '©\n' > "$tmp/e.md" check_out md4c/cli-entity "$(printf '\302\251')" md2html "$tmp/e.md" check_out md4c/cli-entity-verbatim "©" md2html --fverbatim-entities "$tmp/e.md" # UTF-8 must survive byte-for-byte: md4c.c is compiled -DMD4C_USE_UTF8 and its # decoder walks the input a byte at a time, which is the pointer arithmetic most # likely to differ on this arm. printf 'caf\303\251 \346\227\245\346\234\254\n' > "$tmp/u.md" check_out md4c/cli-utf8 "$(printf 'caf\303\251 \346\227\245\346\234\254')" md2html "$tmp/u.md" # A missing input file is an error, not a silent empty render. check_rc md4c/cli-missing-file 1 md2html "$tmp/does-not-exist.md" # A large document: exercises the block-array realloc path (md4c.c moves its # block array as it grows, then fixes up ->current_block) rather than any single # construct. 2000 headings in, 2000

out. i=0 : > "$tmp/big.md" while [ "$i" -lt 2000 ]; do printf '# H%s\n\n' "$i" >> "$tmp/big.md"; i=$((i+1)); done n=$(md2html "$tmp/big.md" 2>/dev/null | grep -c '^

') check_eq md4c/cli-big-headings 2000 "$n" # ------------------------------------------------------- the consumer probe # The recipe installs everything under data/ with `install -m 0644`, so the # payload is NOT executable where it lands: stage it to $TMPDIR and chmod +x, # the same way every sibling probe suite does. The deployed rootfs is read-only # at browser runtime, which is the other reason it is copied before it runs. # # Do NOT guard this on [ -x "$BST_DATA/md4c-probe" ]. That is false on a # correctly-built image, and worse, `test -x` on a mode-0644 file aborts the # whole script here with `sh: getgroups: Function not implemented` # (brintos/linux#19) -- which truncated this suite at exactly this line and # still let it report a green pass=46 fail=0 skip=0 with every runtime # assertion below silently unrun. if ! cp "$BST_DATA/md4c-probe" "$tmp/md4c-probe" 2>/dev/null \ || ! chmod +x "$tmp/md4c-probe" 2>/dev/null; then bst_skip md4c/probe "md4c-probe payload missing or not stageable" else pout=$("$tmp/md4c-probe" 2>&1); prc=$? # The probe's own tally. 186/186 is the native control's score, so anything # less names a real difference on this arm -- and the FAIL/DIAG lines in the # console dump say which case and what it got. psum=$(printf '%s\n' "$pout" | sed -n 's/^MD4C-SUMMARY //p') check_eq md4c/probe-summary "186/186" "$psum" check_eq md4c/probe-exit "0" "$prc" # A handful of the probe's groups are re-asserted individually, so a red run # localises to a mechanism from the summary line alone rather than needing # the whole dump read. nfail=$(printf '%s\n' "$pout" | grep -c '^MD4C FAIL' || true) check_eq md4c/probe-no-failures "0" "$nfail" for g in blk spn flg det off utf ent html abt; do gf=$(printf '%s\n' "$pout" | grep -c "^MD4C FAIL $g" || true) check_eq "md4c/probe-group-$g" "0" "$gf" done # The two mechanisms this port exists to prove, called out by name: # the slices stay inside the caller's buffer (BRINTOS-217), and # a value survives the round trip out of an indirect call. check_out md4c/probe-slices-in-range "MD4C ok off-mixed-in-range" \ printf '%s\n' "$pout" check_out md4c/probe-indirect-return "MD4C ok abt-leave-block-propagates" \ printf '%s\n' "$pout" fi rm -rf "$tmp"