git subrepo clone --branch=0.30.3 https://github.com/commonmark/cmark.git deps/cmark
subrepo: subdir: "deps/cmark" merged: "5ba25ff" upstream: origin: "https://github.com/commonmark/cmark.git" branch: "0.30.3" commit: "5ba25ff" git-subrepo: version: "0.4.6" commit: "d4444b563"
This commit is contained in:
parent
17a4224cb8
commit
24810cbbbd
18
deps/cmark/.editorconfig
vendored
Normal file
18
deps/cmark/.editorconfig
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
# editorconfig.org
|
||||
|
||||
root = true
|
||||
|
||||
[*]
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
insert_final_newline = true
|
||||
|
||||
[*.{c,h}]
|
||||
trim_trailing_whitespace = true
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
[Makefile]
|
||||
trim_trailing_whitespace = true
|
||||
indent_style = tab
|
||||
indent_size = 8
|
1
deps/cmark/.gitattributes
vendored
Normal file
1
deps/cmark/.gitattributes
vendored
Normal file
@ -0,0 +1 @@
|
||||
* text=auto eol=lf
|
1
deps/cmark/.github/FUNDING.yml
vendored
Normal file
1
deps/cmark/.github/FUNDING.yml
vendored
Normal file
@ -0,0 +1 @@
|
||||
github: [jgm]
|
102
deps/cmark/.github/workflows/ci.yml
vendored
Normal file
102
deps/cmark/.github/workflows/ci.yml
vendored
Normal file
@ -0,0 +1,102 @@
|
||||
name: CI tests
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
|
||||
linter:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
|
||||
- uses: actions/checkout@v1
|
||||
- name: Install clang-tidy
|
||||
run: |
|
||||
sudo apt-get install -y clang-tidy-11
|
||||
sudo update-alternatives --install /usr/bin/clang-tidy clang-tidy /usr/bin/clang-tidy-11 100
|
||||
- name: lint with clang-tidy
|
||||
run: |
|
||||
make lint
|
||||
env:
|
||||
CC: clang
|
||||
CXX: clang++
|
||||
|
||||
linux:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
cmake_opts:
|
||||
- '-DCMARK_SHARED=ON'
|
||||
- ''
|
||||
compiler:
|
||||
- c: 'clang'
|
||||
cpp: 'clang++'
|
||||
- c: 'gcc'
|
||||
cpp: 'g++'
|
||||
env:
|
||||
CMAKE_OPTIONS: ${{ matrix.cmake_opts }}
|
||||
CC: ${{ matrix.compiler.c }}
|
||||
CXX: ${{ matrix.compiler.cpp }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Install valgrind
|
||||
run: |
|
||||
sudo apt install -y valgrind
|
||||
- name: Build and test
|
||||
run: |
|
||||
make
|
||||
make test
|
||||
make leakcheck
|
||||
|
||||
macos:
|
||||
|
||||
runs-on: macOS-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
cmake_opts:
|
||||
- '-DCMARK_SHARED=ON'
|
||||
- ''
|
||||
compiler:
|
||||
- c: 'clang'
|
||||
cpp: 'clang++'
|
||||
- c: 'gcc'
|
||||
cpp: 'g++'
|
||||
env:
|
||||
CMAKE_OPTIONS: ${{ matrix.cmake_opts }}
|
||||
CC: ${{ matrix.compiler.c }}
|
||||
CXX: ${{ matrix.compiler.cpp }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Build and test
|
||||
env:
|
||||
CMAKE_OPTIONS: -DCMARK_SHARED=OFF
|
||||
run: |
|
||||
make
|
||||
make test
|
||||
|
||||
windows:
|
||||
|
||||
runs-on: windows-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
cmake_opts:
|
||||
- '-DCMARK_SHARED=ON'
|
||||
- ''
|
||||
env:
|
||||
CMAKE_OPTIONS: ${{ matrix.cmake_opts }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: ilammy/msvc-dev-cmd@v1
|
||||
- name: Build and test
|
||||
run: |
|
||||
chcp 65001
|
||||
nmake.exe /nologo /f Makefile.nmake test
|
||||
shell: cmd
|
23
deps/cmark/.github/workflows/fuzz.yml
vendored
Normal file
23
deps/cmark/.github/workflows/fuzz.yml
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
name: CIFuzz
|
||||
on: [pull_request]
|
||||
jobs:
|
||||
Fuzzing:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Build Fuzzers
|
||||
uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master
|
||||
with:
|
||||
oss-fuzz-project-name: 'cmark'
|
||||
dry-run: false
|
||||
- name: Run Fuzzers
|
||||
uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master
|
||||
with:
|
||||
oss-fuzz-project-name: 'cmark'
|
||||
fuzz-seconds: 600
|
||||
dry-run: false
|
||||
- name: Upload Crash
|
||||
uses: actions/upload-artifact@v1
|
||||
if: failure()
|
||||
with:
|
||||
name: artifacts
|
||||
path: ./out/artifacts
|
34
deps/cmark/.gitignore
vendored
Normal file
34
deps/cmark/.gitignore
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
# Object files
|
||||
*.o
|
||||
*.ko
|
||||
*.obj
|
||||
*.elf
|
||||
|
||||
# Libraries
|
||||
*.lib
|
||||
*.a
|
||||
|
||||
# Shared objects (inc. Windows DLLs)
|
||||
*.dll
|
||||
*.so
|
||||
*.so.*
|
||||
*.dylib
|
||||
|
||||
# Executables
|
||||
*.exe
|
||||
*.out
|
||||
*.app
|
||||
*.i*86
|
||||
*.x86_64
|
||||
*.hex
|
||||
*.pyc
|
||||
|
||||
*~
|
||||
*.bak
|
||||
*.diff
|
||||
*#
|
||||
*.zip
|
||||
bstrlib.txt
|
||||
build
|
||||
cmark.dSYM/*
|
||||
cmark
|
12
deps/cmark/.gitrepo
vendored
Normal file
12
deps/cmark/.gitrepo
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
; DO NOT EDIT (unless you know what you are doing)
|
||||
;
|
||||
; This subdirectory is a git "subrepo", and this file is maintained by the
|
||||
; git-subrepo command. See https://github.com/ingydotnet/git-subrepo#readme
|
||||
;
|
||||
[subrepo]
|
||||
remote = https://github.com/commonmark/cmark.git
|
||||
branch = 0.30.3
|
||||
commit = 5ba25ff40eba44c811f79ab6a792baf945b8307c
|
||||
parent = bb99771ac83ea85da0e1ab2c6c0c3475a7d21726
|
||||
method = merge
|
||||
cmdver = 0.4.6
|
43
deps/cmark/.travis.yml
vendored
Normal file
43
deps/cmark/.travis.yml
vendored
Normal file
@ -0,0 +1,43 @@
|
||||
# Ensures that sudo is disabled, so that containerized builds are allowed
|
||||
arch:
|
||||
- amd64
|
||||
- ppc64le
|
||||
sudo: false
|
||||
|
||||
os:
|
||||
- linux
|
||||
- osx
|
||||
language: c
|
||||
compiler:
|
||||
- clang
|
||||
- gcc
|
||||
matrix:
|
||||
include:
|
||||
- os: linux
|
||||
compiler: gcc
|
||||
env: CMAKE_OPTIONS="-DCMARK_SHARED=OFF"
|
||||
addons:
|
||||
apt:
|
||||
# we need a more recent cmake than travis/linux provides (at least 2.8.9):
|
||||
sources:
|
||||
- kubuntu-backports
|
||||
- kalakris-cmake
|
||||
packages:
|
||||
- cmake
|
||||
- python3
|
||||
- valgrind
|
||||
before_install:
|
||||
- |
|
||||
if [ ${TRAVIS_OS_NAME:-'linux'} = 'osx' ]
|
||||
then
|
||||
echo "Building without python3, to make sure that works."
|
||||
fi
|
||||
|
||||
script:
|
||||
- (mkdir -p build && cd build && cmake $CMAKE_OPTIONS ..)
|
||||
- make test
|
||||
- |
|
||||
if [ ${TRAVIS_OS_NAME:-'linux'} = 'linux' ]
|
||||
then
|
||||
make leakcheck
|
||||
fi
|
79
deps/cmark/CMakeLists.txt
vendored
Normal file
79
deps/cmark/CMakeLists.txt
vendored
Normal file
@ -0,0 +1,79 @@
|
||||
cmake_minimum_required(VERSION 3.7)
|
||||
project(cmark VERSION 0.30.3)
|
||||
|
||||
include("FindAsan.cmake")
|
||||
include(GNUInstallDirs)
|
||||
|
||||
if("${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_BINARY_DIR}")
|
||||
message(FATAL_ERROR "Do not build in-source.\nPlease remove CMakeCache.txt and the CMakeFiles/ directory.\nThen: mkdir build ; cd build ; cmake .. ; make")
|
||||
endif()
|
||||
|
||||
option(CMARK_TESTS "Build cmark tests and enable testing" ON)
|
||||
option(CMARK_STATIC "Build static libcmark library" ON)
|
||||
option(CMARK_SHARED "Build shared libcmark library" ON)
|
||||
option(CMARK_LIB_FUZZER "Build libFuzzer fuzzing harness" OFF)
|
||||
|
||||
if(NOT MSVC)
|
||||
set(CMAKE_C_STANDARD 99)
|
||||
set(CMAKE_C_STANDARD_REQUIRED YES)
|
||||
set(CMAKE_C_EXTENSIONS NO)
|
||||
endif()
|
||||
|
||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||
|
||||
# The Linux modules distributed with CMake add "-rdynamic" to the build flags
|
||||
# which is incompatible with static linking under certain configurations.
|
||||
# Unsetting CMAKE_SHARED_LIBRARY_LINK_C_FLAGS ensures this does not happen.
|
||||
if(CMARK_STATIC AND "${CMAKE_SYSTEM_NAME}" STREQUAL "Linux")
|
||||
SET(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS)
|
||||
endif()
|
||||
|
||||
# Check integrity of node structure when compiled as debug
|
||||
add_compile_options($<$<CONFIG:Debug>:-DCMARK_DEBUG_NODES>)
|
||||
|
||||
# In order to maintain compatibility with older platforms which may not have a
|
||||
# recent version of CMake (i.e. are running CMake <3.3), we cannot simply use
|
||||
# the `add_compile_options` with a generator expression. This uses the
|
||||
# `target_compile_options` with `PRIVATE` to add the flags only to the targets
|
||||
# so that CMark may be used in projects with non-C languages.
|
||||
function(cmark_add_compile_options target)
|
||||
if(MSVC)
|
||||
target_compile_options(${target} PRIVATE /W4 /wd4706)
|
||||
if(MSVC_VERSION LESS 1800)
|
||||
target_compile_options(${target} PRIVATE /TP)
|
||||
endif()
|
||||
target_compile_definitions(${target} PRIVATE _CRT_SECURE_NO_WARNINGS)
|
||||
else()
|
||||
target_compile_options(${target} PRIVATE
|
||||
-Wall -Wextra -pedantic
|
||||
$<$<COMPILE_LANGUAGE:C>:-Wstrict-prototypes>)
|
||||
endif()
|
||||
if(CMAKE_BUILD_TYPE MATCHES profile)
|
||||
target_compile_options(${target} PRIVATE -pg)
|
||||
endif()
|
||||
if(CMAKE_BUILD_TYPE MATCHES ubsan)
|
||||
target_compile_options(${target} PRIVATE -fsanitize=undefined)
|
||||
endif()
|
||||
if(CMARK_LIB_FUZZER)
|
||||
target_compile_options(${target} PRIVATE -fsanitize-coverage=trace-pc-guard)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
add_subdirectory(src)
|
||||
if(CMARK_TESTS AND (CMARK_SHARED OR CMARK_STATIC))
|
||||
add_subdirectory(api_test)
|
||||
endif()
|
||||
# TODO(compnerd) should this be enabled for MinGW, which sets CMAKE_SYSTEM_NAME
|
||||
# to Windows, but defines `MINGW`.
|
||||
if(NOT CMAKE_SYSTEM_NAME STREQUAL Windows)
|
||||
add_subdirectory(man)
|
||||
endif()
|
||||
if(CMARK_TESTS)
|
||||
enable_testing()
|
||||
add_subdirectory(test testdir)
|
||||
endif()
|
||||
|
||||
if(NOT CMAKE_BUILD_TYPE)
|
||||
set(CMAKE_BUILD_TYPE "Release" CACHE STRING
|
||||
"Choose the type of build, options are: Debug Profile Release Asan Ubsan." FORCE)
|
||||
endif(NOT CMAKE_BUILD_TYPE)
|
170
deps/cmark/COPYING
vendored
Normal file
170
deps/cmark/COPYING
vendored
Normal file
@ -0,0 +1,170 @@
|
||||
Copyright (c) 2014, John MacFarlane
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer in the documentation and/or other materials provided
|
||||
with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
-----
|
||||
|
||||
houdini.h, houdini_href_e.c, houdini_html_e.c, houdini_html_u.c
|
||||
|
||||
derive from https://github.com/vmg/houdini (with some modifications)
|
||||
|
||||
Copyright (C) 2012 Vicent Martí
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
-----
|
||||
|
||||
buffer.h, buffer.c, chunk.h
|
||||
|
||||
are derived from code (C) 2012 Github, Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
-----
|
||||
|
||||
utf8.c and utf8.c
|
||||
|
||||
are derived from utf8proc
|
||||
(<http://www.public-software-group.org/utf8proc>),
|
||||
(C) 2009 Public Software Group e. V., Berlin, Germany.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
-----
|
||||
|
||||
The normalization code in normalize.py was derived from the
|
||||
markdowntest project, Copyright 2013 Karl Dubost:
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2013 Karl Dubost
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
-----
|
||||
|
||||
The CommonMark spec (test/spec.txt) is
|
||||
|
||||
Copyright (C) 2014-15 John MacFarlane
|
||||
|
||||
Released under the Creative Commons CC-BY-SA 4.0 license:
|
||||
<http://creativecommons.org/licenses/by-sa/4.0/>.
|
||||
|
||||
-----
|
||||
|
||||
The test software in test/ is
|
||||
|
||||
Copyright (c) 2014, John MacFarlane
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer in the documentation and/or other materials provided
|
||||
with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
74
deps/cmark/FindAsan.cmake
vendored
Normal file
74
deps/cmark/FindAsan.cmake
vendored
Normal file
@ -0,0 +1,74 @@
|
||||
#
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) 2013 Matthew Arsenault
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
#
|
||||
# This module tests if address sanitizer is supported by the compiler,
|
||||
# and creates a ASan build type (i.e. set CMAKE_BUILD_TYPE=ASan to use
|
||||
# it). This sets the following variables:
|
||||
#
|
||||
# CMAKE_C_FLAGS_ASAN - Flags to use for C with asan
|
||||
# CMAKE_CXX_FLAGS_ASAN - Flags to use for C++ with asan
|
||||
# HAVE_ADDRESS_SANITIZER - True or false if the ASan build type is available
|
||||
|
||||
include(CheckCCompilerFlag)
|
||||
|
||||
# Set -Werror to catch "argument unused during compilation" warnings
|
||||
set(CMAKE_REQUIRED_FLAGS "-Werror -faddress-sanitizer") # Also needs to be a link flag for test to pass
|
||||
check_c_compiler_flag("-faddress-sanitizer" HAVE_FLAG_ADDRESS_SANITIZER)
|
||||
|
||||
set(CMAKE_REQUIRED_FLAGS "-Werror -fsanitize=address") # Also needs to be a link flag for test to pass
|
||||
check_c_compiler_flag("-fsanitize=address" HAVE_FLAG_SANITIZE_ADDRESS)
|
||||
|
||||
unset(CMAKE_REQUIRED_FLAGS)
|
||||
|
||||
if(HAVE_FLAG_SANITIZE_ADDRESS)
|
||||
# Clang 3.2+ use this version
|
||||
set(ADDRESS_SANITIZER_FLAG "-fsanitize=address")
|
||||
elseif(HAVE_FLAG_ADDRESS_SANITIZER)
|
||||
# Older deprecated flag for ASan
|
||||
set(ADDRESS_SANITIZER_FLAG "-faddress-sanitizer")
|
||||
endif()
|
||||
|
||||
if(NOT ADDRESS_SANITIZER_FLAG)
|
||||
return()
|
||||
else(NOT ADDRESS_SANITIZER_FLAG)
|
||||
set(HAVE_ADDRESS_SANITIZER FALSE)
|
||||
endif()
|
||||
|
||||
set(HAVE_ADDRESS_SANITIZER TRUE)
|
||||
|
||||
set(CMAKE_C_FLAGS_ASAN "-O1 -g ${ADDRESS_SANITIZER_FLAG} -fno-omit-frame-pointer -fno-optimize-sibling-calls"
|
||||
CACHE STRING "Flags used by the C compiler during ASan builds."
|
||||
FORCE)
|
||||
set(CMAKE_CXX_FLAGS_ASAN "-O1 -g ${ADDRESS_SANITIZER_FLAG} -fno-omit-frame-pointer -fno-optimize-sibling-calls"
|
||||
CACHE STRING "Flags used by the C++ compiler during ASan builds."
|
||||
FORCE)
|
||||
set(CMAKE_EXE_LINKER_FLAGS_ASAN "${ADDRESS_SANITIZER_FLAG}"
|
||||
CACHE STRING "Flags used for linking binaries during ASan builds."
|
||||
FORCE)
|
||||
set(CMAKE_SHARED_LINKER_FLAGS_ASAN "${ADDRESS_SANITIZER_FLAG}"
|
||||
CACHE STRING "Flags used by the shared libraries linker during ASan builds."
|
||||
FORCE)
|
||||
mark_as_advanced(CMAKE_C_FLAGS_ASAN
|
||||
CMAKE_CXX_FLAGS_ASAN
|
||||
CMAKE_EXE_LINKER_FLAGS_ASAN
|
||||
CMAKE_SHARED_LINKER_FLAGS_ASAN)
|
202
deps/cmark/Makefile
vendored
Normal file
202
deps/cmark/Makefile
vendored
Normal file
@ -0,0 +1,202 @@
|
||||
SRCDIR=src
|
||||
DATADIR=data
|
||||
BUILDDIR?=build
|
||||
GENERATOR?=Unix Makefiles
|
||||
MINGW_BUILDDIR?=build-mingw
|
||||
MINGW_INSTALLDIR?=windows
|
||||
SPEC=test/spec.txt
|
||||
SITE=_site
|
||||
SPECVERSION=$(shell perl -ne 'print $$1 if /^version: *([0-9.]+)/' $(SPEC))
|
||||
FUZZCHARS?=2000000 # for fuzztest
|
||||
BENCHDIR=bench
|
||||
BENCHSAMPLES=$(wildcard $(BENCHDIR)/samples/*.md)
|
||||
BENCHFILE=$(BENCHDIR)/benchinput.md
|
||||
ALLTESTS=alltests.md
|
||||
NUMRUNS?=10
|
||||
CMARK=$(BUILDDIR)/src/cmark
|
||||
CMARK_FUZZ=$(BUILDDIR)/src/cmark-fuzz
|
||||
PROG?=$(CMARK)
|
||||
VERSION?=$(SPECVERSION)
|
||||
RELEASE?=cmark-$(VERSION)
|
||||
INSTALL_PREFIX?=/usr/local
|
||||
CLANG_CHECK?=clang-check
|
||||
CLANG_FORMAT=clang-format -style llvm -sort-includes=0 -i
|
||||
AFL_PATH?=/usr/local/bin
|
||||
|
||||
.PHONY: all cmake_build leakcheck clean fuzztest test debug ubsan asan mingw archive newbench bench format update-spec afl libFuzzer lint
|
||||
|
||||
all: cmake_build man/man3/cmark.3
|
||||
|
||||
$(CMARK): cmake_build
|
||||
|
||||
cmake_build: $(BUILDDIR)
|
||||
@$(MAKE) -j2 -C $(BUILDDIR)
|
||||
@echo "Binaries can be found in $(BUILDDIR)/src"
|
||||
|
||||
$(BUILDDIR):
|
||||
@cmake --version > /dev/null || (echo "You need cmake to build this program: http://www.cmake.org/download/" && exit 1)
|
||||
mkdir -p $(BUILDDIR); \
|
||||
cd $(BUILDDIR); \
|
||||
cmake .. \
|
||||
-G "$(GENERATOR)" \
|
||||
-DCMAKE_BUILD_TYPE=$(BUILD_TYPE) \
|
||||
-DCMAKE_INSTALL_PREFIX=$(INSTALL_PREFIX) \
|
||||
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON
|
||||
|
||||
install: $(BUILDDIR)
|
||||
$(MAKE) -C $(BUILDDIR) install
|
||||
|
||||
uninstall: $(BUILDDIR)/install_manifest.txt
|
||||
xargs rm < $<
|
||||
|
||||
debug:
|
||||
mkdir -p $(BUILDDIR); \
|
||||
cd $(BUILDDIR); \
|
||||
cmake .. -DCMAKE_BUILD_TYPE=Debug; \
|
||||
$(MAKE)
|
||||
|
||||
ubsan:
|
||||
mkdir -p $(BUILDDIR); \
|
||||
cd $(BUILDDIR); \
|
||||
cmake .. -DCMAKE_BUILD_TYPE=Ubsan; \
|
||||
$(MAKE)
|
||||
|
||||
asan:
|
||||
mkdir -p $(BUILDDIR); \
|
||||
cd $(BUILDDIR); \
|
||||
cmake .. -DCMAKE_BUILD_TYPE=Asan; \
|
||||
$(MAKE)
|
||||
|
||||
prof:
|
||||
mkdir -p $(BUILDDIR); \
|
||||
cd $(BUILDDIR); \
|
||||
cmake .. -DCMAKE_BUILD_TYPE=Profile; \
|
||||
$(MAKE)
|
||||
|
||||
afl:
|
||||
@[ -n "$(AFL_PATH)" ] || { echo '$$AFL_PATH not set'; false; }
|
||||
mkdir -p $(BUILDDIR)
|
||||
cd $(BUILDDIR) && cmake .. -DCMARK_TESTS=0 -DCMAKE_C_COMPILER=$(AFL_PATH)/afl-clang
|
||||
$(MAKE)
|
||||
$(AFL_PATH)/afl-fuzz \
|
||||
-i test/afl_test_cases \
|
||||
-o test/afl_results \
|
||||
-x test/fuzzing_dictionary \
|
||||
-t 100 \
|
||||
$(CMARK) $(CMARK_OPTS)
|
||||
|
||||
libFuzzer:
|
||||
@[ -n "$(LIB_FUZZER_PATH)" ] || { echo '$$LIB_FUZZER_PATH not set'; false; }
|
||||
mkdir -p $(BUILDDIR)
|
||||
cd $(BUILDDIR) && cmake -DCMAKE_BUILD_TYPE=Asan -DCMARK_LIB_FUZZER=ON -DCMAKE_LIB_FUZZER_PATH=$(LIB_FUZZER_PATH) ..
|
||||
$(MAKE) -j2 -C $(BUILDDIR) cmark-fuzz
|
||||
test/run-cmark-fuzz $(CMARK_FUZZ)
|
||||
|
||||
lint: $(BUILDDIR)
|
||||
errs=0 ; \
|
||||
for f in `ls src/*.[ch] | grep -v "scanners.c"` ; \
|
||||
do echo $$f ; clang-tidy -header-filter='^build/.*' -p=build -warnings-as-errors='*' $$f || errs=1 ; done ; \
|
||||
exit $$errs
|
||||
|
||||
mingw:
|
||||
mkdir -p $(MINGW_BUILDDIR); \
|
||||
cd $(MINGW_BUILDDIR); \
|
||||
cmake .. -DCMAKE_TOOLCHAIN_FILE=../toolchain-mingw32.cmake -DCMAKE_INSTALL_PREFIX=$(MINGW_INSTALLDIR) ;\
|
||||
$(MAKE) && $(MAKE) install
|
||||
|
||||
man/man3/cmark.3: src/cmark.h | $(CMARK)
|
||||
python man/make_man_page.py $< > $@ \
|
||||
|
||||
archive:
|
||||
git archive --prefix=$(RELEASE)/ -o $(RELEASE).tar.gz HEAD
|
||||
git archive --prefix=$(RELEASE)/ -o $(RELEASE).zip HEAD
|
||||
|
||||
clean:
|
||||
rm -rf $(BUILDDIR) $(MINGW_BUILDDIR) $(MINGW_INSTALLDIR)
|
||||
|
||||
# We include case_fold_switch.inc in the repository, so this shouldn't
|
||||
# normally need to be generated.
|
||||
$(SRCDIR)/case_fold_switch.inc: $(DATADIR)/CaseFolding.txt
|
||||
perl tools/mkcasefold.pl < $< > $@
|
||||
|
||||
# We include scanners.c in the repository, so this shouldn't
|
||||
# normally need to be generated.
|
||||
$(SRCDIR)/scanners.c: $(SRCDIR)/scanners.re
|
||||
@case "$$(re2c -v)" in \
|
||||
*\ 0.13.*|*\ 0.14|*\ 0.14.1) \
|
||||
echo "re2c >= 0.14.2 is required"; \
|
||||
false; \
|
||||
;; \
|
||||
esac
|
||||
re2c -W -Werror --case-insensitive -b -i --no-generation-date -8 \
|
||||
--encoding-policy substitute -o $@ $<
|
||||
$(CLANG_FORMAT) $@
|
||||
|
||||
# We include entities.inc in the repository, so normally this
|
||||
# doesn't need to be regenerated:
|
||||
$(SRCDIR)/entities.inc: tools/make_entities_inc.py
|
||||
python3 $< > $@
|
||||
|
||||
update-spec:
|
||||
curl 'https://raw.githubusercontent.com/jgm/CommonMark/master/spec.txt'\
|
||||
> $(SPEC)
|
||||
|
||||
test: $(SPEC) cmake_build
|
||||
$(MAKE) -C $(BUILDDIR) test || (cat $(BUILDDIR)/Testing/Temporary/LastTest.log && exit 1)
|
||||
|
||||
$(ALLTESTS): $(SPEC)
|
||||
python3 test/spec_tests.py --spec $< --dump-tests | python3 -c 'import json; import sys; tests = json.loads(sys.stdin.read()); print("\n".join([test["markdown"] for test in tests]))' > $@
|
||||
|
||||
leakcheck: $(ALLTESTS)
|
||||
for format in html man xml latex commonmark; do \
|
||||
for opts in "" "--smart"; do \
|
||||
echo "cmark -t $$format $$opts" ; \
|
||||
valgrind -q --leak-check=full --dsymutil=yes --error-exitcode=1 $(PROG) -t $$format $$opts $(ALLTESTS) >/dev/null || exit 1;\
|
||||
done; \
|
||||
done;
|
||||
|
||||
fuzztest:
|
||||
{ for i in `seq 1 10`; do \
|
||||
cat /dev/urandom | head -c $(FUZZCHARS) | iconv -f latin1 -t utf-8 | tee fuzz-$$i.txt | \
|
||||
/usr/bin/env time -p $(PROG) >/dev/null && rm fuzz-$$i.txt ; \
|
||||
done } 2>&1 | grep 'user\|abnormally'
|
||||
|
||||
progit:
|
||||
git clone https://github.com/progit/progit.git
|
||||
|
||||
$(BENCHFILE): progit
|
||||
echo "" > $@
|
||||
for lang in ar az be ca cs de en eo es es-ni fa fi fr hi hu id it ja ko mk nl no-nb pl pt-br ro ru sr th tr uk vi zh zh-tw; do \
|
||||
cat progit/$$lang/*/*.markdown >> $@; \
|
||||
done
|
||||
|
||||
# for more accurate results, run with
|
||||
# sudo renice -10 $$; make bench
|
||||
bench: $(BENCHFILE)
|
||||
{ for x in `seq 1 $(NUMRUNS)` ; do \
|
||||
/usr/bin/env time -p $(PROG) </dev/null >/dev/null ; \
|
||||
/usr/bin/env time -p $(PROG) $< >/dev/null ; \
|
||||
done \
|
||||
} 2>&1 | grep 'real' | awk '{print $$2}' | python3 'bench/stats.py'
|
||||
|
||||
newbench:
|
||||
for f in $(BENCHSAMPLES) ; do \
|
||||
printf "%26s " `basename $$f` ; \
|
||||
{ for x in `seq 1 $(NUMRUNS)` ; do \
|
||||
/usr/bin/env time -p $(PROG) </dev/null >/dev/null ; \
|
||||
for x in `seq 1 200` ; do cat $$f ; done | \
|
||||
/usr/bin/env time -p $(PROG) > /dev/null; \
|
||||
done \
|
||||
} 2>&1 | grep 'real' | awk '{print $$2}' | \
|
||||
python3 'bench/stats.py'; done
|
||||
|
||||
format:
|
||||
$(CLANG_FORMAT) src/*.c src/*.h api_test/*.c api_test/*.h
|
||||
|
||||
operf: $(CMARK)
|
||||
operf $< < $(BENCHFILE) > /dev/null
|
||||
|
||||
distclean: clean
|
||||
-rm -rf *.dSYM
|
||||
-rm -f README.html
|
||||
-rm -rf $(BENCHFILE) $(ALLTESTS) progit
|
36
deps/cmark/Makefile.nmake
vendored
Normal file
36
deps/cmark/Makefile.nmake
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
SRCDIR=src
|
||||
DATADIR=data
|
||||
BUILDDIR=build
|
||||
INSTALLDIR=windows
|
||||
SPEC=test/spec.txt
|
||||
PROG=$(BUILDDIR)\src\cmark.exe
|
||||
GENERATOR=NMake Makefiles
|
||||
|
||||
all: $(BUILDDIR)/CMakeFiles
|
||||
@cd $(BUILDDIR) && $(MAKE) /nologo && cd ..
|
||||
|
||||
$(BUILDDIR)/CMakeFiles:
|
||||
@-mkdir $(BUILDDIR) 2> nul
|
||||
cd $(BUILDDIR) && \
|
||||
cmake \
|
||||
-G "$(GENERATOR)" \
|
||||
-D CMAKE_BUILD_TYPE=$(BUILD_TYPE) \
|
||||
-D CMAKE_INSTALL_PREFIX=$(INSTALLDIR) \
|
||||
.. && \
|
||||
cd ..
|
||||
|
||||
install: all
|
||||
@cd $(BUILDDIR) && $(MAKE) /nologo install && cd ..
|
||||
|
||||
clean:
|
||||
-rmdir /s /q $(BUILDDIR) $(MINGW_INSTALLDIR) 2> nul
|
||||
|
||||
$(SRCDIR)\case_fold_switch.inc: $(DATADIR)\CaseFolding-3.2.0.txt
|
||||
perl mkcasefold.pl < $? > $@
|
||||
|
||||
test: $(SPEC) all
|
||||
@cd $(BUILDDIR) && $(MAKE) /nologo test ARGS="-V" && cd ..
|
||||
|
||||
distclean: clean
|
||||
del /q src\scanners.c 2> nul
|
||||
del /q spec.md spec.html 2> nul
|
198
deps/cmark/README.md
vendored
Normal file
198
deps/cmark/README.md
vendored
Normal file
@ -0,0 +1,198 @@
|
||||
cmark
|
||||
=====
|
||||
|
||||
[](https://github.com/commonmark/cmark/actions)
|
||||
|
||||
`cmark` is the C reference implementation of [CommonMark], a
|
||||
rationalized version of Markdown syntax with a [spec][the spec].
|
||||
(For the JavaScript reference implementation, see
|
||||
[commonmark.js].)
|
||||
|
||||
It provides a shared library (`libcmark`) with functions for parsing
|
||||
CommonMark documents to an abstract syntax tree (AST), manipulating
|
||||
the AST, and rendering the document to HTML, groff man, LaTeX,
|
||||
CommonMark, or an XML representation of the AST. It also provides a
|
||||
command-line program (`cmark`) for parsing and rendering CommonMark
|
||||
documents.
|
||||
|
||||
Advantages of this library:
|
||||
|
||||
- **Portable.** The library and program are written in standard
|
||||
C99 and have no external dependencies. They have been tested with
|
||||
MSVC, gcc, tcc, and clang.
|
||||
|
||||
- **Fast.** cmark can render a Markdown version of *War and Peace* in
|
||||
the blink of an eye (127 milliseconds on a ten year old laptop,
|
||||
vs. 100-400 milliseconds for an eye blink). In our [benchmarks],
|
||||
cmark is 10,000 times faster than the original `Markdown.pl`, and
|
||||
on par with the very fastest available Markdown processors.
|
||||
|
||||
- **Accurate.** The library passes all CommonMark conformance tests.
|
||||
|
||||
- **Standardized.** The library can be expected to parse CommonMark
|
||||
the same way as any other conforming parser. So, for example,
|
||||
you can use `commonmark.js` on the client to preview content that
|
||||
will be rendered on the server using `cmark`.
|
||||
|
||||
- **Robust.** The library has been extensively fuzz-tested using
|
||||
[american fuzzy lop]. The test suite includes pathological cases
|
||||
that bring many other Markdown parsers to a crawl (for example,
|
||||
thousands-deep nested bracketed text or block quotes).
|
||||
|
||||
- **Flexible.** CommonMark input is parsed to an AST which can be
|
||||
manipulated programmatically prior to rendering.
|
||||
|
||||
- **Multiple renderers.** Output in HTML, groff man, LaTeX, CommonMark,
|
||||
and a custom XML format is supported. And it is easy to write new
|
||||
renderers to support other formats.
|
||||
|
||||
- **Free.** BSD2-licensed.
|
||||
|
||||
It is easy to use `libcmark` in python, lua, ruby, and other dynamic
|
||||
languages: see the `wrappers/` subdirectory for some simple examples.
|
||||
|
||||
There are also libraries that wrap `libcmark` for
|
||||
[Go](https://github.com/rhinoman/go-commonmark),
|
||||
[Haskell](https://hackage.haskell.org/package/cmark),
|
||||
[Ruby](https://github.com/gjtorikian/commonmarker),
|
||||
[Lua](https://github.com/jgm/cmark-lua),
|
||||
[Perl](https://metacpan.org/release/CommonMark),
|
||||
[Python](https://pypi.python.org/pypi/paka.cmark),
|
||||
[R](https://cran.r-project.org/package=commonmark) and
|
||||
[Scala](https://github.com/sparsetech/cmark-scala).
|
||||
|
||||
Installing
|
||||
----------
|
||||
|
||||
Building the C program (`cmark`) and shared library (`libcmark`)
|
||||
requires [cmake]. If you modify `scanners.re`, then you will also
|
||||
need [re2c] \(>= 0.14.2\), which is used to generate `scanners.c` from
|
||||
`scanners.re`. We have included a pre-generated `scanners.c` in
|
||||
the repository to reduce build dependencies.
|
||||
|
||||
If you have GNU make, you can simply `make`, `make test`, and `make
|
||||
install`. This calls [cmake] to create a `Makefile` in the `build`
|
||||
directory, then uses that `Makefile` to create the executable and
|
||||
library. The binaries can be found in `build/src`. The default
|
||||
installation prefix is `/usr/local`. To change the installation
|
||||
prefix, pass the `INSTALL_PREFIX` variable if you run `make` for the
|
||||
first time: `make INSTALL_PREFIX=path`.
|
||||
|
||||
For a more portable method, you can use [cmake] manually. [cmake] knows
|
||||
how to create build environments for many build systems. For example,
|
||||
on FreeBSD:
|
||||
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. # optionally: -DCMAKE_INSTALL_PREFIX=path
|
||||
make # executable will be created as build/src/cmark
|
||||
make test
|
||||
make install
|
||||
|
||||
Or, to create Xcode project files on OSX:
|
||||
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -G Xcode ..
|
||||
open cmark.xcodeproj
|
||||
|
||||
The GNU Makefile also provides a few other targets for developers.
|
||||
To run a benchmark:
|
||||
|
||||
make bench
|
||||
|
||||
For more detailed benchmarks:
|
||||
|
||||
make newbench
|
||||
|
||||
To run a test for memory leaks using `valgrind`:
|
||||
|
||||
make leakcheck
|
||||
|
||||
To reformat source code using `clang-format`:
|
||||
|
||||
make format
|
||||
|
||||
To run a "fuzz test" against ten long randomly generated inputs:
|
||||
|
||||
make fuzztest
|
||||
|
||||
To do a more systematic fuzz test with [american fuzzy lop]:
|
||||
|
||||
AFL_PATH=/path/to/afl_directory make afl
|
||||
|
||||
Fuzzing with [libFuzzer] is also supported but, because libFuzzer is still
|
||||
under active development, may not work with your system-installed version of
|
||||
clang. Assuming LLVM has been built in `$HOME/src/llvm/build` the fuzzer can be
|
||||
run with:
|
||||
|
||||
CC="$HOME/src/llvm/build/bin/clang" LIB_FUZZER_PATH="$HOME/src/llvm/lib/Fuzzer/libFuzzer.a" make libFuzzer
|
||||
|
||||
To make a release tarball and zip archive:
|
||||
|
||||
make archive
|
||||
|
||||
Installing (Windows)
|
||||
--------------------
|
||||
|
||||
To compile with MSVC and NMAKE:
|
||||
|
||||
nmake
|
||||
|
||||
You can cross-compile a Windows binary and dll on linux if you have the
|
||||
`mingw32` compiler:
|
||||
|
||||
make mingw
|
||||
|
||||
The binaries will be in `build-mingw/windows/bin`.
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
Instructions for the use of the command line program and library can
|
||||
be found in the man pages in the `man` subdirectory.
|
||||
|
||||
Security
|
||||
--------
|
||||
|
||||
By default, the library will scrub raw HTML and potentially
|
||||
dangerous links (`javascript:`, `vbscript:`, `data:`, `file:`).
|
||||
|
||||
To allow these, use the option `CMARK_OPT_UNSAFE` (or
|
||||
`--unsafe`) with the command line program. If doing so, we
|
||||
recommend you use a HTML sanitizer specific to your needs to
|
||||
protect against [XSS
|
||||
attacks](http://en.wikipedia.org/wiki/Cross-site_scripting).
|
||||
|
||||
Contributing
|
||||
------------
|
||||
|
||||
There is a [forum for discussing
|
||||
CommonMark](http://talk.commonmark.org); you should use it instead of
|
||||
github issues for questions and possibly open-ended discussions.
|
||||
Use the [github issue tracker](http://github.com/commonmark/CommonMark/issues)
|
||||
only for simple, clear, actionable issues.
|
||||
|
||||
Authors
|
||||
-------
|
||||
|
||||
John MacFarlane wrote the original library and program.
|
||||
The block parsing algorithm was worked out together with David
|
||||
Greenspan. Vicent Marti optimized the C implementation for
|
||||
performance, increasing its speed tenfold. Kārlis Gaņģis helped
|
||||
work out a better parsing algorithm for links and emphasis,
|
||||
eliminating several worst-case performance issues.
|
||||
Nick Wellnhofer contributed many improvements, including
|
||||
most of the C library's API and its test harness.
|
||||
|
||||
[benchmarks]: benchmarks.md
|
||||
[the spec]: http://spec.commonmark.org
|
||||
[CommonMark]: http://commonmark.org
|
||||
[cmake]: http://www.cmake.org/download/
|
||||
[re2c]: http://re2c.org
|
||||
[commonmark.js]: https://github.com/commonmark/commonmark.js
|
||||
[Build Status]: https://img.shields.io/travis/commonmark/cmark/master.svg?style=flat
|
||||
[Windows Build Status]: https://ci.appveyor.com/api/projects/status/h3fd91vtd1xfmp69?svg=true
|
||||
[american fuzzy lop]: http://lcamtuf.coredump.cx/afl/
|
||||
[libFuzzer]: http://llvm.org/docs/LibFuzzer.html
|
12
deps/cmark/api_test/CMakeLists.txt
vendored
Normal file
12
deps/cmark/api_test/CMakeLists.txt
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
add_executable(api_test
|
||||
cplusplus.cpp
|
||||
harness.c
|
||||
harness.h
|
||||
main.c
|
||||
)
|
||||
cmark_add_compile_options(api_test)
|
||||
if(CMARK_SHARED)
|
||||
target_link_libraries(api_test cmark)
|
||||
else()
|
||||
target_link_libraries(api_test cmark_static)
|
||||
endif()
|
15
deps/cmark/api_test/cplusplus.cpp
vendored
Normal file
15
deps/cmark/api_test/cplusplus.cpp
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
#include <cstdlib>
|
||||
|
||||
#include "cmark.h"
|
||||
#include "cplusplus.h"
|
||||
#include "harness.h"
|
||||
|
||||
void
|
||||
test_cplusplus(test_batch_runner *runner)
|
||||
{
|
||||
static const char md[] = "paragraph\n";
|
||||
char *html = cmark_markdown_to_html(md, sizeof(md) - 1, CMARK_OPT_DEFAULT);
|
||||
STR_EQ(runner, html, "<p>paragraph</p>\n", "libcmark works with C++");
|
||||
free(html);
|
||||
}
|
||||
|
16
deps/cmark/api_test/cplusplus.h
vendored
Normal file
16
deps/cmark/api_test/cplusplus.h
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
#ifndef CMARK_API_TEST_CPLUSPLUS_H
|
||||
#define CMARK_API_TEST_CPLUSPLUS_H
|
||||
|
||||
#include "harness.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void test_cplusplus(test_batch_runner *runner);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
83
deps/cmark/api_test/harness.c
vendored
Normal file
83
deps/cmark/api_test/harness.c
vendored
Normal file
@ -0,0 +1,83 @@
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "harness.h"
|
||||
|
||||
test_batch_runner *test_batch_runner_new(void) {
|
||||
return (test_batch_runner *)calloc(1, sizeof(test_batch_runner));
|
||||
}
|
||||
|
||||
static void test_result(test_batch_runner *runner, int cond, const char *msg,
|
||||
va_list ap) {
|
||||
++runner->test_num;
|
||||
|
||||
if (cond) {
|
||||
++runner->num_passed;
|
||||
} else {
|
||||
fprintf(stderr, "FAILED test %d: ", runner->test_num);
|
||||
vfprintf(stderr, msg, ap);
|
||||
fprintf(stderr, "\n");
|
||||
++runner->num_failed;
|
||||
}
|
||||
}
|
||||
|
||||
void SKIP(test_batch_runner *runner, int num_tests) {
|
||||
runner->test_num += num_tests;
|
||||
runner->num_skipped += num_tests;
|
||||
}
|
||||
|
||||
void OK(test_batch_runner *runner, int cond, const char *msg, ...) {
|
||||
va_list ap;
|
||||
va_start(ap, msg);
|
||||
test_result(runner, cond, msg, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void INT_EQ(test_batch_runner *runner, int got, int expected, const char *msg,
|
||||
...) {
|
||||
int cond = got == expected;
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, msg);
|
||||
test_result(runner, cond, msg, ap);
|
||||
va_end(ap);
|
||||
|
||||
if (!cond) {
|
||||
fprintf(stderr, " Got: %d\n", got);
|
||||
fprintf(stderr, " Expected: %d\n", expected);
|
||||
}
|
||||
}
|
||||
|
||||
void STR_EQ(test_batch_runner *runner, const char *got, const char *expected,
|
||||
const char *msg, ...) {
|
||||
int cond = strcmp(got, expected) == 0;
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, msg);
|
||||
test_result(runner, cond, msg, ap);
|
||||
va_end(ap);
|
||||
|
||||
if (!cond) {
|
||||
fprintf(stderr, " Got: \"%s\"\n", got);
|
||||
fprintf(stderr, " Expected: \"%s\"\n", expected);
|
||||
}
|
||||
}
|
||||
|
||||
int test_ok(test_batch_runner *runner) { return runner->num_failed == 0; }
|
||||
|
||||
void test_print_summary(test_batch_runner *runner) {
|
||||
int num_passed = runner->num_passed;
|
||||
int num_skipped = runner->num_skipped;
|
||||
int num_failed = runner->num_failed;
|
||||
|
||||
fprintf(stderr, "%d tests passed, %d failed, %d skipped\n", num_passed,
|
||||
num_failed, num_skipped);
|
||||
|
||||
if (test_ok(runner)) {
|
||||
fprintf(stderr, "PASS\n");
|
||||
} else {
|
||||
fprintf(stderr, "FAIL\n");
|
||||
}
|
||||
}
|
35
deps/cmark/api_test/harness.h
vendored
Normal file
35
deps/cmark/api_test/harness.h
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
#ifndef CMARK_API_TEST_HARNESS_H
|
||||
#define CMARK_API_TEST_HARNESS_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
int test_num;
|
||||
int num_passed;
|
||||
int num_failed;
|
||||
int num_skipped;
|
||||
} test_batch_runner;
|
||||
|
||||
test_batch_runner *test_batch_runner_new(void);
|
||||
|
||||
void SKIP(test_batch_runner *runner, int num_tests);
|
||||
|
||||
void OK(test_batch_runner *runner, int cond, const char *msg, ...);
|
||||
|
||||
void INT_EQ(test_batch_runner *runner, int got, int expected, const char *msg,
|
||||
...);
|
||||
|
||||
void STR_EQ(test_batch_runner *runner, const char *got, const char *expected,
|
||||
const char *msg, ...);
|
||||
|
||||
int test_ok(test_batch_runner *runner);
|
||||
|
||||
void test_print_summary(test_batch_runner *runner);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
1105
deps/cmark/api_test/main.c
vendored
Normal file
1105
deps/cmark/api_test/main.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
16
deps/cmark/bench/samples/block-bq-flat.md
vendored
Normal file
16
deps/cmark/bench/samples/block-bq-flat.md
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
> the simple example of a blockquote
|
||||
> the simple example of a blockquote
|
||||
> the simple example of a blockquote
|
||||
> the simple example of a blockquote
|
||||
... continuation
|
||||
... continuation
|
||||
... continuation
|
||||
... continuation
|
||||
|
||||
empty blockquote:
|
||||
|
||||
>
|
||||
>
|
||||
>
|
||||
>
|
||||
|
13
deps/cmark/bench/samples/block-bq-nested.md
vendored
Normal file
13
deps/cmark/bench/samples/block-bq-nested.md
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
>>>>>> deeply nested blockquote
|
||||
>>>>> deeply nested blockquote
|
||||
>>>> deeply nested blockquote
|
||||
>>> deeply nested blockquote
|
||||
>> deeply nested blockquote
|
||||
> deeply nested blockquote
|
||||
|
||||
> deeply nested blockquote
|
||||
>> deeply nested blockquote
|
||||
>>> deeply nested blockquote
|
||||
>>>> deeply nested blockquote
|
||||
>>>>> deeply nested blockquote
|
||||
>>>>>> deeply nested blockquote
|
11
deps/cmark/bench/samples/block-code.md
vendored
Normal file
11
deps/cmark/bench/samples/block-code.md
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
|
||||
an
|
||||
example
|
||||
|
||||
of
|
||||
|
||||
|
||||
|
||||
a code
|
||||
block
|
||||
|
14
deps/cmark/bench/samples/block-fences.md
vendored
Normal file
14
deps/cmark/bench/samples/block-fences.md
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
|
||||
``````````text
|
||||
an
|
||||
example
|
||||
```
|
||||
of
|
||||
|
||||
|
||||
a fenced
|
||||
```
|
||||
code
|
||||
block
|
||||
``````````
|
||||
|
9
deps/cmark/bench/samples/block-heading.md
vendored
Normal file
9
deps/cmark/bench/samples/block-heading.md
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
# heading
|
||||
### heading
|
||||
##### heading
|
||||
|
||||
# heading #
|
||||
### heading ###
|
||||
##### heading \#\#\#\#\######
|
||||
|
||||
############ not a heading
|
10
deps/cmark/bench/samples/block-hr.md
vendored
Normal file
10
deps/cmark/bench/samples/block-hr.md
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
|
||||
* * * * *
|
||||
|
||||
- - - - -
|
||||
|
||||
________
|
||||
|
||||
|
||||
************************* text
|
||||
|
32
deps/cmark/bench/samples/block-html.md
vendored
Normal file
32
deps/cmark/bench/samples/block-html.md
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
<div class="this is an html block">
|
||||
|
||||
blah blah
|
||||
|
||||
</div>
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
**test**
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<table>
|
||||
|
||||
<tr>
|
||||
|
||||
<td>
|
||||
|
||||
test
|
||||
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
|
||||
<![CDATA[
|
||||
[[[[[[[[[[[... *cdata section - this should not be parsed* ...]]]]]]]]]]]
|
||||
]]>
|
||||
|
8
deps/cmark/bench/samples/block-lheading.md
vendored
Normal file
8
deps/cmark/bench/samples/block-lheading.md
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
heading
|
||||
---
|
||||
|
||||
heading
|
||||
===================================
|
||||
|
||||
not a heading
|
||||
----------------------------------- text
|
67
deps/cmark/bench/samples/block-list-flat.md
vendored
Normal file
67
deps/cmark/bench/samples/block-list-flat.md
vendored
Normal file
@ -0,0 +1,67 @@
|
||||
- tidy
|
||||
- bullet
|
||||
- list
|
||||
|
||||
|
||||
- loose
|
||||
|
||||
- bullet
|
||||
|
||||
- list
|
||||
|
||||
|
||||
0. ordered
|
||||
1. list
|
||||
2. example
|
||||
|
||||
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
|
||||
|
||||
- an example
|
||||
of a list item
|
||||
with a continuation
|
||||
|
||||
this part is inside the list
|
||||
|
||||
this part is just a paragraph
|
||||
|
||||
|
||||
1. test
|
||||
- test
|
||||
1. test
|
||||
- test
|
||||
|
||||
|
||||
111111111111111111111111111111111111111111. is this a valid bullet?
|
||||
|
||||
- _________________________
|
||||
|
||||
- this
|
||||
- is
|
||||
|
||||
a
|
||||
|
||||
long
|
||||
- loose
|
||||
- list
|
||||
|
||||
- with
|
||||
- some
|
||||
|
||||
tidy
|
||||
|
||||
- list
|
||||
- items
|
||||
- in
|
||||
|
||||
- between
|
||||
- _________________________
|
36
deps/cmark/bench/samples/block-list-nested.md
vendored
Normal file
36
deps/cmark/bench/samples/block-list-nested.md
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
|
||||
- this
|
||||
- is
|
||||
- a
|
||||
- deeply
|
||||
- nested
|
||||
- bullet
|
||||
- list
|
||||
|
||||
|
||||
1. this
|
||||
2. is
|
||||
3. a
|
||||
4. deeply
|
||||
5. nested
|
||||
6. unordered
|
||||
7. list
|
||||
|
||||
|
||||
- 1
|
||||
- 2
|
||||
- 3
|
||||
- 4
|
||||
- 5
|
||||
- 6
|
||||
- 7
|
||||
- 6
|
||||
- 5
|
||||
- 4
|
||||
- 3
|
||||
- 2
|
||||
- 1
|
||||
|
||||
|
||||
- - - - - - - - - deeply-nested one-element item
|
||||
|
15
deps/cmark/bench/samples/block-ref-flat.md
vendored
Normal file
15
deps/cmark/bench/samples/block-ref-flat.md
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
[1] [2] [3] [1] [2] [3]
|
||||
|
||||
[looooooooooooooooooooooooooooooooooooooooooooooooooong label]
|
||||
|
||||
[1]: <http://something.example.com/foo/bar>
|
||||
[2]: http://something.example.com/foo/bar 'test'
|
||||
[3]:
|
||||
http://foo/bar
|
||||
[ looooooooooooooooooooooooooooooooooooooooooooooooooong label ]:
|
||||
111
|
||||
'test'
|
||||
[[[[[[[[[[[[[[[[[[[[ this should not slow down anything ]]]]]]]]]]]]]]]]]]]]: q
|
||||
(as long as it is not referenced anywhere)
|
||||
|
||||
[[[[[[[[[[[[[[[[[[[[]: this is not a valid reference
|
17
deps/cmark/bench/samples/block-ref-nested.md
vendored
Normal file
17
deps/cmark/bench/samples/block-ref-nested.md
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
[[[[[[[foo]]]]]]]
|
||||
|
||||
[[[[[[[foo]]]]]]]: bar
|
||||
[[[[[[foo]]]]]]: bar
|
||||
[[[[[foo]]]]]: bar
|
||||
[[[[foo]]]]: bar
|
||||
[[[foo]]]: bar
|
||||
[[foo]]: bar
|
||||
[foo]: bar
|
||||
|
||||
[*[*[*[*[foo]*]*]*]*]
|
||||
|
||||
[*[*[*[*[foo]*]*]*]*]: bar
|
||||
[*[*[*[foo]*]*]*]: bar
|
||||
[*[*[foo]*]*]: bar
|
||||
[*[foo]*]: bar
|
||||
[foo]: bar
|
14
deps/cmark/bench/samples/inline-autolink.md
vendored
Normal file
14
deps/cmark/bench/samples/inline-autolink.md
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
closed (valid) autolinks:
|
||||
|
||||
<ftp://1.2.3.4:21/path/foo>
|
||||
<http://foo.bar.baz?q=hello&id=22&boolean>
|
||||
<http://veeeeeeeeeeeeeeeeeeery.loooooooooooooooooooooooooooooooong.autolink/>
|
||||
<teeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeest@gmail.com>
|
||||
|
||||
these are not autolinks:
|
||||
|
||||
<ftp://1.2.3.4:21/path/foo
|
||||
<http://foo.bar.baz?q=hello&id=22&boolean
|
||||
<http://veeeeeeeeeeeeeeeeeeery.loooooooooooooooooooooooooooooooong.autolink
|
||||
<teeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeest@gmail.com
|
||||
< http://foo.bar.baz?q=hello&id=22&boolean >
|
3
deps/cmark/bench/samples/inline-backticks.md
vendored
Normal file
3
deps/cmark/bench/samples/inline-backticks.md
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
`lots`of`backticks`
|
||||
|
||||
``i``wonder``how``this``will``be``parsed``
|
5
deps/cmark/bench/samples/inline-em-flat.md
vendored
Normal file
5
deps/cmark/bench/samples/inline-em-flat.md
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
*this* *is* *your* *basic* *boring* *emphasis*
|
||||
|
||||
_this_ _is_ _your_ _basic_ _boring_ _emphasis_
|
||||
|
||||
**this** **is** **your** **basic** **boring** **emphasis**
|
5
deps/cmark/bench/samples/inline-em-nested.md
vendored
Normal file
5
deps/cmark/bench/samples/inline-em-nested.md
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
*this *is *a *bunch* of* nested* emphases*
|
||||
|
||||
__this __is __a __bunch__ of__ nested__ emphases__
|
||||
|
||||
***this ***is ***a ***bunch*** of*** nested*** emphases***
|
5
deps/cmark/bench/samples/inline-em-worst.md
vendored
Normal file
5
deps/cmark/bench/samples/inline-em-worst.md
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
*this *is *a *worst *case *for *em *backtracking
|
||||
|
||||
__this __is __a __worst __case __for __em __backtracking
|
||||
|
||||
***this ***is ***a ***worst ***case ***for ***em ***backtracking
|
11
deps/cmark/bench/samples/inline-entity.md
vendored
Normal file
11
deps/cmark/bench/samples/inline-entity.md
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
entities:
|
||||
|
||||
& © Æ Ď ¾ ℋ ⅆ ∲
|
||||
|
||||
# Ӓ Ϡ �
|
||||
|
||||
non-entities:
|
||||
|
||||
&18900987654321234567890; &1234567890098765432123456789009876543212345678987654;
|
||||
|
||||
&qwertyuioppoiuytrewqwer; &oiuytrewqwertyuioiuytrewqwertyuioytrewqwertyuiiuytri;
|
15
deps/cmark/bench/samples/inline-escape.md
vendored
Normal file
15
deps/cmark/bench/samples/inline-escape.md
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
|
||||
\t\e\s\t\i\n\g \e\s\c\a\p\e \s\e\q\u\e\n\c\e\s
|
||||
|
||||
\!\\\"\#\$\%\&\'\(\)\*\+\,\.\/\:\;\<\=\>\?
|
||||
|
||||
\@ \[ \] \^ \_ \` \{ \| \} \~ \- \'
|
||||
|
||||
\
|
||||
\\
|
||||
\\\
|
||||
\\\\
|
||||
\\\\\
|
||||
|
||||
\<this\> \<is\> \<not\> \<html\>
|
||||
|
44
deps/cmark/bench/samples/inline-html.md
vendored
Normal file
44
deps/cmark/bench/samples/inline-html.md
vendored
Normal file
@ -0,0 +1,44 @@
|
||||
Taking commonmark tests from the spec for benchmarking here:
|
||||
|
||||
<a><bab><c2c>
|
||||
|
||||
<a/><b2/>
|
||||
|
||||
<a /><b2
|
||||
data="foo" >
|
||||
|
||||
<a foo="bar" bam = 'baz <em>"</em>'
|
||||
_boolean zoop:33=zoop:33 />
|
||||
|
||||
<33> <__>
|
||||
|
||||
<a h*#ref="hi">
|
||||
|
||||
<a href="hi'> <a href=hi'>
|
||||
|
||||
< a><
|
||||
foo><bar/ >
|
||||
|
||||
<a href='bar'title=title>
|
||||
|
||||
</a>
|
||||
</foo >
|
||||
|
||||
</a href="foo">
|
||||
|
||||
foo <!-- this is a
|
||||
comment - with hyphen -->
|
||||
|
||||
foo <!-- not a comment -- two hyphens -->
|
||||
|
||||
foo <?php echo $a; ?>
|
||||
|
||||
foo <!ELEMENT br EMPTY>
|
||||
|
||||
foo <![CDATA[>&<]]>
|
||||
|
||||
<a href="ö">
|
||||
|
||||
<a href="\*">
|
||||
|
||||
<a href="\"">
|
23
deps/cmark/bench/samples/inline-links-flat.md
vendored
Normal file
23
deps/cmark/bench/samples/inline-links-flat.md
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
Valid links:
|
||||
|
||||
[this is a link]()
|
||||
[this is a link](<http://something.example.com/foo/bar>)
|
||||
[this is a link](http://something.example.com/foo/bar 'test')
|
||||
![this is an image]()
|
||||

|
||||

|
||||
|
||||
[escape test](<\>\>\>\>\>\>\>\>\>\>\>\>\>\>> '\'\'\'\'\'\'\'\'\'\'\'\'\'\'')
|
||||
[escape test \]\]\]\]\]\]\]\]\]\]\]\]\]\]\]\]](\)\)\)\)\)\)\)\)\)\)\)\)\)\))
|
||||
|
||||
Invalid links:
|
||||
|
||||
[this is not a link
|
||||
|
||||
[this is not a link](
|
||||
|
||||
[this is not a link](http://something.example.com/foo/bar 'test'
|
||||
|
||||
[this is not a link](((((((((((((((((((((((((((((((((((((((((((((((
|
||||
|
||||
[this is not a link]((((((((((()))))))))) (((((((((()))))))))))
|
13
deps/cmark/bench/samples/inline-links-nested.md
vendored
Normal file
13
deps/cmark/bench/samples/inline-links-nested.md
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
Valid links:
|
||||
|
||||
[[[[[[[[](test)](test)](test)](test)](test)](test)](test)]
|
||||
|
||||
[ [[[[[[[[[[[[[[[[[[ [](test) ]]]]]]]]]]]]]]]]]] ](test)
|
||||
|
||||
Invalid links:
|
||||
|
||||
[[[[[[[[[
|
||||
|
||||
[ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [
|
||||
|
||||
 lobortis, sapien arcu mattis erat, vel aliquet sem urna et risus. Ut feugiat sapien vitae mi elementum laoreet. Suspendisse potenti. Aliquam erat nisl, aliquam pretium libero aliquet, sagittis eleifend nunc. In hac habitasse platea dictumst. Integer turpis augue, tincidunt dignissim mauris id, rhoncus dapibus purus. Maecenas et enim odio. Nullam massa metus, varius quis vehicula sed, pharetra mollis erat. In quis viverra velit. Vivamus placerat, est nec hendrerit varius, enim dui hendrerit magna, ut pulvinar nibh lorem vel lacus. Mauris a orci iaculis, hendrerit eros sed, gravida leo. In dictum mauris vel augue varius, ac ullamcorper nisl ornare. In eu posuere velit, ac fermentum arcu. Interdum et malesuada fames ac ante ipsum primis in faucibus. Nullam sed malesuada leo, at interdum elit.
|
||||
|
||||
Nullam ut tincidunt nunc. [Pellentesque][1] metus lacus, commodo eget justo ut, rutrum varius nunc. Sed non rhoncus risus. Morbi sodales gravida pulvinar. Duis malesuada, odio volutpat elementum vulputate, massa magna scelerisque ante, et accumsan tellus nunc in sem. Donec mattis arcu et velit aliquet, non sagittis justo vestibulum. Suspendisse volutpat felis lectus, nec consequat ipsum mattis id. Donec dapibus vehicula facilisis. In tincidunt mi nisi, nec faucibus tortor euismod nec. Suspendisse ante ligula, aliquet vitae libero eu, vulputate dapibus libero. Sed bibendum, sapien at posuere interdum, libero est sollicitudin magna, ac gravida tellus purus eu ipsum. Proin ut quam arcu.
|
||||
|
||||
Suspendisse potenti. Donec ante velit, ornare at augue quis, tristique laoreet sem. Etiam in ipsum elit. Nullam cursus dolor sit amet nulla feugiat tristique. Phasellus ac tellus tincidunt, imperdiet purus eget, ullamcorper ipsum. Cras eu tincidunt sem. Nullam sed dapibus magna. Lorem ipsum dolor sit amet, consectetur adipiscing elit. In id venenatis tortor. In consectetur sollicitudin pharetra. Etiam convallis nisi nunc, et aliquam turpis viverra sit amet. Maecenas faucibus sodales tortor. Suspendisse lobortis mi eu leo viverra volutpat. Pellentesque velit ante, vehicula sodales congue ut, elementum a urna. Cras tempor, ipsum eget luctus rhoncus, arcu ligula fermentum urna, vulputate pharetra enim enim non libero.
|
||||
|
||||
Proin diam quam, elementum in eleifend id, elementum et metus. Cras in justo consequat justo semper ultrices. Sed dignissim lectus a ante mollis, nec vulputate ante molestie. Proin in porta nunc. Etiam pulvinar turpis sed velit porttitor, vel adipiscing velit fringilla. Cras ac tellus vitae purus pharetra tincidunt. Sed cursus aliquet aliquet. Cras eleifend commodo malesuada. In turpis turpis, ullamcorper ut tincidunt a, ullamcorper a nunc. Etiam luctus tellus ac dapibus gravida. Ut nec lacus laoreet neque ullamcorper volutpat.
|
||||
|
||||
Nunc et leo erat. Aenean mattis ultrices lorem, eget adipiscing dolor ultricies eu. In hac habitasse platea dictumst. Vivamus cursus feugiat sapien quis aliquam. Mauris quam libero, porta vel volutpat ut, blandit a purus. Vivamus vestibulum dui vel tortor molestie, sit amet feugiat sem commodo. Nulla facilisi. Sed molestie arcu eget tellus vestibulum tristique.
|
||||
|
||||
[1]: https://github.com/markdown-it
|
18
deps/cmark/bench/samples/rawtabs.md
vendored
Normal file
18
deps/cmark/bench/samples/rawtabs.md
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
|
||||
this is a test for tab expansion, be careful not to replace them with spaces
|
||||
|
||||
1 4444
|
||||
22 333
|
||||
333 22
|
||||
4444 1
|
||||
|
||||
|
||||
tab-indented line
|
||||
space-indented line
|
||||
tab-indented line
|
||||
|
||||
|
||||
a lot of spaces in between here
|
||||
|
||||
a lot of tabs in between here
|
||||
|
595
deps/cmark/bench/statistics.py
vendored
Normal file
595
deps/cmark/bench/statistics.py
vendored
Normal file
@ -0,0 +1,595 @@
|
||||
## Module statistics.py
|
||||
##
|
||||
## Copyright (c) 2013 Steven D'Aprano <steve+python@pearwood.info>.
|
||||
##
|
||||
## Licensed under the Apache License, Version 2.0 (the "License");
|
||||
## you may not use this file except in compliance with the License.
|
||||
## You may obtain a copy of the License at
|
||||
##
|
||||
## http://www.apache.org/licenses/LICENSE-2.0
|
||||
##
|
||||
## Unless required by applicable law or agreed to in writing, software
|
||||
## distributed under the License is distributed on an "AS IS" BASIS,
|
||||
## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
## See the License for the specific language governing permissions and
|
||||
## limitations under the License.
|
||||
|
||||
|
||||
"""
|
||||
Basic statistics module.
|
||||
|
||||
This module provides functions for calculating statistics of data, including
|
||||
averages, variance, and standard deviation.
|
||||
|
||||
Calculating averages
|
||||
--------------------
|
||||
|
||||
================== =============================================
|
||||
Function Description
|
||||
================== =============================================
|
||||
mean Arithmetic mean (average) of data.
|
||||
median Median (middle value) of data.
|
||||
median_low Low median of data.
|
||||
median_high High median of data.
|
||||
median_grouped Median, or 50th percentile, of grouped data.
|
||||
mode Mode (most common value) of data.
|
||||
================== =============================================
|
||||
|
||||
Calculate the arithmetic mean ("the average") of data:
|
||||
|
||||
>>> mean([-1.0, 2.5, 3.25, 5.75])
|
||||
2.625
|
||||
|
||||
|
||||
Calculate the standard median of discrete data:
|
||||
|
||||
>>> median([2, 3, 4, 5])
|
||||
3.5
|
||||
|
||||
|
||||
Calculate the median, or 50th percentile, of data grouped into class intervals
|
||||
centred on the data values provided. E.g. if your data points are rounded to
|
||||
the nearest whole number:
|
||||
|
||||
>>> median_grouped([2, 2, 3, 3, 3, 4]) #doctest: +ELLIPSIS
|
||||
2.8333333333...
|
||||
|
||||
This should be interpreted in this way: you have two data points in the class
|
||||
interval 1.5-2.5, three data points in the class interval 2.5-3.5, and one in
|
||||
the class interval 3.5-4.5. The median of these data points is 2.8333...
|
||||
|
||||
|
||||
Calculating variability or spread
|
||||
---------------------------------
|
||||
|
||||
================== =============================================
|
||||
Function Description
|
||||
================== =============================================
|
||||
pvariance Population variance of data.
|
||||
variance Sample variance of data.
|
||||
pstdev Population standard deviation of data.
|
||||
stdev Sample standard deviation of data.
|
||||
================== =============================================
|
||||
|
||||
Calculate the standard deviation of sample data:
|
||||
|
||||
>>> stdev([2.5, 3.25, 5.5, 11.25, 11.75]) #doctest: +ELLIPSIS
|
||||
4.38961843444...
|
||||
|
||||
If you have previously calculated the mean, you can pass it as the optional
|
||||
second argument to the four "spread" functions to avoid recalculating it:
|
||||
|
||||
>>> data = [1, 2, 2, 4, 4, 4, 5, 6]
|
||||
>>> mu = mean(data)
|
||||
>>> pvariance(data, mu)
|
||||
2.5
|
||||
|
||||
|
||||
Exceptions
|
||||
----------
|
||||
|
||||
A single exception is defined: StatisticsError is a subclass of ValueError.
|
||||
|
||||
"""
|
||||
|
||||
__all__ = [ 'StatisticsError',
|
||||
'pstdev', 'pvariance', 'stdev', 'variance',
|
||||
'median', 'median_low', 'median_high', 'median_grouped',
|
||||
'mean', 'mode',
|
||||
]
|
||||
|
||||
|
||||
import collections
|
||||
import math
|
||||
|
||||
from fractions import Fraction
|
||||
from decimal import Decimal
|
||||
|
||||
|
||||
# === Exceptions ===
|
||||
|
||||
class StatisticsError(ValueError):
|
||||
pass
|
||||
|
||||
|
||||
# === Private utilities ===
|
||||
|
||||
def _sum(data, start=0):
|
||||
"""_sum(data [, start]) -> value
|
||||
|
||||
Return a high-precision sum of the given numeric data. If optional
|
||||
argument ``start`` is given, it is added to the total. If ``data`` is
|
||||
empty, ``start`` (defaulting to 0) is returned.
|
||||
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
>>> _sum([3, 2.25, 4.5, -0.5, 1.0], 0.75)
|
||||
11.0
|
||||
|
||||
Some sources of round-off error will be avoided:
|
||||
|
||||
>>> _sum([1e50, 1, -1e50] * 1000) # Built-in sum returns zero.
|
||||
1000.0
|
||||
|
||||
Fractions and Decimals are also supported:
|
||||
|
||||
>>> from fractions import Fraction as F
|
||||
>>> _sum([F(2, 3), F(7, 5), F(1, 4), F(5, 6)])
|
||||
Fraction(63, 20)
|
||||
|
||||
>>> from decimal import Decimal as D
|
||||
>>> data = [D("0.1375"), D("0.2108"), D("0.3061"), D("0.0419")]
|
||||
>>> _sum(data)
|
||||
Decimal('0.6963')
|
||||
|
||||
Mixed types are currently treated as an error, except that int is
|
||||
allowed.
|
||||
"""
|
||||
# We fail as soon as we reach a value that is not an int or the type of
|
||||
# the first value which is not an int. E.g. _sum([int, int, float, int])
|
||||
# is okay, but sum([int, int, float, Fraction]) is not.
|
||||
allowed_types = set([int, type(start)])
|
||||
n, d = _exact_ratio(start)
|
||||
partials = {d: n} # map {denominator: sum of numerators}
|
||||
# Micro-optimizations.
|
||||
exact_ratio = _exact_ratio
|
||||
partials_get = partials.get
|
||||
# Add numerators for each denominator.
|
||||
for x in data:
|
||||
_check_type(type(x), allowed_types)
|
||||
n, d = exact_ratio(x)
|
||||
partials[d] = partials_get(d, 0) + n
|
||||
# Find the expected result type. If allowed_types has only one item, it
|
||||
# will be int; if it has two, use the one which isn't int.
|
||||
assert len(allowed_types) in (1, 2)
|
||||
if len(allowed_types) == 1:
|
||||
assert allowed_types.pop() is int
|
||||
T = int
|
||||
else:
|
||||
T = (allowed_types - set([int])).pop()
|
||||
if None in partials:
|
||||
assert issubclass(T, (float, Decimal))
|
||||
assert not math.isfinite(partials[None])
|
||||
return T(partials[None])
|
||||
total = Fraction()
|
||||
for d, n in sorted(partials.items()):
|
||||
total += Fraction(n, d)
|
||||
if issubclass(T, int):
|
||||
assert total.denominator == 1
|
||||
return T(total.numerator)
|
||||
if issubclass(T, Decimal):
|
||||
return T(total.numerator)/total.denominator
|
||||
return T(total)
|
||||
|
||||
|
||||
def _check_type(T, allowed):
|
||||
if T not in allowed:
|
||||
if len(allowed) == 1:
|
||||
allowed.add(T)
|
||||
else:
|
||||
types = ', '.join([t.__name__ for t in allowed] + [T.__name__])
|
||||
raise TypeError("unsupported mixed types: %s" % types)
|
||||
|
||||
|
||||
def _exact_ratio(x):
|
||||
"""Convert Real number x exactly to (numerator, denominator) pair.
|
||||
|
||||
>>> _exact_ratio(0.25)
|
||||
(1, 4)
|
||||
|
||||
x is expected to be an int, Fraction, Decimal or float.
|
||||
"""
|
||||
try:
|
||||
try:
|
||||
# int, Fraction
|
||||
return (x.numerator, x.denominator)
|
||||
except AttributeError:
|
||||
# float
|
||||
try:
|
||||
return x.as_integer_ratio()
|
||||
except AttributeError:
|
||||
# Decimal
|
||||
try:
|
||||
return _decimal_to_ratio(x)
|
||||
except AttributeError:
|
||||
msg = "can't convert type '{}' to numerator/denominator"
|
||||
raise TypeError(msg.format(type(x).__name__)) from None
|
||||
except (OverflowError, ValueError):
|
||||
# INF or NAN
|
||||
if __debug__:
|
||||
# Decimal signalling NANs cannot be converted to float :-(
|
||||
if isinstance(x, Decimal):
|
||||
assert not x.is_finite()
|
||||
else:
|
||||
assert not math.isfinite(x)
|
||||
return (x, None)
|
||||
|
||||
|
||||
# FIXME This is faster than Fraction.from_decimal, but still too slow.
|
||||
def _decimal_to_ratio(d):
|
||||
"""Convert Decimal d to exact integer ratio (numerator, denominator).
|
||||
|
||||
>>> from decimal import Decimal
|
||||
>>> _decimal_to_ratio(Decimal("2.6"))
|
||||
(26, 10)
|
||||
|
||||
"""
|
||||
sign, digits, exp = d.as_tuple()
|
||||
if exp in ('F', 'n', 'N'): # INF, NAN, sNAN
|
||||
assert not d.is_finite()
|
||||
raise ValueError
|
||||
num = 0
|
||||
for digit in digits:
|
||||
num = num*10 + digit
|
||||
if exp < 0:
|
||||
den = 10**-exp
|
||||
else:
|
||||
num *= 10**exp
|
||||
den = 1
|
||||
if sign:
|
||||
num = -num
|
||||
return (num, den)
|
||||
|
||||
|
||||
def _counts(data):
|
||||
# Generate a table of sorted (value, frequency) pairs.
|
||||
table = collections.Counter(iter(data)).most_common()
|
||||
if not table:
|
||||
return table
|
||||
# Extract the values with the highest frequency.
|
||||
maxfreq = table[0][1]
|
||||
for i in range(1, len(table)):
|
||||
if table[i][1] != maxfreq:
|
||||
table = table[:i]
|
||||
break
|
||||
return table
|
||||
|
||||
|
||||
# === Measures of central tendency (averages) ===
|
||||
|
||||
def mean(data):
|
||||
"""Return the sample arithmetic mean of data.
|
||||
|
||||
>>> mean([1, 2, 3, 4, 4])
|
||||
2.8
|
||||
|
||||
>>> from fractions import Fraction as F
|
||||
>>> mean([F(3, 7), F(1, 21), F(5, 3), F(1, 3)])
|
||||
Fraction(13, 21)
|
||||
|
||||
>>> from decimal import Decimal as D
|
||||
>>> mean([D("0.5"), D("0.75"), D("0.625"), D("0.375")])
|
||||
Decimal('0.5625')
|
||||
|
||||
If ``data`` is empty, StatisticsError will be raised.
|
||||
"""
|
||||
if iter(data) is data:
|
||||
data = list(data)
|
||||
n = len(data)
|
||||
if n < 1:
|
||||
raise StatisticsError('mean requires at least one data point')
|
||||
return _sum(data)/n
|
||||
|
||||
|
||||
# FIXME: investigate ways to calculate medians without sorting? Quickselect?
|
||||
def median(data):
|
||||
"""Return the median (middle value) of numeric data.
|
||||
|
||||
When the number of data points is odd, return the middle data point.
|
||||
When the number of data points is even, the median is interpolated by
|
||||
taking the average of the two middle values:
|
||||
|
||||
>>> median([1, 3, 5])
|
||||
3
|
||||
>>> median([1, 3, 5, 7])
|
||||
4.0
|
||||
|
||||
"""
|
||||
data = sorted(data)
|
||||
n = len(data)
|
||||
if n == 0:
|
||||
raise StatisticsError("no median for empty data")
|
||||
if n%2 == 1:
|
||||
return data[n//2]
|
||||
else:
|
||||
i = n//2
|
||||
return (data[i - 1] + data[i])/2
|
||||
|
||||
|
||||
def median_low(data):
|
||||
"""Return the low median of numeric data.
|
||||
|
||||
When the number of data points is odd, the middle value is returned.
|
||||
When it is even, the smaller of the two middle values is returned.
|
||||
|
||||
>>> median_low([1, 3, 5])
|
||||
3
|
||||
>>> median_low([1, 3, 5, 7])
|
||||
3
|
||||
|
||||
"""
|
||||
data = sorted(data)
|
||||
n = len(data)
|
||||
if n == 0:
|
||||
raise StatisticsError("no median for empty data")
|
||||
if n%2 == 1:
|
||||
return data[n//2]
|
||||
else:
|
||||
return data[n//2 - 1]
|
||||
|
||||
|
||||
def median_high(data):
|
||||
"""Return the high median of data.
|
||||
|
||||
When the number of data points is odd, the middle value is returned.
|
||||
When it is even, the larger of the two middle values is returned.
|
||||
|
||||
>>> median_high([1, 3, 5])
|
||||
3
|
||||
>>> median_high([1, 3, 5, 7])
|
||||
5
|
||||
|
||||
"""
|
||||
data = sorted(data)
|
||||
n = len(data)
|
||||
if n == 0:
|
||||
raise StatisticsError("no median for empty data")
|
||||
return data[n//2]
|
||||
|
||||
|
||||
def median_grouped(data, interval=1):
|
||||
""""Return the 50th percentile (median) of grouped continuous data.
|
||||
|
||||
>>> median_grouped([1, 2, 2, 3, 4, 4, 4, 4, 4, 5])
|
||||
3.7
|
||||
>>> median_grouped([52, 52, 53, 54])
|
||||
52.5
|
||||
|
||||
This calculates the median as the 50th percentile, and should be
|
||||
used when your data is continuous and grouped. In the above example,
|
||||
the values 1, 2, 3, etc. actually represent the midpoint of classes
|
||||
0.5-1.5, 1.5-2.5, 2.5-3.5, etc. The middle value falls somewhere in
|
||||
class 3.5-4.5, and interpolation is used to estimate it.
|
||||
|
||||
Optional argument ``interval`` represents the class interval, and
|
||||
defaults to 1. Changing the class interval naturally will change the
|
||||
interpolated 50th percentile value:
|
||||
|
||||
>>> median_grouped([1, 3, 3, 5, 7], interval=1)
|
||||
3.25
|
||||
>>> median_grouped([1, 3, 3, 5, 7], interval=2)
|
||||
3.5
|
||||
|
||||
This function does not check whether the data points are at least
|
||||
``interval`` apart.
|
||||
"""
|
||||
data = sorted(data)
|
||||
n = len(data)
|
||||
if n == 0:
|
||||
raise StatisticsError("no median for empty data")
|
||||
elif n == 1:
|
||||
return data[0]
|
||||
# Find the value at the midpoint. Remember this corresponds to the
|
||||
# centre of the class interval.
|
||||
x = data[n//2]
|
||||
for obj in (x, interval):
|
||||
if isinstance(obj, (str, bytes)):
|
||||
raise TypeError('expected number but got %r' % obj)
|
||||
try:
|
||||
L = x - interval/2 # The lower limit of the median interval.
|
||||
except TypeError:
|
||||
# Mixed type. For now we just coerce to float.
|
||||
L = float(x) - float(interval)/2
|
||||
cf = data.index(x) # Number of values below the median interval.
|
||||
# FIXME The following line could be more efficient for big lists.
|
||||
f = data.count(x) # Number of data points in the median interval.
|
||||
return L + interval*(n/2 - cf)/f
|
||||
|
||||
|
||||
def mode(data):
|
||||
"""Return the most common data point from discrete or nominal data.
|
||||
|
||||
``mode`` assumes discrete data, and returns a single value. This is the
|
||||
standard treatment of the mode as commonly taught in schools:
|
||||
|
||||
>>> mode([1, 1, 2, 3, 3, 3, 3, 4])
|
||||
3
|
||||
|
||||
This also works with nominal (non-numeric) data:
|
||||
|
||||
>>> mode(["red", "blue", "blue", "red", "green", "red", "red"])
|
||||
'red'
|
||||
|
||||
If there is not exactly one most common value, ``mode`` will raise
|
||||
StatisticsError.
|
||||
"""
|
||||
# Generate a table of sorted (value, frequency) pairs.
|
||||
table = _counts(data)
|
||||
if len(table) == 1:
|
||||
return table[0][0]
|
||||
elif table:
|
||||
raise StatisticsError(
|
||||
'no unique mode; found %d equally common values' % len(table)
|
||||
)
|
||||
else:
|
||||
raise StatisticsError('no mode for empty data')
|
||||
|
||||
|
||||
# === Measures of spread ===
|
||||
|
||||
# See http://mathworld.wolfram.com/Variance.html
|
||||
# http://mathworld.wolfram.com/SampleVariance.html
|
||||
# http://en.wikipedia.org/wiki/Algorithms_for_calculating_variance
|
||||
#
|
||||
# Under no circumstances use the so-called "computational formula for
|
||||
# variance", as that is only suitable for hand calculations with a small
|
||||
# amount of low-precision data. It has terrible numeric properties.
|
||||
#
|
||||
# See a comparison of three computational methods here:
|
||||
# http://www.johndcook.com/blog/2008/09/26/comparing-three-methods-of-computing-standard-deviation/
|
||||
|
||||
def _ss(data, c=None):
|
||||
"""Return sum of square deviations of sequence data.
|
||||
|
||||
If ``c`` is None, the mean is calculated in one pass, and the deviations
|
||||
from the mean are calculated in a second pass. Otherwise, deviations are
|
||||
calculated from ``c`` as given. Use the second case with care, as it can
|
||||
lead to garbage results.
|
||||
"""
|
||||
if c is None:
|
||||
c = mean(data)
|
||||
ss = _sum((x-c)**2 for x in data)
|
||||
# The following sum should mathematically equal zero, but due to rounding
|
||||
# error may not.
|
||||
ss -= _sum((x-c) for x in data)**2/len(data)
|
||||
assert not ss < 0, 'negative sum of square deviations: %f' % ss
|
||||
return ss
|
||||
|
||||
|
||||
def variance(data, xbar=None):
|
||||
"""Return the sample variance of data.
|
||||
|
||||
data should be an iterable of Real-valued numbers, with at least two
|
||||
values. The optional argument xbar, if given, should be the mean of
|
||||
the data. If it is missing or None, the mean is automatically calculated.
|
||||
|
||||
Use this function when your data is a sample from a population. To
|
||||
calculate the variance from the entire population, see ``pvariance``.
|
||||
|
||||
Examples:
|
||||
|
||||
>>> data = [2.75, 1.75, 1.25, 0.25, 0.5, 1.25, 3.5]
|
||||
>>> variance(data)
|
||||
1.3720238095238095
|
||||
|
||||
If you have already calculated the mean of your data, you can pass it as
|
||||
the optional second argument ``xbar`` to avoid recalculating it:
|
||||
|
||||
>>> m = mean(data)
|
||||
>>> variance(data, m)
|
||||
1.3720238095238095
|
||||
|
||||
This function does not check that ``xbar`` is actually the mean of
|
||||
``data``. Giving arbitrary values for ``xbar`` may lead to invalid or
|
||||
impossible results.
|
||||
|
||||
Decimals and Fractions are supported:
|
||||
|
||||
>>> from decimal import Decimal as D
|
||||
>>> variance([D("27.5"), D("30.25"), D("30.25"), D("34.5"), D("41.75")])
|
||||
Decimal('31.01875')
|
||||
|
||||
>>> from fractions import Fraction as F
|
||||
>>> variance([F(1, 6), F(1, 2), F(5, 3)])
|
||||
Fraction(67, 108)
|
||||
|
||||
"""
|
||||
if iter(data) is data:
|
||||
data = list(data)
|
||||
n = len(data)
|
||||
if n < 2:
|
||||
raise StatisticsError('variance requires at least two data points')
|
||||
ss = _ss(data, xbar)
|
||||
return ss/(n-1)
|
||||
|
||||
|
||||
def pvariance(data, mu=None):
|
||||
"""Return the population variance of ``data``.
|
||||
|
||||
data should be an iterable of Real-valued numbers, with at least one
|
||||
value. The optional argument mu, if given, should be the mean of
|
||||
the data. If it is missing or None, the mean is automatically calculated.
|
||||
|
||||
Use this function to calculate the variance from the entire population.
|
||||
To estimate the variance from a sample, the ``variance`` function is
|
||||
usually a better choice.
|
||||
|
||||
Examples:
|
||||
|
||||
>>> data = [0.0, 0.25, 0.25, 1.25, 1.5, 1.75, 2.75, 3.25]
|
||||
>>> pvariance(data)
|
||||
1.25
|
||||
|
||||
If you have already calculated the mean of the data, you can pass it as
|
||||
the optional second argument to avoid recalculating it:
|
||||
|
||||
>>> mu = mean(data)
|
||||
>>> pvariance(data, mu)
|
||||
1.25
|
||||
|
||||
This function does not check that ``mu`` is actually the mean of ``data``.
|
||||
Giving arbitrary values for ``mu`` may lead to invalid or impossible
|
||||
results.
|
||||
|
||||
Decimals and Fractions are supported:
|
||||
|
||||
>>> from decimal import Decimal as D
|
||||
>>> pvariance([D("27.5"), D("30.25"), D("30.25"), D("34.5"), D("41.75")])
|
||||
Decimal('24.815')
|
||||
|
||||
>>> from fractions import Fraction as F
|
||||
>>> pvariance([F(1, 4), F(5, 4), F(1, 2)])
|
||||
Fraction(13, 72)
|
||||
|
||||
"""
|
||||
if iter(data) is data:
|
||||
data = list(data)
|
||||
n = len(data)
|
||||
if n < 1:
|
||||
raise StatisticsError('pvariance requires at least one data point')
|
||||
ss = _ss(data, mu)
|
||||
return ss/n
|
||||
|
||||
|
||||
def stdev(data, xbar=None):
|
||||
"""Return the square root of the sample variance.
|
||||
|
||||
See ``variance`` for arguments and other details.
|
||||
|
||||
>>> stdev([1.5, 2.5, 2.5, 2.75, 3.25, 4.75])
|
||||
1.0810874155219827
|
||||
|
||||
"""
|
||||
var = variance(data, xbar)
|
||||
try:
|
||||
return var.sqrt()
|
||||
except AttributeError:
|
||||
return math.sqrt(var)
|
||||
|
||||
|
||||
def pstdev(data, mu=None):
|
||||
"""Return the square root of the population variance.
|
||||
|
||||
See ``pvariance`` for arguments and other details.
|
||||
|
||||
>>> pstdev([1.5, 2.5, 2.5, 2.75, 3.25, 4.75])
|
||||
0.986893273527251
|
||||
|
||||
"""
|
||||
var = pvariance(data, mu)
|
||||
try:
|
||||
return var.sqrt()
|
||||
except AttributeError:
|
||||
return math.sqrt(var)
|
19
deps/cmark/bench/stats.py
vendored
Normal file
19
deps/cmark/bench/stats.py
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import sys
|
||||
import statistics
|
||||
|
||||
def pairs(l, n):
|
||||
return zip(*[l[i::n] for i in range(n)])
|
||||
|
||||
# data comes in pairs:
|
||||
# n - time for running the program with no input
|
||||
# m - time for running it with the benchmark input
|
||||
# we measure (m - n)
|
||||
|
||||
values = [ float(y) - float(x) for (x,y) in pairs(sys.stdin.readlines(),2)]
|
||||
|
||||
print("mean = %.4f, median = %.4f, stdev = %.4f" %
|
||||
(statistics.mean(values), statistics.median(values),
|
||||
statistics.stdev(values)))
|
||||
|
23
deps/cmark/benchmarks.md
vendored
Normal file
23
deps/cmark/benchmarks.md
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
# Benchmarks
|
||||
|
||||
Here are some benchmarks, run on a 2.3GHz 8-core i9 macbook pro.
|
||||
The input text is a 1106 KB Markdown file built by concatenating
|
||||
the Markdown sources of all the localizations of the first edition
|
||||
of [*Pro Git*](https://github.com/progit/progit/tree/master/en) by
|
||||
Scott Chacon.
|
||||
|
||||
|Implementation | Time (sec)|
|
||||
|-------------------|-----------:|
|
||||
| **commonmark.js** | 0.59 |
|
||||
| **cmark** | 0.12 |
|
||||
| **md4c** | 0.04 |
|
||||
|
||||
To run these benchmarks, use `make bench PROG=/path/to/program`.
|
||||
|
||||
`time` is used to measure execution speed. The reported
|
||||
time is the *difference* between the time to run the program
|
||||
with the benchmark input and the time to run it with no input.
|
||||
(This procedure ensures that implementations in dynamic languages are
|
||||
not penalized by startup time.) A median of ten runs is taken. The
|
||||
process is reniced to a high priority so that the system doesn't
|
||||
interrupt runs.
|
1316
deps/cmark/changelog.txt
vendored
Normal file
1316
deps/cmark/changelog.txt
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1624
deps/cmark/data/CaseFolding.txt
vendored
Normal file
1624
deps/cmark/data/CaseFolding.txt
vendored
Normal file
File diff suppressed because it is too large
Load Diff
4
deps/cmark/man/CMakeLists.txt
vendored
Normal file
4
deps/cmark/man/CMakeLists.txt
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/man1/cmark.1
|
||||
DESTINATION ${CMAKE_INSTALL_MANDIR}/man1)
|
||||
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/man3/cmark.3
|
||||
DESTINATION ${CMAKE_INSTALL_MANDIR}/man3)
|
137
deps/cmark/man/make_man_page.py
vendored
Normal file
137
deps/cmark/man/make_man_page.py
vendored
Normal file
@ -0,0 +1,137 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# Creates a man page from a C file.
|
||||
|
||||
# Comments beginning with `/**` are treated as Groff man, except that
|
||||
# 'this' is converted to \fIthis\f[], and ''this'' to \fBthis\f[].
|
||||
|
||||
# Non-blank lines immediately following a man page comment are treated
|
||||
# as function signatures or examples and parsed into .Ft, .Fo, .Fa, .Fc. The
|
||||
# immediately preceding man documentation chunk is printed after the example
|
||||
# as a comment on it.
|
||||
|
||||
# That's about it!
|
||||
|
||||
import sys, re, os, platform
|
||||
from datetime import date
|
||||
from ctypes import CDLL, c_char_p, c_long, c_void_p
|
||||
|
||||
sysname = platform.system()
|
||||
|
||||
curdir = os.getcwd()
|
||||
|
||||
if sysname == 'Darwin':
|
||||
cmark = CDLL(curdir + "/build/src/libcmark.dylib")
|
||||
else:
|
||||
cmark = CDLL(curdir + "/build/src/libcmark.so")
|
||||
|
||||
parse_document = cmark.cmark_parse_document
|
||||
parse_document.restype = c_void_p
|
||||
parse_document.argtypes = [c_char_p, c_long]
|
||||
|
||||
render_man = cmark.cmark_render_man
|
||||
render_man.restype = c_char_p
|
||||
render_man.argtypes = [c_void_p, c_long, c_long]
|
||||
|
||||
cmark_version_string = cmark.cmark_version_string
|
||||
cmark_version_string.restype = c_char_p
|
||||
cmark_version_string.argtypes = []
|
||||
|
||||
def md2man(text):
|
||||
if sys.version_info >= (3,0):
|
||||
textbytes = text.encode('utf-8')
|
||||
textlen = len(textbytes)
|
||||
return render_man(parse_document(textbytes, textlen), 0, 65).decode('utf-8')
|
||||
else:
|
||||
textbytes = text
|
||||
textlen = len(text)
|
||||
return render_man(parse_document(textbytes, textlen), 0, 72)
|
||||
|
||||
comment_start_re = re.compile('^\/\*\* ?')
|
||||
comment_delim_re = re.compile('^[/ ]\** ?')
|
||||
comment_end_re = re.compile('^ \**\/')
|
||||
function_re = re.compile('^ *(?:CMARK_EXPORT\s+)?(?P<type>(?:const\s+)?\w+(?:\s*[*])?)\s*(?P<name>\w+)\s*\((?P<args>[^)]*)\)')
|
||||
blank_re = re.compile('^\s*$')
|
||||
macro_re = re.compile('CMARK_EXPORT *')
|
||||
typedef_start_re = re.compile('typedef.*{$')
|
||||
typedef_end_re = re.compile('}')
|
||||
single_quote_re = re.compile("(?<!\w)'([^']+)'(?!\w)")
|
||||
double_quote_re = re.compile("(?<!\w)''([^']+)''(?!\w)")
|
||||
|
||||
def handle_quotes(s):
|
||||
return re.sub(double_quote_re, '**\g<1>**', re.sub(single_quote_re, '*\g<1>*', s))
|
||||
|
||||
typedef = False
|
||||
mdlines = []
|
||||
chunk = []
|
||||
sig = []
|
||||
|
||||
if len(sys.argv) > 1:
|
||||
sourcefile = sys.argv[1]
|
||||
else:
|
||||
print("Usage: make_man_page.py sourcefile")
|
||||
exit(1)
|
||||
|
||||
with open(sourcefile, 'r') as cmarkh:
|
||||
state = 'default'
|
||||
for line in cmarkh:
|
||||
# state transition
|
||||
oldstate = state
|
||||
if comment_start_re.match(line):
|
||||
state = 'man'
|
||||
elif comment_end_re.match(line) and state == 'man':
|
||||
continue
|
||||
elif comment_delim_re.match(line) and state == 'man':
|
||||
state = 'man'
|
||||
elif not typedef and blank_re.match(line):
|
||||
state = 'default'
|
||||
elif typedef and typedef_end_re.match(line):
|
||||
typedef = False
|
||||
elif typedef_start_re.match(line):
|
||||
typedef = True
|
||||
state = 'signature'
|
||||
elif state == 'man':
|
||||
state = 'signature'
|
||||
|
||||
# handle line
|
||||
if state == 'man':
|
||||
chunk.append(handle_quotes(re.sub(comment_delim_re, '', line)))
|
||||
elif state == 'signature':
|
||||
ln = re.sub(macro_re, '', line)
|
||||
if typedef or not re.match(blank_re, ln):
|
||||
sig.append(ln)
|
||||
elif oldstate == 'signature' and state != 'signature':
|
||||
if len(mdlines) > 0 and mdlines[-1] != '\n':
|
||||
mdlines.append('\n')
|
||||
rawsig = ''.join(sig)
|
||||
m = function_re.match(rawsig)
|
||||
mdlines.append('.PP\n')
|
||||
if m:
|
||||
mdlines.append('\\fI' + m.group('type') + '\\f[]' + ' ')
|
||||
mdlines.append('\\fB' + m.group('name') + '\\f[]' + '(')
|
||||
first = True
|
||||
for argument in re.split(',', m.group('args')):
|
||||
if not first:
|
||||
mdlines.append(', ')
|
||||
first = False
|
||||
mdlines.append('\\fI' + argument.strip() + '\\f[]')
|
||||
mdlines.append(')\n')
|
||||
else:
|
||||
mdlines.append('.nf\n\\fC\n.RS 0n\n')
|
||||
mdlines += sig
|
||||
mdlines.append('.RE\n\\f[]\n.fi\n')
|
||||
if len(mdlines) > 0 and mdlines[-1] != '\n':
|
||||
mdlines.append('\n')
|
||||
mdlines += md2man(''.join(chunk))
|
||||
mdlines.append('\n')
|
||||
chunk = []
|
||||
sig = []
|
||||
elif oldstate == 'man' and state != 'signature':
|
||||
if len(mdlines) > 0 and mdlines[-1] != '\n':
|
||||
mdlines.append('\n')
|
||||
mdlines += md2man(''.join(chunk)) # add man chunk
|
||||
chunk = []
|
||||
mdlines.append('\n')
|
||||
|
||||
sys.stdout.write('.TH ' + os.path.basename(sourcefile).replace('.h','') + ' 3 "' + date.today().strftime('%B %d, %Y') + '" "cmark ' + cmark_version_string().decode('utf-8') + '" "Library Functions Manual"\n')
|
||||
sys.stdout.write(''.join(mdlines))
|
73
deps/cmark/man/man1/cmark.1
vendored
Normal file
73
deps/cmark/man/man1/cmark.1
vendored
Normal file
@ -0,0 +1,73 @@
|
||||
.TH "cmark" "1" "February 11, 2020" "LOCAL" "General Commands Manual"
|
||||
.SH "NAME"
|
||||
\fBcmark\fR
|
||||
\- convert CommonMark formatted text to HTML
|
||||
.SH "SYNOPSIS"
|
||||
.HP 6n
|
||||
\fBcmark\fR
|
||||
[options]
|
||||
file*
|
||||
.SH "DESCRIPTION"
|
||||
\fBcmark\fR
|
||||
converts Markdown formatted plain text to either HTML, groff man,
|
||||
CommonMark XML, LaTeX, or CommonMark, using the conventions
|
||||
described in the CommonMark spec. It reads input from \fIstdin\fR
|
||||
or the specified files (concatenating their contents) and writes
|
||||
output to \fIstdout\fR.
|
||||
.SH "OPTIONS"
|
||||
.TP 12n
|
||||
.B \-\-to, \-t \f[I]FORMAT\f[]
|
||||
Specify output format (\f[C]html\f[], \f[C]man\f[], \f[C]xml\f[],
|
||||
\f[C]latex\f[], \f[C]commonmark\f[]).
|
||||
.TP 12n
|
||||
.B \-\-width \f[I]WIDTH\f[]
|
||||
Specify a column width to which to wrap the output. For no wrapping, use
|
||||
the value 0 (the default). This option currently only affects the
|
||||
commonmark, latex, and man renderers.
|
||||
.TP 12n
|
||||
.B \-\-hardbreaks
|
||||
Render soft breaks (newlines inside paragraphs in the CommonMark source)
|
||||
as hard line breaks in the target format. If this option is specified,
|
||||
hard wrapping is disabled for CommonMark output, regardless of the value
|
||||
given with \-\-width.
|
||||
.TP 12n
|
||||
.B \-\-nobreaks
|
||||
Render soft breaks as spaces. If this option is specified,
|
||||
hard wrapping is disabled for all output formats, regardless of the value
|
||||
given with \-\-width.
|
||||
.TP 12n
|
||||
.B \-\-sourcepos
|
||||
Include source position attribute.
|
||||
.TP 12n
|
||||
.B \-\-validate-utf8
|
||||
Validate UTF-8, replacing illegal sequences with U+FFFD.
|
||||
.TP 12n
|
||||
.B \-\-smart
|
||||
Use smart punctuation. Straight double and single quotes will
|
||||
be rendered as curly quotes, depending on their position.
|
||||
\f[C]\-\-\f[] will be rendered as an en-dash.
|
||||
\f[C]\-\-\-\f[] will be rendered as an em-dash.
|
||||
\f[C]...\f[] will be rendered as ellipses.
|
||||
.TP 12n
|
||||
.B \-\-safe
|
||||
Omit raw HTML and potentially dangerous URLs (default).
|
||||
Raw HTML is replaced by a placeholder comment
|
||||
and potentially dangerous URLs are replaced by empty strings.
|
||||
Potentially dangerous URLs are those that begin with `javascript:`,
|
||||
`vbscript:`, `file:`, or `data:` (except for `image/png`,
|
||||
`image/gif`, `image/jpeg`, or `image/webp` mime types).
|
||||
.TP 12n
|
||||
.B \-\-unsafe
|
||||
Render raw HTML or potentially dangerous URLs, overriding
|
||||
the default (\-\-safe) behavior.
|
||||
.TP 12n
|
||||
.B \-\-help
|
||||
Print usage information.
|
||||
.TP 12n
|
||||
.B \-\-version
|
||||
Print version.
|
||||
.SH "AUTHORS"
|
||||
John MacFarlane, Vicent Marti, Kārlis Gaņģis, Nick Wellnhofer.
|
||||
.SH "SEE ALSO"
|
||||
.PP
|
||||
CommonMark spec: \f[C]https://spec.commonmark.org\f[].
|
838
deps/cmark/man/man3/cmark.3
vendored
Normal file
838
deps/cmark/man/man3/cmark.3
vendored
Normal file
@ -0,0 +1,838 @@
|
||||
.TH cmark 3 "September 24, 2021" "cmark 0.30.2" "Library Functions Manual"
|
||||
.SH
|
||||
NAME
|
||||
.PP
|
||||
\f[B]cmark\f[] \- CommonMark parsing, manipulating, and rendering
|
||||
|
||||
.SH
|
||||
DESCRIPTION
|
||||
.SS
|
||||
Simple Interface
|
||||
|
||||
.PP
|
||||
\fIchar *\f[] \fBcmark_markdown_to_html\f[](\fIconst char *text\f[], \fIsize_t len\f[], \fIint options\f[])
|
||||
|
||||
.PP
|
||||
Convert \f[I]text\f[] (assumed to be a UTF\-8 encoded string with length
|
||||
\f[I]len\f[]) from CommonMark Markdown to HTML, returning a
|
||||
null\-terminated, UTF\-8\-encoded string. It is the caller's
|
||||
responsibility to free the returned buffer.
|
||||
|
||||
.SS
|
||||
Node Structure
|
||||
|
||||
.PP
|
||||
.nf
|
||||
\fC
|
||||
.RS 0n
|
||||
typedef enum {
|
||||
/* Error status */
|
||||
CMARK_NODE_NONE,
|
||||
|
||||
/* Block */
|
||||
CMARK_NODE_DOCUMENT,
|
||||
CMARK_NODE_BLOCK_QUOTE,
|
||||
CMARK_NODE_LIST,
|
||||
CMARK_NODE_ITEM,
|
||||
CMARK_NODE_CODE_BLOCK,
|
||||
CMARK_NODE_HTML_BLOCK,
|
||||
CMARK_NODE_CUSTOM_BLOCK,
|
||||
CMARK_NODE_PARAGRAPH,
|
||||
CMARK_NODE_HEADING,
|
||||
CMARK_NODE_THEMATIC_BREAK,
|
||||
|
||||
CMARK_NODE_FIRST_BLOCK = CMARK_NODE_DOCUMENT,
|
||||
CMARK_NODE_LAST_BLOCK = CMARK_NODE_THEMATIC_BREAK,
|
||||
|
||||
/* Inline */
|
||||
CMARK_NODE_TEXT,
|
||||
CMARK_NODE_SOFTBREAK,
|
||||
CMARK_NODE_LINEBREAK,
|
||||
CMARK_NODE_CODE,
|
||||
CMARK_NODE_HTML_INLINE,
|
||||
CMARK_NODE_CUSTOM_INLINE,
|
||||
CMARK_NODE_EMPH,
|
||||
CMARK_NODE_STRONG,
|
||||
CMARK_NODE_LINK,
|
||||
CMARK_NODE_IMAGE,
|
||||
|
||||
CMARK_NODE_FIRST_INLINE = CMARK_NODE_TEXT,
|
||||
CMARK_NODE_LAST_INLINE = CMARK_NODE_IMAGE
|
||||
} cmark_node_type;
|
||||
.RE
|
||||
\f[]
|
||||
.fi
|
||||
|
||||
|
||||
|
||||
.PP
|
||||
.nf
|
||||
\fC
|
||||
.RS 0n
|
||||
typedef enum {
|
||||
CMARK_NO_LIST,
|
||||
CMARK_BULLET_LIST,
|
||||
CMARK_ORDERED_LIST
|
||||
} cmark_list_type;
|
||||
.RE
|
||||
\f[]
|
||||
.fi
|
||||
|
||||
|
||||
|
||||
.PP
|
||||
.nf
|
||||
\fC
|
||||
.RS 0n
|
||||
typedef enum {
|
||||
CMARK_NO_DELIM,
|
||||
CMARK_PERIOD_DELIM,
|
||||
CMARK_PAREN_DELIM
|
||||
} cmark_delim_type;
|
||||
.RE
|
||||
\f[]
|
||||
.fi
|
||||
|
||||
|
||||
|
||||
.SS
|
||||
Custom memory allocator support
|
||||
|
||||
.PP
|
||||
.nf
|
||||
\fC
|
||||
.RS 0n
|
||||
typedef struct cmark_mem {
|
||||
void *(*calloc)(size_t, size_t);
|
||||
void *(*realloc)(void *, size_t);
|
||||
void (*free)(void *);
|
||||
} cmark_mem;
|
||||
.RE
|
||||
\f[]
|
||||
.fi
|
||||
|
||||
.PP
|
||||
Defines the memory allocation functions to be used by CMark when parsing
|
||||
and allocating a document tree
|
||||
|
||||
.PP
|
||||
\fIcmark_mem *\f[] \fBcmark_get_default_mem_allocator\f[](\fI\f[])
|
||||
|
||||
.PP
|
||||
Returns a pointer to the default memory allocator.
|
||||
|
||||
.SS
|
||||
Creating and Destroying Nodes
|
||||
|
||||
.PP
|
||||
\fIcmark_node *\f[] \fBcmark_node_new\f[](\fIcmark_node_type type\f[])
|
||||
|
||||
.PP
|
||||
Creates a new node of type \f[I]type\f[]. Note that the node may have
|
||||
other required properties, which it is the caller's responsibility to
|
||||
assign.
|
||||
|
||||
.PP
|
||||
\fIcmark_node *\f[] \fBcmark_node_new_with_mem\f[](\fIcmark_node_type type\f[], \fIcmark_mem *mem\f[])
|
||||
|
||||
.PP
|
||||
Same as \f[C]cmark_node_new\f[], but explicitly listing the memory
|
||||
allocator used to allocate the node. Note: be sure to use the same
|
||||
allocator for every node in a tree, or bad things can happen.
|
||||
|
||||
.PP
|
||||
\fIvoid\f[] \fBcmark_node_free\f[](\fIcmark_node *node\f[])
|
||||
|
||||
.PP
|
||||
Frees the memory allocated for a node and any children.
|
||||
|
||||
.SS
|
||||
Tree Traversal
|
||||
|
||||
.PP
|
||||
\fIcmark_node *\f[] \fBcmark_node_next\f[](\fIcmark_node *node\f[])
|
||||
|
||||
.PP
|
||||
Returns the next node in the sequence after \f[I]node\f[], or NULL if
|
||||
there is none.
|
||||
|
||||
.PP
|
||||
\fIcmark_node *\f[] \fBcmark_node_previous\f[](\fIcmark_node *node\f[])
|
||||
|
||||
.PP
|
||||
Returns the previous node in the sequence after \f[I]node\f[], or NULL
|
||||
if there is none.
|
||||
|
||||
.PP
|
||||
\fIcmark_node *\f[] \fBcmark_node_parent\f[](\fIcmark_node *node\f[])
|
||||
|
||||
.PP
|
||||
Returns the parent of \f[I]node\f[], or NULL if there is none.
|
||||
|
||||
.PP
|
||||
\fIcmark_node *\f[] \fBcmark_node_first_child\f[](\fIcmark_node *node\f[])
|
||||
|
||||
.PP
|
||||
Returns the first child of \f[I]node\f[], or NULL if \f[I]node\f[] has
|
||||
no children.
|
||||
|
||||
.PP
|
||||
\fIcmark_node *\f[] \fBcmark_node_last_child\f[](\fIcmark_node *node\f[])
|
||||
|
||||
.PP
|
||||
Returns the last child of \f[I]node\f[], or NULL if \f[I]node\f[] has no
|
||||
children.
|
||||
|
||||
.SS
|
||||
Iterator
|
||||
.PP
|
||||
An iterator will walk through a tree of nodes, starting from a root
|
||||
node, returning one node at a time, together with information about
|
||||
whether the node is being entered or exited. The iterator will first
|
||||
descend to a child node, if there is one. When there is no child, the
|
||||
iterator will go to the next sibling. When there is no next sibling, the
|
||||
iterator will return to the parent (but with a \f[I]cmark_event_type\f[]
|
||||
of \f[C]CMARK_EVENT_EXIT\f[]). The iterator will return
|
||||
\f[C]CMARK_EVENT_DONE\f[] when it reaches the root node again. One
|
||||
natural application is an HTML renderer, where an \f[C]ENTER\f[] event
|
||||
outputs an open tag and an \f[C]EXIT\f[] event outputs a close tag. An
|
||||
iterator might also be used to transform an AST in some systematic way,
|
||||
for example, turning all level\-3 headings into regular paragraphs.
|
||||
.IP
|
||||
.nf
|
||||
\f[C]
|
||||
void
|
||||
usage_example(cmark_node *root) {
|
||||
cmark_event_type ev_type;
|
||||
cmark_iter *iter = cmark_iter_new(root);
|
||||
|
||||
while ((ev_type = cmark_iter_next(iter)) != CMARK_EVENT_DONE) {
|
||||
cmark_node *cur = cmark_iter_get_node(iter);
|
||||
// Do something with `cur` and `ev_type`
|
||||
}
|
||||
|
||||
cmark_iter_free(iter);
|
||||
}
|
||||
\f[]
|
||||
.fi
|
||||
.PP
|
||||
Iterators will never return \f[C]EXIT\f[] events for leaf nodes, which
|
||||
are nodes of type:
|
||||
.IP \[bu] 2
|
||||
CMARK_NODE_HTML_BLOCK
|
||||
.IP \[bu] 2
|
||||
CMARK_NODE_THEMATIC_BREAK
|
||||
.IP \[bu] 2
|
||||
CMARK_NODE_CODE_BLOCK
|
||||
.IP \[bu] 2
|
||||
CMARK_NODE_TEXT
|
||||
.IP \[bu] 2
|
||||
CMARK_NODE_SOFTBREAK
|
||||
.IP \[bu] 2
|
||||
CMARK_NODE_LINEBREAK
|
||||
.IP \[bu] 2
|
||||
CMARK_NODE_CODE
|
||||
.IP \[bu] 2
|
||||
CMARK_NODE_HTML_INLINE
|
||||
.PP
|
||||
Nodes must only be modified after an \f[C]EXIT\f[] event, or an
|
||||
\f[C]ENTER\f[] event for leaf nodes.
|
||||
|
||||
.PP
|
||||
.nf
|
||||
\fC
|
||||
.RS 0n
|
||||
typedef enum {
|
||||
CMARK_EVENT_NONE,
|
||||
CMARK_EVENT_DONE,
|
||||
CMARK_EVENT_ENTER,
|
||||
CMARK_EVENT_EXIT
|
||||
} cmark_event_type;
|
||||
.RE
|
||||
\f[]
|
||||
.fi
|
||||
|
||||
|
||||
|
||||
.PP
|
||||
\fIcmark_iter *\f[] \fBcmark_iter_new\f[](\fIcmark_node *root\f[])
|
||||
|
||||
.PP
|
||||
Creates a new iterator starting at \f[I]root\f[]. The current node and
|
||||
event type are undefined until \f[I]cmark_iter_next\f[] is called for
|
||||
the first time. The memory allocated for the iterator should be released
|
||||
using \f[I]cmark_iter_free\f[] when it is no longer needed.
|
||||
|
||||
.PP
|
||||
\fIvoid\f[] \fBcmark_iter_free\f[](\fIcmark_iter *iter\f[])
|
||||
|
||||
.PP
|
||||
Frees the memory allocated for an iterator.
|
||||
|
||||
.PP
|
||||
\fIcmark_event_type\f[] \fBcmark_iter_next\f[](\fIcmark_iter *iter\f[])
|
||||
|
||||
.PP
|
||||
Advances to the next node and returns the event type
|
||||
(\f[C]CMARK_EVENT_ENTER\f[], \f[C]CMARK_EVENT_EXIT\f[] or
|
||||
\f[C]CMARK_EVENT_DONE\f[]).
|
||||
|
||||
.PP
|
||||
\fIcmark_node *\f[] \fBcmark_iter_get_node\f[](\fIcmark_iter *iter\f[])
|
||||
|
||||
.PP
|
||||
Returns the current node.
|
||||
|
||||
.PP
|
||||
\fIcmark_event_type\f[] \fBcmark_iter_get_event_type\f[](\fIcmark_iter *iter\f[])
|
||||
|
||||
.PP
|
||||
Returns the current event type.
|
||||
|
||||
.PP
|
||||
\fIcmark_node *\f[] \fBcmark_iter_get_root\f[](\fIcmark_iter *iter\f[])
|
||||
|
||||
.PP
|
||||
Returns the root node.
|
||||
|
||||
.PP
|
||||
\fIvoid\f[] \fBcmark_iter_reset\f[](\fIcmark_iter *iter\f[], \fIcmark_node *current\f[], \fIcmark_event_type event_type\f[])
|
||||
|
||||
.PP
|
||||
Resets the iterator so that the current node is \f[I]current\f[] and the
|
||||
event type is \f[I]event_type\f[]. The new current node must be a
|
||||
descendant of the root node or the root node itself.
|
||||
|
||||
.SS
|
||||
Accessors
|
||||
|
||||
.PP
|
||||
\fIvoid *\f[] \fBcmark_node_get_user_data\f[](\fIcmark_node *node\f[])
|
||||
|
||||
.PP
|
||||
Returns the user data of \f[I]node\f[].
|
||||
|
||||
.PP
|
||||
\fIint\f[] \fBcmark_node_set_user_data\f[](\fIcmark_node *node\f[], \fIvoid *user_data\f[])
|
||||
|
||||
.PP
|
||||
Sets arbitrary user data for \f[I]node\f[]. Returns 1 on success, 0 on
|
||||
failure.
|
||||
|
||||
.PP
|
||||
\fIcmark_node_type\f[] \fBcmark_node_get_type\f[](\fIcmark_node *node\f[])
|
||||
|
||||
.PP
|
||||
Returns the type of \f[I]node\f[], or \f[C]CMARK_NODE_NONE\f[] on error.
|
||||
|
||||
.PP
|
||||
\fIconst char *\f[] \fBcmark_node_get_type_string\f[](\fIcmark_node *node\f[])
|
||||
|
||||
.PP
|
||||
Like \f[I]cmark_node_get_type\f[], but returns a string representation
|
||||
of the type, or \f[C]"<unknown>"\f[].
|
||||
|
||||
.PP
|
||||
\fIconst char *\f[] \fBcmark_node_get_literal\f[](\fIcmark_node *node\f[])
|
||||
|
||||
.PP
|
||||
Returns the string contents of \f[I]node\f[], or an empty string if none
|
||||
is set. Returns NULL if called on a node that does not have string
|
||||
content.
|
||||
|
||||
.PP
|
||||
\fIint\f[] \fBcmark_node_set_literal\f[](\fIcmark_node *node\f[], \fIconst char *content\f[])
|
||||
|
||||
.PP
|
||||
Sets the string contents of \f[I]node\f[]. Returns 1 on success, 0 on
|
||||
failure.
|
||||
|
||||
.PP
|
||||
\fIint\f[] \fBcmark_node_get_heading_level\f[](\fIcmark_node *node\f[])
|
||||
|
||||
.PP
|
||||
Returns the heading level of \f[I]node\f[], or 0 if \f[I]node\f[] is not
|
||||
a heading.
|
||||
|
||||
.PP
|
||||
\fIint\f[] \fBcmark_node_set_heading_level\f[](\fIcmark_node *node\f[], \fIint level\f[])
|
||||
|
||||
.PP
|
||||
Sets the heading level of \f[I]node\f[], returning 1 on success and 0 on
|
||||
error.
|
||||
|
||||
.PP
|
||||
\fIcmark_list_type\f[] \fBcmark_node_get_list_type\f[](\fIcmark_node *node\f[])
|
||||
|
||||
.PP
|
||||
Returns the list type of \f[I]node\f[], or \f[C]CMARK_NO_LIST\f[] if
|
||||
\f[I]node\f[] is not a list.
|
||||
|
||||
.PP
|
||||
\fIint\f[] \fBcmark_node_set_list_type\f[](\fIcmark_node *node\f[], \fIcmark_list_type type\f[])
|
||||
|
||||
.PP
|
||||
Sets the list type of \f[I]node\f[], returning 1 on success and 0 on
|
||||
error.
|
||||
|
||||
.PP
|
||||
\fIcmark_delim_type\f[] \fBcmark_node_get_list_delim\f[](\fIcmark_node *node\f[])
|
||||
|
||||
.PP
|
||||
Returns the list delimiter type of \f[I]node\f[], or
|
||||
\f[C]CMARK_NO_DELIM\f[] if \f[I]node\f[] is not a list.
|
||||
|
||||
.PP
|
||||
\fIint\f[] \fBcmark_node_set_list_delim\f[](\fIcmark_node *node\f[], \fIcmark_delim_type delim\f[])
|
||||
|
||||
.PP
|
||||
Sets the list delimiter type of \f[I]node\f[], returning 1 on success
|
||||
and 0 on error.
|
||||
|
||||
.PP
|
||||
\fIint\f[] \fBcmark_node_get_list_start\f[](\fIcmark_node *node\f[])
|
||||
|
||||
.PP
|
||||
Returns starting number of \f[I]node\f[], if it is an ordered list,
|
||||
otherwise 0.
|
||||
|
||||
.PP
|
||||
\fIint\f[] \fBcmark_node_set_list_start\f[](\fIcmark_node *node\f[], \fIint start\f[])
|
||||
|
||||
.PP
|
||||
Sets starting number of \f[I]node\f[], if it is an ordered list.
|
||||
Returns 1 on success, 0 on failure.
|
||||
|
||||
.PP
|
||||
\fIint\f[] \fBcmark_node_get_list_tight\f[](\fIcmark_node *node\f[])
|
||||
|
||||
.PP
|
||||
Returns 1 if \f[I]node\f[] is a tight list, 0 otherwise.
|
||||
|
||||
.PP
|
||||
\fIint\f[] \fBcmark_node_set_list_tight\f[](\fIcmark_node *node\f[], \fIint tight\f[])
|
||||
|
||||
.PP
|
||||
Sets the "tightness" of a list. Returns 1 on success, 0 on failure.
|
||||
|
||||
.PP
|
||||
\fIconst char *\f[] \fBcmark_node_get_fence_info\f[](\fIcmark_node *node\f[])
|
||||
|
||||
.PP
|
||||
Returns the info string from a fenced code block.
|
||||
|
||||
.PP
|
||||
\fIint\f[] \fBcmark_node_set_fence_info\f[](\fIcmark_node *node\f[], \fIconst char *info\f[])
|
||||
|
||||
.PP
|
||||
Sets the info string in a fenced code block, returning 1 on success
|
||||
and 0 on failure.
|
||||
|
||||
.PP
|
||||
\fIconst char *\f[] \fBcmark_node_get_url\f[](\fIcmark_node *node\f[])
|
||||
|
||||
.PP
|
||||
Returns the URL of a link or image \f[I]node\f[], or an empty string if
|
||||
no URL is set. Returns NULL if called on a node that is not a link or
|
||||
image.
|
||||
|
||||
.PP
|
||||
\fIint\f[] \fBcmark_node_set_url\f[](\fIcmark_node *node\f[], \fIconst char *url\f[])
|
||||
|
||||
.PP
|
||||
Sets the URL of a link or image \f[I]node\f[]. Returns 1 on success, 0
|
||||
on failure.
|
||||
|
||||
.PP
|
||||
\fIconst char *\f[] \fBcmark_node_get_title\f[](\fIcmark_node *node\f[])
|
||||
|
||||
.PP
|
||||
Returns the title of a link or image \f[I]node\f[], or an empty string
|
||||
if no title is set. Returns NULL if called on a node that is not a link
|
||||
or image.
|
||||
|
||||
.PP
|
||||
\fIint\f[] \fBcmark_node_set_title\f[](\fIcmark_node *node\f[], \fIconst char *title\f[])
|
||||
|
||||
.PP
|
||||
Sets the title of a link or image \f[I]node\f[]. Returns 1 on success, 0
|
||||
on failure.
|
||||
|
||||
.PP
|
||||
\fIconst char *\f[] \fBcmark_node_get_on_enter\f[](\fIcmark_node *node\f[])
|
||||
|
||||
.PP
|
||||
Returns the literal "on enter" text for a custom \f[I]node\f[], or an
|
||||
empty string if no on_enter is set. Returns NULL if called on a
|
||||
non\-custom node.
|
||||
|
||||
.PP
|
||||
\fIint\f[] \fBcmark_node_set_on_enter\f[](\fIcmark_node *node\f[], \fIconst char *on_enter\f[])
|
||||
|
||||
.PP
|
||||
Sets the literal text to render "on enter" for a custom \f[I]node\f[].
|
||||
Any children of the node will be rendered after this text. Returns 1 on
|
||||
success 0 on failure.
|
||||
|
||||
.PP
|
||||
\fIconst char *\f[] \fBcmark_node_get_on_exit\f[](\fIcmark_node *node\f[])
|
||||
|
||||
.PP
|
||||
Returns the literal "on exit" text for a custom \f[I]node\f[], or an
|
||||
empty string if no on_exit is set. Returns NULL if called on a
|
||||
non\-custom node.
|
||||
|
||||
.PP
|
||||
\fIint\f[] \fBcmark_node_set_on_exit\f[](\fIcmark_node *node\f[], \fIconst char *on_exit\f[])
|
||||
|
||||
.PP
|
||||
Sets the literal text to render "on exit" for a custom \f[I]node\f[].
|
||||
Any children of the node will be rendered before this text. Returns 1 on
|
||||
success 0 on failure.
|
||||
|
||||
.PP
|
||||
\fIint\f[] \fBcmark_node_get_start_line\f[](\fIcmark_node *node\f[])
|
||||
|
||||
.PP
|
||||
Returns the line on which \f[I]node\f[] begins.
|
||||
|
||||
.PP
|
||||
\fIint\f[] \fBcmark_node_get_start_column\f[](\fIcmark_node *node\f[])
|
||||
|
||||
.PP
|
||||
Returns the column at which \f[I]node\f[] begins.
|
||||
|
||||
.PP
|
||||
\fIint\f[] \fBcmark_node_get_end_line\f[](\fIcmark_node *node\f[])
|
||||
|
||||
.PP
|
||||
Returns the line on which \f[I]node\f[] ends.
|
||||
|
||||
.PP
|
||||
\fIint\f[] \fBcmark_node_get_end_column\f[](\fIcmark_node *node\f[])
|
||||
|
||||
.PP
|
||||
Returns the column at which \f[I]node\f[] ends.
|
||||
|
||||
.SS
|
||||
Tree Manipulation
|
||||
|
||||
.PP
|
||||
\fIvoid\f[] \fBcmark_node_unlink\f[](\fIcmark_node *node\f[])
|
||||
|
||||
.PP
|
||||
Unlinks a \f[I]node\f[], removing it from the tree, but not freeing its
|
||||
memory. (Use \f[I]cmark_node_free\f[] for that.)
|
||||
|
||||
.PP
|
||||
\fIint\f[] \fBcmark_node_insert_before\f[](\fIcmark_node *node\f[], \fIcmark_node *sibling\f[])
|
||||
|
||||
.PP
|
||||
Inserts \f[I]sibling\f[] before \f[I]node\f[]. Returns 1 on success, 0
|
||||
on failure.
|
||||
|
||||
.PP
|
||||
\fIint\f[] \fBcmark_node_insert_after\f[](\fIcmark_node *node\f[], \fIcmark_node *sibling\f[])
|
||||
|
||||
.PP
|
||||
Inserts \f[I]sibling\f[] after \f[I]node\f[]. Returns 1 on success, 0 on
|
||||
failure.
|
||||
|
||||
.PP
|
||||
\fIint\f[] \fBcmark_node_replace\f[](\fIcmark_node *oldnode\f[], \fIcmark_node *newnode\f[])
|
||||
|
||||
.PP
|
||||
Replaces \f[I]oldnode\f[] with \f[I]newnode\f[] and unlinks
|
||||
\f[I]oldnode\f[] (but does not free its memory). Returns 1 on success, 0
|
||||
on failure.
|
||||
|
||||
.PP
|
||||
\fIint\f[] \fBcmark_node_prepend_child\f[](\fIcmark_node *node\f[], \fIcmark_node *child\f[])
|
||||
|
||||
.PP
|
||||
Adds \f[I]child\f[] to the beginning of the children of \f[I]node\f[].
|
||||
Returns 1 on success, 0 on failure.
|
||||
|
||||
.PP
|
||||
\fIint\f[] \fBcmark_node_append_child\f[](\fIcmark_node *node\f[], \fIcmark_node *child\f[])
|
||||
|
||||
.PP
|
||||
Adds \f[I]child\f[] to the end of the children of \f[I]node\f[].
|
||||
Returns 1 on success, 0 on failure.
|
||||
|
||||
.PP
|
||||
\fIvoid\f[] \fBcmark_consolidate_text_nodes\f[](\fIcmark_node *root\f[])
|
||||
|
||||
.PP
|
||||
Consolidates adjacent text nodes.
|
||||
|
||||
.SS
|
||||
Parsing
|
||||
.PP
|
||||
Simple interface:
|
||||
.IP
|
||||
.nf
|
||||
\f[C]
|
||||
cmark_node *document = cmark_parse_document("Hello *world*", 13,
|
||||
CMARK_OPT_DEFAULT);
|
||||
\f[]
|
||||
.fi
|
||||
.PP
|
||||
Streaming interface:
|
||||
.IP
|
||||
.nf
|
||||
\f[C]
|
||||
cmark_parser *parser = cmark_parser_new(CMARK_OPT_DEFAULT);
|
||||
FILE *fp = fopen("myfile.md", "rb");
|
||||
while ((bytes = fread(buffer, 1, sizeof(buffer), fp)) > 0) {
|
||||
cmark_parser_feed(parser, buffer, bytes);
|
||||
if (bytes < sizeof(buffer)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
document = cmark_parser_finish(parser);
|
||||
cmark_parser_free(parser);
|
||||
\f[]
|
||||
.fi
|
||||
|
||||
.PP
|
||||
\fIcmark_parser *\f[] \fBcmark_parser_new\f[](\fIint options\f[])
|
||||
|
||||
.PP
|
||||
Creates a new parser object.
|
||||
|
||||
.PP
|
||||
\fIcmark_parser *\f[] \fBcmark_parser_new_with_mem\f[](\fIint options\f[], \fIcmark_mem *mem\f[])
|
||||
|
||||
.PP
|
||||
Creates a new parser object with the given memory allocator
|
||||
|
||||
.PP
|
||||
\fIvoid\f[] \fBcmark_parser_free\f[](\fIcmark_parser *parser\f[])
|
||||
|
||||
.PP
|
||||
Frees memory allocated for a parser object.
|
||||
|
||||
.PP
|
||||
\fIvoid\f[] \fBcmark_parser_feed\f[](\fIcmark_parser *parser\f[], \fIconst char *buffer\f[], \fIsize_t len\f[])
|
||||
|
||||
.PP
|
||||
Feeds a string of length \f[I]len\f[] to \f[I]parser\f[].
|
||||
|
||||
.PP
|
||||
\fIcmark_node *\f[] \fBcmark_parser_finish\f[](\fIcmark_parser *parser\f[])
|
||||
|
||||
.PP
|
||||
Finish parsing and return a pointer to a tree of nodes.
|
||||
|
||||
.PP
|
||||
\fIcmark_node *\f[] \fBcmark_parse_document\f[](\fIconst char *buffer\f[], \fIsize_t len\f[], \fIint options\f[])
|
||||
|
||||
.PP
|
||||
Parse a CommonMark document in \f[I]buffer\f[] of length \f[I]len\f[].
|
||||
Returns a pointer to a tree of nodes. The memory allocated for the node
|
||||
tree should be released using \f[I]cmark_node_free\f[] when it is no
|
||||
longer needed.
|
||||
|
||||
.PP
|
||||
\fIcmark_node *\f[] \fBcmark_parse_file\f[](\fIFILE *f\f[], \fIint options\f[])
|
||||
|
||||
.PP
|
||||
Parse a CommonMark document in file \f[I]f\f[], returning a pointer to a
|
||||
tree of nodes. The memory allocated for the node tree should be released
|
||||
using \f[I]cmark_node_free\f[] when it is no longer needed.
|
||||
|
||||
.SS
|
||||
Rendering
|
||||
|
||||
.PP
|
||||
\fIchar *\f[] \fBcmark_render_xml\f[](\fIcmark_node *root\f[], \fIint options\f[])
|
||||
|
||||
.PP
|
||||
Render a \f[I]node\f[] tree as XML. It is the caller's responsibility to
|
||||
free the returned buffer.
|
||||
|
||||
.PP
|
||||
\fIchar *\f[] \fBcmark_render_html\f[](\fIcmark_node *root\f[], \fIint options\f[])
|
||||
|
||||
.PP
|
||||
Render a \f[I]node\f[] tree as an HTML fragment. It is up to the user to
|
||||
add an appropriate header and footer. It is the caller's responsibility
|
||||
to free the returned buffer.
|
||||
|
||||
.PP
|
||||
\fIchar *\f[] \fBcmark_render_man\f[](\fIcmark_node *root\f[], \fIint options\f[], \fIint width\f[])
|
||||
|
||||
.PP
|
||||
Render a \f[I]node\f[] tree as a groff man page, without the header. It
|
||||
is the caller's responsibility to free the returned buffer.
|
||||
|
||||
.PP
|
||||
\fIchar *\f[] \fBcmark_render_commonmark\f[](\fIcmark_node *root\f[], \fIint options\f[], \fIint width\f[])
|
||||
|
||||
.PP
|
||||
Render a \f[I]node\f[] tree as a commonmark document. It is the caller's
|
||||
responsibility to free the returned buffer.
|
||||
|
||||
.PP
|
||||
\fIchar *\f[] \fBcmark_render_latex\f[](\fIcmark_node *root\f[], \fIint options\f[], \fIint width\f[])
|
||||
|
||||
.PP
|
||||
Render a \f[I]node\f[] tree as a LaTeX document. It is the caller's
|
||||
responsibility to free the returned buffer.
|
||||
|
||||
.SS
|
||||
Options
|
||||
|
||||
.PP
|
||||
.nf
|
||||
\fC
|
||||
.RS 0n
|
||||
#define CMARK_OPT_DEFAULT 0
|
||||
.RE
|
||||
\f[]
|
||||
.fi
|
||||
|
||||
.PP
|
||||
Default options.
|
||||
|
||||
.SS
|
||||
Options affecting rendering
|
||||
|
||||
.PP
|
||||
.nf
|
||||
\fC
|
||||
.RS 0n
|
||||
#define CMARK_OPT_SOURCEPOS (1 << 1)
|
||||
.RE
|
||||
\f[]
|
||||
.fi
|
||||
|
||||
.PP
|
||||
Include a \f[C]data\-sourcepos\f[] attribute on all block elements.
|
||||
|
||||
.PP
|
||||
.nf
|
||||
\fC
|
||||
.RS 0n
|
||||
#define CMARK_OPT_HARDBREAKS (1 << 2)
|
||||
.RE
|
||||
\f[]
|
||||
.fi
|
||||
|
||||
.PP
|
||||
Render \f[C]softbreak\f[] elements as hard line breaks.
|
||||
|
||||
.PP
|
||||
.nf
|
||||
\fC
|
||||
.RS 0n
|
||||
#define CMARK_OPT_SAFE (1 << 3)
|
||||
.RE
|
||||
\f[]
|
||||
.fi
|
||||
|
||||
.PP
|
||||
\f[C]CMARK_OPT_SAFE\f[] is defined here for API compatibility, but it no
|
||||
longer has any effect. "Safe" mode is now the default: set
|
||||
\f[C]CMARK_OPT_UNSAFE\f[] to disable it.
|
||||
|
||||
.PP
|
||||
.nf
|
||||
\fC
|
||||
.RS 0n
|
||||
#define CMARK_OPT_UNSAFE (1 << 17)
|
||||
.RE
|
||||
\f[]
|
||||
.fi
|
||||
|
||||
.PP
|
||||
Render raw HTML and unsafe links (\f[C]javascript:\f[],
|
||||
\f[C]vbscript:\f[], \f[C]file:\f[], and \f[C]data:\f[], except for
|
||||
\f[C]image/png\f[], \f[C]image/gif\f[], \f[C]image/jpeg\f[], or
|
||||
\f[C]image/webp\f[] mime types). By default, raw HTML is replaced by a
|
||||
placeholder HTML comment. Unsafe links are replaced by empty strings.
|
||||
|
||||
.PP
|
||||
.nf
|
||||
\fC
|
||||
.RS 0n
|
||||
#define CMARK_OPT_NOBREAKS (1 << 4)
|
||||
.RE
|
||||
\f[]
|
||||
.fi
|
||||
|
||||
.PP
|
||||
Render \f[C]softbreak\f[] elements as spaces.
|
||||
|
||||
.SS
|
||||
Options affecting parsing
|
||||
|
||||
.PP
|
||||
.nf
|
||||
\fC
|
||||
.RS 0n
|
||||
#define CMARK_OPT_NORMALIZE (1 << 8)
|
||||
.RE
|
||||
\f[]
|
||||
.fi
|
||||
|
||||
.PP
|
||||
Legacy option (no effect).
|
||||
|
||||
.PP
|
||||
.nf
|
||||
\fC
|
||||
.RS 0n
|
||||
#define CMARK_OPT_VALIDATE_UTF8 (1 << 9)
|
||||
.RE
|
||||
\f[]
|
||||
.fi
|
||||
|
||||
.PP
|
||||
Validate UTF\-8 in the input before parsing, replacing illegal sequences
|
||||
with the replacement character U+FFFD.
|
||||
|
||||
.PP
|
||||
.nf
|
||||
\fC
|
||||
.RS 0n
|
||||
#define CMARK_OPT_SMART (1 << 10)
|
||||
.RE
|
||||
\f[]
|
||||
.fi
|
||||
|
||||
.PP
|
||||
Convert straight quotes to curly, \-\-\- to em dashes, \-\- to en
|
||||
dashes.
|
||||
|
||||
.SS
|
||||
Version information
|
||||
|
||||
.PP
|
||||
\fIint\f[] \fBcmark_version\f[](\fIvoid\f[])
|
||||
|
||||
.PP
|
||||
The library version as integer for runtime checks. Also available as
|
||||
macro CMARK_VERSION for compile time checks.
|
||||
.IP \[bu] 2
|
||||
Bits 16\-23 contain the major version.
|
||||
.IP \[bu] 2
|
||||
Bits 8\-15 contain the minor version.
|
||||
.IP \[bu] 2
|
||||
Bits 0\-7 contain the patchlevel.
|
||||
.PP
|
||||
In hexadecimal format, the number 0x010203 represents version 1.2.3.
|
||||
|
||||
.PP
|
||||
\fIconst char *\f[] \fBcmark_version_string\f[](\fIvoid\f[])
|
||||
|
||||
.PP
|
||||
The library version string for runtime checks. Also available as macro
|
||||
CMARK_VERSION_STRING for compile time checks.
|
||||
|
||||
.SH
|
||||
AUTHORS
|
||||
.PP
|
||||
John MacFarlane, Vicent Marti, Kārlis Gaņģis, Nick Wellnhofer.
|
||||
|
1
deps/cmark/nmake.bat
vendored
Normal file
1
deps/cmark/nmake.bat
vendored
Normal file
@ -0,0 +1 @@
|
||||
@nmake.exe /nologo /f Makefile.nmake %*
|
12
deps/cmark/shell.nix
vendored
Normal file
12
deps/cmark/shell.nix
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
with (import <nixpkgs> {});
|
||||
mkShell {
|
||||
buildInputs = [
|
||||
clangStdenv
|
||||
cmake
|
||||
gdb
|
||||
python3
|
||||
perl
|
||||
re2c
|
||||
curl
|
||||
];
|
||||
}
|
207
deps/cmark/src/CMakeLists.txt
vendored
Normal file
207
deps/cmark/src/CMakeLists.txt
vendored
Normal file
@ -0,0 +1,207 @@
|
||||
if(${CMAKE_VERSION} VERSION_GREATER "3.3")
|
||||
cmake_policy(SET CMP0063 NEW)
|
||||
endif()
|
||||
|
||||
set(LIBRARY "cmark")
|
||||
set(STATICLIBRARY "cmark_static")
|
||||
set(HEADERS
|
||||
cmark.h
|
||||
parser.h
|
||||
buffer.h
|
||||
node.h
|
||||
iterator.h
|
||||
chunk.h
|
||||
references.h
|
||||
utf8.h
|
||||
scanners.h
|
||||
inlines.h
|
||||
houdini.h
|
||||
cmark_ctype.h
|
||||
render.h
|
||||
)
|
||||
set(LIBRARY_SOURCES
|
||||
cmark.c
|
||||
node.c
|
||||
iterator.c
|
||||
blocks.c
|
||||
inlines.c
|
||||
scanners.c
|
||||
scanners.re
|
||||
utf8.c
|
||||
buffer.c
|
||||
references.c
|
||||
render.c
|
||||
man.c
|
||||
xml.c
|
||||
html.c
|
||||
commonmark.c
|
||||
latex.c
|
||||
houdini_href_e.c
|
||||
houdini_html_e.c
|
||||
houdini_html_u.c
|
||||
cmark_ctype.c
|
||||
${HEADERS}
|
||||
)
|
||||
|
||||
set(PROGRAM "cmark_exe")
|
||||
set(PROGRAM_SOURCES main.c)
|
||||
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/cmark_version.h.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/cmark_version.h)
|
||||
|
||||
include(GNUInstallDirs)
|
||||
include (GenerateExportHeader)
|
||||
|
||||
add_executable(${PROGRAM} ${PROGRAM_SOURCES})
|
||||
cmark_add_compile_options(${PROGRAM})
|
||||
set_target_properties(${PROGRAM} PROPERTIES
|
||||
OUTPUT_NAME "cmark")
|
||||
|
||||
if (CMARK_STATIC)
|
||||
target_link_libraries(${PROGRAM} ${STATICLIBRARY})
|
||||
# Disable the PUBLIC declarations when compiling the executable:
|
||||
set_target_properties(${PROGRAM} PROPERTIES
|
||||
COMPILE_FLAGS -DCMARK_STATIC_DEFINE)
|
||||
elseif (CMARK_SHARED)
|
||||
target_link_libraries(${PROGRAM} ${LIBRARY})
|
||||
endif()
|
||||
|
||||
# -fvisibility=hidden
|
||||
set(CMAKE_C_VISIBILITY_PRESET hidden)
|
||||
set(CMAKE_VISIBILITY_INLINES_HIDDEN 1)
|
||||
|
||||
if (CMARK_SHARED)
|
||||
add_library(${LIBRARY} SHARED ${LIBRARY_SOURCES})
|
||||
cmark_add_compile_options(${LIBRARY})
|
||||
set_target_properties(${LIBRARY} PROPERTIES
|
||||
MACOSX_RPATH TRUE
|
||||
OUTPUT_NAME "cmark"
|
||||
# Avoid name clash between PROGRAM and LIBRARY pdb files.
|
||||
PDB_NAME cmark_dll
|
||||
# Include minor version and patch level in soname for now.
|
||||
SOVERSION ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}
|
||||
VERSION ${PROJECT_VERSION})
|
||||
target_include_directories(${LIBRARY} INTERFACE
|
||||
$<INSTALL_INTERFACE:include>
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>)
|
||||
add_library(cmark::cmark ALIAS ${LIBRARY})
|
||||
|
||||
generate_export_header(${LIBRARY}
|
||||
BASE_NAME ${PROJECT_NAME})
|
||||
|
||||
list(APPEND CMARK_INSTALL ${LIBRARY})
|
||||
endif()
|
||||
|
||||
if (CMARK_STATIC)
|
||||
add_library(${STATICLIBRARY} STATIC ${LIBRARY_SOURCES})
|
||||
cmark_add_compile_options(${STATICLIBRARY})
|
||||
set_target_properties(${STATICLIBRARY} PROPERTIES
|
||||
COMPILE_FLAGS -DCMARK_STATIC_DEFINE
|
||||
POSITION_INDEPENDENT_CODE ON
|
||||
VERSION ${PROJECT_VERSION})
|
||||
if(MSVC)
|
||||
set_target_properties(${STATICLIBRARY} PROPERTIES
|
||||
OUTPUT_NAME cmark_static)
|
||||
else()
|
||||
set_target_properties(${STATICLIBRARY} PROPERTIES
|
||||
OUTPUT_NAME cmark)
|
||||
endif()
|
||||
target_include_directories(${STATICLIBRARY} INTERFACE
|
||||
$<INSTALL_INTERFACE:include>
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>)
|
||||
add_library(cmark::cmark_static ALIAS ${STATICLIBRARY})
|
||||
|
||||
if (NOT CMARK_SHARED)
|
||||
generate_export_header(${STATICLIBRARY}
|
||||
BASE_NAME ${PROJECT_NAME})
|
||||
endif()
|
||||
|
||||
list(APPEND CMARK_INSTALL ${STATICLIBRARY})
|
||||
endif()
|
||||
|
||||
if (MSVC)
|
||||
set_property(TARGET ${PROGRAM}
|
||||
APPEND PROPERTY LINK_FLAGS /INCREMENTAL:NO)
|
||||
endif(MSVC)
|
||||
|
||||
if(NOT MSVC OR CMAKE_HOST_SYSTEM_NAME STREQUAL Windows)
|
||||
set(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_NO_WARNINGS ON)
|
||||
include(InstallRequiredSystemLibraries)
|
||||
endif()
|
||||
|
||||
install(TARGETS ${PROGRAM} ${CMARK_INSTALL}
|
||||
EXPORT cmark-targets
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
)
|
||||
|
||||
if(CMARK_SHARED OR CMARK_STATIC)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/libcmark.pc.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/libcmark.pc @ONLY)
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libcmark.pc
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
|
||||
|
||||
install(FILES
|
||||
cmark.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/cmark_export.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/cmark_version.h
|
||||
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
|
||||
)
|
||||
|
||||
# Include module for functions
|
||||
# - 'write_basic_package_version_file'
|
||||
# - 'configure_package_config_file'
|
||||
include(CMakePackageConfigHelpers)
|
||||
# generate cmark-config.cmake and cmark-config-version.cmake files
|
||||
configure_package_config_file(
|
||||
"cmarkConfig.cmake.in"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/generated/cmark-config.cmake"
|
||||
INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/cmark")
|
||||
write_basic_package_version_file(
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/generated/cmark-config-version.cmake"
|
||||
VERSION ${PROJECT_VERSION}
|
||||
COMPATIBILITY SameMajorVersion)
|
||||
# install config and version file
|
||||
install(
|
||||
FILES "${CMAKE_CURRENT_BINARY_DIR}/generated/cmark-config.cmake"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/generated/cmark-config-version.cmake"
|
||||
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/cmark"
|
||||
)
|
||||
# install targets file
|
||||
install(
|
||||
EXPORT "cmark-targets"
|
||||
NAMESPACE "cmark::"
|
||||
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/cmark"
|
||||
)
|
||||
|
||||
endif()
|
||||
|
||||
# Feature tests
|
||||
include(CheckIncludeFile)
|
||||
include(CheckCSourceCompiles)
|
||||
CHECK_INCLUDE_FILE(stdbool.h HAVE_STDBOOL_H)
|
||||
CHECK_C_SOURCE_COMPILES(
|
||||
"int main() { __builtin_expect(0,0); return 0; }"
|
||||
HAVE___BUILTIN_EXPECT)
|
||||
CHECK_C_SOURCE_COMPILES("
|
||||
int f(void) __attribute__ (());
|
||||
int main() { return 0; }
|
||||
" HAVE___ATTRIBUTE__)
|
||||
|
||||
CONFIGURE_FILE(
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/config.h.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/config.h)
|
||||
|
||||
if(CMARK_LIB_FUZZER)
|
||||
add_executable(cmark-fuzz ../test/cmark-fuzz.c ${LIBRARY_SOURCES})
|
||||
cmark_add_compile_options(cmark-fuzz)
|
||||
target_link_libraries(cmark-fuzz "${CMAKE_LIB_FUZZER_PATH}")
|
||||
|
||||
# cmark is written in C but the libFuzzer runtime is written in C++ which
|
||||
# needs to link against the C++ runtime.
|
||||
set_target_properties(cmark-fuzz PROPERTIES
|
||||
LINKER_LANGUAGE CXX)
|
||||
endif()
|
1315
deps/cmark/src/blocks.c
vendored
Normal file
1315
deps/cmark/src/blocks.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
278
deps/cmark/src/buffer.c
vendored
Normal file
278
deps/cmark/src/buffer.c
vendored
Normal file
@ -0,0 +1,278 @@
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "cmark_ctype.h"
|
||||
#include "buffer.h"
|
||||
|
||||
/* Used as default value for cmark_strbuf->ptr so that people can always
|
||||
* assume ptr is non-NULL and zero terminated even for new cmark_strbufs.
|
||||
*/
|
||||
unsigned char cmark_strbuf__initbuf[1];
|
||||
|
||||
#ifndef MIN
|
||||
#define MIN(x, y) ((x < y) ? x : y)
|
||||
#endif
|
||||
|
||||
void cmark_strbuf_init(cmark_mem *mem, cmark_strbuf *buf,
|
||||
bufsize_t initial_size) {
|
||||
buf->mem = mem;
|
||||
buf->asize = 0;
|
||||
buf->size = 0;
|
||||
buf->ptr = cmark_strbuf__initbuf;
|
||||
|
||||
if (initial_size > 0)
|
||||
cmark_strbuf_grow(buf, initial_size);
|
||||
}
|
||||
|
||||
static CMARK_INLINE void S_strbuf_grow_by(cmark_strbuf *buf, bufsize_t add) {
|
||||
cmark_strbuf_grow(buf, buf->size + add);
|
||||
}
|
||||
|
||||
void cmark_strbuf_grow(cmark_strbuf *buf, bufsize_t target_size) {
|
||||
assert(target_size > 0);
|
||||
|
||||
if (target_size < buf->asize)
|
||||
return;
|
||||
|
||||
if (target_size > (bufsize_t)(INT32_MAX / 2)) {
|
||||
fprintf(stderr,
|
||||
"[cmark] cmark_strbuf_grow requests buffer with size > %d, aborting\n",
|
||||
(INT32_MAX / 2));
|
||||
abort();
|
||||
}
|
||||
|
||||
/* Oversize the buffer by 50% to guarantee amortized linear time
|
||||
* complexity on append operations. */
|
||||
bufsize_t new_size = target_size + target_size / 2;
|
||||
new_size += 1;
|
||||
new_size = (new_size + 7) & ~7;
|
||||
|
||||
buf->ptr = (unsigned char *)buf->mem->realloc(buf->asize ? buf->ptr : NULL,
|
||||
new_size);
|
||||
buf->asize = new_size;
|
||||
}
|
||||
|
||||
bufsize_t cmark_strbuf_len(const cmark_strbuf *buf) { return buf->size; }
|
||||
|
||||
void cmark_strbuf_free(cmark_strbuf *buf) {
|
||||
if (!buf)
|
||||
return;
|
||||
|
||||
if (buf->ptr != cmark_strbuf__initbuf)
|
||||
buf->mem->free(buf->ptr);
|
||||
|
||||
cmark_strbuf_init(buf->mem, buf, 0);
|
||||
}
|
||||
|
||||
void cmark_strbuf_clear(cmark_strbuf *buf) {
|
||||
buf->size = 0;
|
||||
|
||||
if (buf->asize > 0)
|
||||
buf->ptr[0] = '\0';
|
||||
}
|
||||
|
||||
void cmark_strbuf_set(cmark_strbuf *buf, const unsigned char *data,
|
||||
bufsize_t len) {
|
||||
if (len <= 0 || data == NULL) {
|
||||
cmark_strbuf_clear(buf);
|
||||
} else {
|
||||
if (data != buf->ptr) {
|
||||
if (len >= buf->asize)
|
||||
cmark_strbuf_grow(buf, len);
|
||||
memmove(buf->ptr, data, len);
|
||||
}
|
||||
buf->size = len;
|
||||
buf->ptr[buf->size] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
void cmark_strbuf_sets(cmark_strbuf *buf, const char *string) {
|
||||
cmark_strbuf_set(buf, (const unsigned char *)string,
|
||||
string ? strlen(string) : 0);
|
||||
}
|
||||
|
||||
void cmark_strbuf_putc(cmark_strbuf *buf, int c) {
|
||||
S_strbuf_grow_by(buf, 1);
|
||||
buf->ptr[buf->size++] = (unsigned char)(c & 0xFF);
|
||||
buf->ptr[buf->size] = '\0';
|
||||
}
|
||||
|
||||
void cmark_strbuf_put(cmark_strbuf *buf, const unsigned char *data,
|
||||
bufsize_t len) {
|
||||
if (len <= 0)
|
||||
return;
|
||||
|
||||
S_strbuf_grow_by(buf, len);
|
||||
memmove(buf->ptr + buf->size, data, len);
|
||||
buf->size += len;
|
||||
buf->ptr[buf->size] = '\0';
|
||||
}
|
||||
|
||||
void cmark_strbuf_puts(cmark_strbuf *buf, const char *string) {
|
||||
cmark_strbuf_put(buf, (const unsigned char *)string, strlen(string));
|
||||
}
|
||||
|
||||
void cmark_strbuf_copy_cstr(char *data, bufsize_t datasize,
|
||||
const cmark_strbuf *buf) {
|
||||
bufsize_t copylen;
|
||||
|
||||
assert(buf);
|
||||
if (!data || datasize <= 0)
|
||||
return;
|
||||
|
||||
data[0] = '\0';
|
||||
|
||||
if (buf->size == 0 || buf->asize <= 0)
|
||||
return;
|
||||
|
||||
copylen = buf->size;
|
||||
if (copylen > datasize - 1)
|
||||
copylen = datasize - 1;
|
||||
memmove(data, buf->ptr, copylen);
|
||||
data[copylen] = '\0';
|
||||
}
|
||||
|
||||
void cmark_strbuf_swap(cmark_strbuf *buf_a, cmark_strbuf *buf_b) {
|
||||
cmark_strbuf t = *buf_a;
|
||||
*buf_a = *buf_b;
|
||||
*buf_b = t;
|
||||
}
|
||||
|
||||
unsigned char *cmark_strbuf_detach(cmark_strbuf *buf) {
|
||||
unsigned char *data = buf->ptr;
|
||||
|
||||
if (buf->asize == 0) {
|
||||
/* return an empty string */
|
||||
return (unsigned char *)buf->mem->calloc(1, 1);
|
||||
}
|
||||
|
||||
cmark_strbuf_init(buf->mem, buf, 0);
|
||||
return data;
|
||||
}
|
||||
|
||||
int cmark_strbuf_cmp(const cmark_strbuf *a, const cmark_strbuf *b) {
|
||||
int result = memcmp(a->ptr, b->ptr, MIN(a->size, b->size));
|
||||
return (result != 0) ? result
|
||||
: (a->size < b->size) ? -1 : (a->size > b->size) ? 1 : 0;
|
||||
}
|
||||
|
||||
bufsize_t cmark_strbuf_strchr(const cmark_strbuf *buf, int c, bufsize_t pos) {
|
||||
if (pos >= buf->size)
|
||||
return -1;
|
||||
if (pos < 0)
|
||||
pos = 0;
|
||||
|
||||
const unsigned char *p =
|
||||
(unsigned char *)memchr(buf->ptr + pos, c, buf->size - pos);
|
||||
if (!p)
|
||||
return -1;
|
||||
|
||||
return (bufsize_t)(p - (const unsigned char *)buf->ptr);
|
||||
}
|
||||
|
||||
bufsize_t cmark_strbuf_strrchr(const cmark_strbuf *buf, int c, bufsize_t pos) {
|
||||
if (pos < 0 || buf->size == 0)
|
||||
return -1;
|
||||
if (pos >= buf->size)
|
||||
pos = buf->size - 1;
|
||||
|
||||
bufsize_t i;
|
||||
for (i = pos; i >= 0; i--) {
|
||||
if (buf->ptr[i] == (unsigned char)c)
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void cmark_strbuf_truncate(cmark_strbuf *buf, bufsize_t len) {
|
||||
if (len < 0)
|
||||
len = 0;
|
||||
|
||||
if (len < buf->size) {
|
||||
buf->size = len;
|
||||
buf->ptr[buf->size] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
void cmark_strbuf_drop(cmark_strbuf *buf, bufsize_t n) {
|
||||
if (n > 0) {
|
||||
if (n > buf->size)
|
||||
n = buf->size;
|
||||
buf->size = buf->size - n;
|
||||
if (buf->size)
|
||||
memmove(buf->ptr, buf->ptr + n, buf->size);
|
||||
|
||||
buf->ptr[buf->size] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
void cmark_strbuf_rtrim(cmark_strbuf *buf) {
|
||||
if (!buf->size)
|
||||
return;
|
||||
|
||||
while (buf->size > 0) {
|
||||
if (!cmark_isspace(buf->ptr[buf->size - 1]))
|
||||
break;
|
||||
|
||||
buf->size--;
|
||||
}
|
||||
|
||||
buf->ptr[buf->size] = '\0';
|
||||
}
|
||||
|
||||
void cmark_strbuf_trim(cmark_strbuf *buf) {
|
||||
bufsize_t i = 0;
|
||||
|
||||
if (!buf->size)
|
||||
return;
|
||||
|
||||
while (i < buf->size && cmark_isspace(buf->ptr[i]))
|
||||
i++;
|
||||
|
||||
cmark_strbuf_drop(buf, i);
|
||||
|
||||
cmark_strbuf_rtrim(buf);
|
||||
}
|
||||
|
||||
// Destructively modify string, collapsing consecutive
|
||||
// space and newline characters into a single space.
|
||||
void cmark_strbuf_normalize_whitespace(cmark_strbuf *s) {
|
||||
bool last_char_was_space = false;
|
||||
bufsize_t r, w;
|
||||
|
||||
for (r = 0, w = 0; r < s->size; ++r) {
|
||||
if (cmark_isspace(s->ptr[r])) {
|
||||
if (!last_char_was_space) {
|
||||
s->ptr[w++] = ' ';
|
||||
last_char_was_space = true;
|
||||
}
|
||||
} else {
|
||||
s->ptr[w++] = s->ptr[r];
|
||||
last_char_was_space = false;
|
||||
}
|
||||
}
|
||||
|
||||
cmark_strbuf_truncate(s, w);
|
||||
}
|
||||
|
||||
// Destructively unescape a string: remove backslashes before punctuation chars.
|
||||
extern void cmark_strbuf_unescape(cmark_strbuf *buf) {
|
||||
bufsize_t r, w;
|
||||
|
||||
for (r = 0, w = 0; r < buf->size; ++r) {
|
||||
if (buf->ptr[r] == '\\' && cmark_ispunct(buf->ptr[r + 1]))
|
||||
r++;
|
||||
|
||||
buf->ptr[w++] = buf->ptr[r];
|
||||
}
|
||||
|
||||
cmark_strbuf_truncate(buf, w);
|
||||
}
|
84
deps/cmark/src/buffer.h
vendored
Normal file
84
deps/cmark/src/buffer.h
vendored
Normal file
@ -0,0 +1,84 @@
|
||||
#ifndef CMARK_BUFFER_H
|
||||
#define CMARK_BUFFER_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
#include <stdint.h>
|
||||
#include "config.h"
|
||||
#include "cmark.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef int32_t bufsize_t;
|
||||
|
||||
typedef struct {
|
||||
cmark_mem *mem;
|
||||
unsigned char *ptr;
|
||||
bufsize_t asize, size;
|
||||
} cmark_strbuf;
|
||||
|
||||
extern unsigned char cmark_strbuf__initbuf[];
|
||||
|
||||
#define CMARK_BUF_INIT(mem) \
|
||||
{ mem, cmark_strbuf__initbuf, 0, 0 }
|
||||
|
||||
/**
|
||||
* Initialize a cmark_strbuf structure.
|
||||
*
|
||||
* For the cases where CMARK_BUF_INIT cannot be used to do static
|
||||
* initialization.
|
||||
*/
|
||||
void cmark_strbuf_init(cmark_mem *mem, cmark_strbuf *buf,
|
||||
bufsize_t initial_size);
|
||||
|
||||
/**
|
||||
* Grow the buffer to hold at least `target_size` bytes.
|
||||
*/
|
||||
void cmark_strbuf_grow(cmark_strbuf *buf, bufsize_t target_size);
|
||||
|
||||
void cmark_strbuf_free(cmark_strbuf *buf);
|
||||
void cmark_strbuf_swap(cmark_strbuf *buf_a, cmark_strbuf *buf_b);
|
||||
|
||||
bufsize_t cmark_strbuf_len(const cmark_strbuf *buf);
|
||||
|
||||
int cmark_strbuf_cmp(const cmark_strbuf *a, const cmark_strbuf *b);
|
||||
|
||||
unsigned char *cmark_strbuf_detach(cmark_strbuf *buf);
|
||||
void cmark_strbuf_copy_cstr(char *data, bufsize_t datasize,
|
||||
const cmark_strbuf *buf);
|
||||
|
||||
/*
|
||||
static CMARK_INLINE const char *cmark_strbuf_cstr(const cmark_strbuf *buf) {
|
||||
return (char *)buf->ptr;
|
||||
}
|
||||
*/
|
||||
|
||||
#define cmark_strbuf_at(buf, n) ((buf)->ptr[n])
|
||||
|
||||
void cmark_strbuf_set(cmark_strbuf *buf, const unsigned char *data,
|
||||
bufsize_t len);
|
||||
void cmark_strbuf_sets(cmark_strbuf *buf, const char *string);
|
||||
void cmark_strbuf_putc(cmark_strbuf *buf, int c);
|
||||
void cmark_strbuf_put(cmark_strbuf *buf, const unsigned char *data,
|
||||
bufsize_t len);
|
||||
void cmark_strbuf_puts(cmark_strbuf *buf, const char *string);
|
||||
void cmark_strbuf_clear(cmark_strbuf *buf);
|
||||
|
||||
bufsize_t cmark_strbuf_strchr(const cmark_strbuf *buf, int c, bufsize_t pos);
|
||||
bufsize_t cmark_strbuf_strrchr(const cmark_strbuf *buf, int c, bufsize_t pos);
|
||||
void cmark_strbuf_drop(cmark_strbuf *buf, bufsize_t n);
|
||||
void cmark_strbuf_truncate(cmark_strbuf *buf, bufsize_t len);
|
||||
void cmark_strbuf_rtrim(cmark_strbuf *buf);
|
||||
void cmark_strbuf_trim(cmark_strbuf *buf);
|
||||
void cmark_strbuf_normalize_whitespace(cmark_strbuf *s);
|
||||
void cmark_strbuf_unescape(cmark_strbuf *s);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
4714
deps/cmark/src/case_fold_switch.inc
vendored
Normal file
4714
deps/cmark/src/case_fold_switch.inc
vendored
Normal file
File diff suppressed because it is too large
Load Diff
69
deps/cmark/src/chunk.h
vendored
Normal file
69
deps/cmark/src/chunk.h
vendored
Normal file
@ -0,0 +1,69 @@
|
||||
#ifndef CMARK_CHUNK_H
|
||||
#define CMARK_CHUNK_H
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include "cmark.h"
|
||||
#include "buffer.h"
|
||||
#include "cmark_ctype.h"
|
||||
|
||||
#define CMARK_CHUNK_EMPTY \
|
||||
{ NULL, 0 }
|
||||
|
||||
typedef struct {
|
||||
const unsigned char *data;
|
||||
bufsize_t len;
|
||||
} cmark_chunk;
|
||||
|
||||
// NOLINTNEXTLINE(clang-diagnostic-unused-function)
|
||||
static CMARK_INLINE void cmark_chunk_free(cmark_chunk *c) {
|
||||
c->data = NULL;
|
||||
c->len = 0;
|
||||
}
|
||||
|
||||
static CMARK_INLINE void cmark_chunk_ltrim(cmark_chunk *c) {
|
||||
while (c->len && cmark_isspace(c->data[0])) {
|
||||
c->data++;
|
||||
c->len--;
|
||||
}
|
||||
}
|
||||
|
||||
static CMARK_INLINE void cmark_chunk_rtrim(cmark_chunk *c) {
|
||||
while (c->len > 0) {
|
||||
if (!cmark_isspace(c->data[c->len - 1]))
|
||||
break;
|
||||
|
||||
c->len--;
|
||||
}
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(clang-diagnostic-unused-function)
|
||||
static CMARK_INLINE void cmark_chunk_trim(cmark_chunk *c) {
|
||||
cmark_chunk_ltrim(c);
|
||||
cmark_chunk_rtrim(c);
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(clang-diagnostic-unused-function)
|
||||
static CMARK_INLINE bufsize_t cmark_chunk_strchr(cmark_chunk *ch, int c,
|
||||
bufsize_t offset) {
|
||||
const unsigned char *p =
|
||||
(unsigned char *)memchr(ch->data + offset, c, ch->len - offset);
|
||||
return p ? (bufsize_t)(p - ch->data) : ch->len;
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(clang-diagnostic-unused-function)
|
||||
static CMARK_INLINE cmark_chunk cmark_chunk_literal(const char *data) {
|
||||
bufsize_t len = data ? (bufsize_t)strlen(data) : 0;
|
||||
cmark_chunk c = {(unsigned char *)data, len};
|
||||
return c;
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(clang-diagnostic-unused-function)
|
||||
static CMARK_INLINE cmark_chunk cmark_chunk_dup(const cmark_chunk *ch,
|
||||
bufsize_t pos, bufsize_t len) {
|
||||
cmark_chunk c = {ch->data + pos, len};
|
||||
return c;
|
||||
}
|
||||
|
||||
#endif
|
48
deps/cmark/src/cmark.c
vendored
Normal file
48
deps/cmark/src/cmark.c
vendored
Normal file
@ -0,0 +1,48 @@
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include "node.h"
|
||||
#include "houdini.h"
|
||||
#include "cmark.h"
|
||||
#include "buffer.h"
|
||||
|
||||
int cmark_version(void) { return CMARK_VERSION; }
|
||||
|
||||
const char *cmark_version_string(void) { return CMARK_VERSION_STRING; }
|
||||
|
||||
static void *xcalloc(size_t nmem, size_t size) {
|
||||
void *ptr = calloc(nmem, size);
|
||||
if (!ptr) {
|
||||
fprintf(stderr, "[cmark] calloc returned null pointer, aborting\n");
|
||||
abort();
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static void *xrealloc(void *ptr, size_t size) {
|
||||
void *new_ptr = realloc(ptr, size);
|
||||
if (!new_ptr) {
|
||||
fprintf(stderr, "[cmark] realloc returned null pointer, aborting\n");
|
||||
abort();
|
||||
}
|
||||
return new_ptr;
|
||||
}
|
||||
|
||||
cmark_mem DEFAULT_MEM_ALLOCATOR = {xcalloc, xrealloc, free};
|
||||
|
||||
cmark_mem *cmark_get_default_mem_allocator(void) {
|
||||
return &DEFAULT_MEM_ALLOCATOR;
|
||||
}
|
||||
|
||||
|
||||
char *cmark_markdown_to_html(const char *text, size_t len, int options) {
|
||||
cmark_node *doc;
|
||||
char *result;
|
||||
|
||||
doc = cmark_parse_document(text, len, options);
|
||||
|
||||
result = cmark_render_html(doc, options);
|
||||
cmark_node_free(doc);
|
||||
|
||||
return result;
|
||||
}
|
654
deps/cmark/src/cmark.h
vendored
Normal file
654
deps/cmark/src/cmark.h
vendored
Normal file
@ -0,0 +1,654 @@
|
||||
#ifndef CMARK_H
|
||||
#define CMARK_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <cmark_export.h>
|
||||
#include <cmark_version.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** # NAME
|
||||
*
|
||||
* **cmark** - CommonMark parsing, manipulating, and rendering
|
||||
*/
|
||||
|
||||
/** # DESCRIPTION
|
||||
*
|
||||
* ## Simple Interface
|
||||
*/
|
||||
|
||||
/** Convert 'text' (assumed to be a UTF-8 encoded string with length
|
||||
* 'len') from CommonMark Markdown to HTML, returning a null-terminated,
|
||||
* UTF-8-encoded string. It is the caller's responsibility
|
||||
* to free the returned buffer.
|
||||
*/
|
||||
CMARK_EXPORT
|
||||
char *cmark_markdown_to_html(const char *text, size_t len, int options);
|
||||
|
||||
/** ## Node Structure
|
||||
*/
|
||||
|
||||
typedef enum {
|
||||
/* Error status */
|
||||
CMARK_NODE_NONE,
|
||||
|
||||
/* Block */
|
||||
CMARK_NODE_DOCUMENT,
|
||||
CMARK_NODE_BLOCK_QUOTE,
|
||||
CMARK_NODE_LIST,
|
||||
CMARK_NODE_ITEM,
|
||||
CMARK_NODE_CODE_BLOCK,
|
||||
CMARK_NODE_HTML_BLOCK,
|
||||
CMARK_NODE_CUSTOM_BLOCK,
|
||||
CMARK_NODE_PARAGRAPH,
|
||||
CMARK_NODE_HEADING,
|
||||
CMARK_NODE_THEMATIC_BREAK,
|
||||
|
||||
CMARK_NODE_FIRST_BLOCK = CMARK_NODE_DOCUMENT,
|
||||
CMARK_NODE_LAST_BLOCK = CMARK_NODE_THEMATIC_BREAK,
|
||||
|
||||
/* Inline */
|
||||
CMARK_NODE_TEXT,
|
||||
CMARK_NODE_SOFTBREAK,
|
||||
CMARK_NODE_LINEBREAK,
|
||||
CMARK_NODE_CODE,
|
||||
CMARK_NODE_HTML_INLINE,
|
||||
CMARK_NODE_CUSTOM_INLINE,
|
||||
CMARK_NODE_EMPH,
|
||||
CMARK_NODE_STRONG,
|
||||
CMARK_NODE_LINK,
|
||||
CMARK_NODE_IMAGE,
|
||||
|
||||
CMARK_NODE_FIRST_INLINE = CMARK_NODE_TEXT,
|
||||
CMARK_NODE_LAST_INLINE = CMARK_NODE_IMAGE
|
||||
} cmark_node_type;
|
||||
|
||||
/* For backwards compatibility: */
|
||||
#define CMARK_NODE_HEADER CMARK_NODE_HEADING
|
||||
#define CMARK_NODE_HRULE CMARK_NODE_THEMATIC_BREAK
|
||||
#define CMARK_NODE_HTML CMARK_NODE_HTML_BLOCK
|
||||
#define CMARK_NODE_INLINE_HTML CMARK_NODE_HTML_INLINE
|
||||
|
||||
typedef enum {
|
||||
CMARK_NO_LIST,
|
||||
CMARK_BULLET_LIST,
|
||||
CMARK_ORDERED_LIST
|
||||
} cmark_list_type;
|
||||
|
||||
typedef enum {
|
||||
CMARK_NO_DELIM,
|
||||
CMARK_PERIOD_DELIM,
|
||||
CMARK_PAREN_DELIM
|
||||
} cmark_delim_type;
|
||||
|
||||
typedef struct cmark_node cmark_node;
|
||||
typedef struct cmark_parser cmark_parser;
|
||||
typedef struct cmark_iter cmark_iter;
|
||||
|
||||
/**
|
||||
* ## Custom memory allocator support
|
||||
*/
|
||||
|
||||
/** Defines the memory allocation functions to be used by CMark
|
||||
* when parsing and allocating a document tree
|
||||
*/
|
||||
typedef struct cmark_mem {
|
||||
void *(*calloc)(size_t, size_t);
|
||||
void *(*realloc)(void *, size_t);
|
||||
void (*free)(void *);
|
||||
} cmark_mem;
|
||||
|
||||
/** Returns a pointer to the default memory allocator.
|
||||
*/
|
||||
CMARK_EXPORT cmark_mem *cmark_get_default_mem_allocator(void);
|
||||
|
||||
/**
|
||||
* ## Creating and Destroying Nodes
|
||||
*/
|
||||
|
||||
/** Creates a new node of type 'type'. Note that the node may have
|
||||
* other required properties, which it is the caller's responsibility
|
||||
* to assign.
|
||||
*/
|
||||
CMARK_EXPORT cmark_node *cmark_node_new(cmark_node_type type);
|
||||
|
||||
/** Same as `cmark_node_new`, but explicitly listing the memory
|
||||
* allocator used to allocate the node. Note: be sure to use the same
|
||||
* allocator for every node in a tree, or bad things can happen.
|
||||
*/
|
||||
CMARK_EXPORT cmark_node *cmark_node_new_with_mem(cmark_node_type type,
|
||||
cmark_mem *mem);
|
||||
|
||||
/** Frees the memory allocated for a node and any children.
|
||||
*/
|
||||
CMARK_EXPORT void cmark_node_free(cmark_node *node);
|
||||
|
||||
/**
|
||||
* ## Tree Traversal
|
||||
*/
|
||||
|
||||
/** Returns the next node in the sequence after 'node', or NULL if
|
||||
* there is none.
|
||||
*/
|
||||
CMARK_EXPORT cmark_node *cmark_node_next(cmark_node *node);
|
||||
|
||||
/** Returns the previous node in the sequence after 'node', or NULL if
|
||||
* there is none.
|
||||
*/
|
||||
CMARK_EXPORT cmark_node *cmark_node_previous(cmark_node *node);
|
||||
|
||||
/** Returns the parent of 'node', or NULL if there is none.
|
||||
*/
|
||||
CMARK_EXPORT cmark_node *cmark_node_parent(cmark_node *node);
|
||||
|
||||
/** Returns the first child of 'node', or NULL if 'node' has no children.
|
||||
*/
|
||||
CMARK_EXPORT cmark_node *cmark_node_first_child(cmark_node *node);
|
||||
|
||||
/** Returns the last child of 'node', or NULL if 'node' has no children.
|
||||
*/
|
||||
CMARK_EXPORT cmark_node *cmark_node_last_child(cmark_node *node);
|
||||
|
||||
/**
|
||||
* ## Iterator
|
||||
*
|
||||
* An iterator will walk through a tree of nodes, starting from a root
|
||||
* node, returning one node at a time, together with information about
|
||||
* whether the node is being entered or exited. The iterator will
|
||||
* first descend to a child node, if there is one. When there is no
|
||||
* child, the iterator will go to the next sibling. When there is no
|
||||
* next sibling, the iterator will return to the parent (but with
|
||||
* a 'cmark_event_type' of `CMARK_EVENT_EXIT`). The iterator will
|
||||
* return `CMARK_EVENT_DONE` when it reaches the root node again.
|
||||
* One natural application is an HTML renderer, where an `ENTER` event
|
||||
* outputs an open tag and an `EXIT` event outputs a close tag.
|
||||
* An iterator might also be used to transform an AST in some systematic
|
||||
* way, for example, turning all level-3 headings into regular paragraphs.
|
||||
*
|
||||
* void
|
||||
* usage_example(cmark_node *root) {
|
||||
* cmark_event_type ev_type;
|
||||
* cmark_iter *iter = cmark_iter_new(root);
|
||||
*
|
||||
* while ((ev_type = cmark_iter_next(iter)) != CMARK_EVENT_DONE) {
|
||||
* cmark_node *cur = cmark_iter_get_node(iter);
|
||||
* // Do something with `cur` and `ev_type`
|
||||
* }
|
||||
*
|
||||
* cmark_iter_free(iter);
|
||||
* }
|
||||
*
|
||||
* Iterators will never return `EXIT` events for leaf nodes, which are nodes
|
||||
* of type:
|
||||
*
|
||||
* * CMARK_NODE_HTML_BLOCK
|
||||
* * CMARK_NODE_THEMATIC_BREAK
|
||||
* * CMARK_NODE_CODE_BLOCK
|
||||
* * CMARK_NODE_TEXT
|
||||
* * CMARK_NODE_SOFTBREAK
|
||||
* * CMARK_NODE_LINEBREAK
|
||||
* * CMARK_NODE_CODE
|
||||
* * CMARK_NODE_HTML_INLINE
|
||||
*
|
||||
* Nodes must only be modified after an `EXIT` event, or an `ENTER` event for
|
||||
* leaf nodes.
|
||||
*/
|
||||
|
||||
typedef enum {
|
||||
CMARK_EVENT_NONE,
|
||||
CMARK_EVENT_DONE,
|
||||
CMARK_EVENT_ENTER,
|
||||
CMARK_EVENT_EXIT
|
||||
} cmark_event_type;
|
||||
|
||||
/** Creates a new iterator starting at 'root'. The current node and event
|
||||
* type are undefined until 'cmark_iter_next' is called for the first time.
|
||||
* The memory allocated for the iterator should be released using
|
||||
* 'cmark_iter_free' when it is no longer needed.
|
||||
*/
|
||||
CMARK_EXPORT
|
||||
cmark_iter *cmark_iter_new(cmark_node *root);
|
||||
|
||||
/** Frees the memory allocated for an iterator.
|
||||
*/
|
||||
CMARK_EXPORT
|
||||
void cmark_iter_free(cmark_iter *iter);
|
||||
|
||||
/** Advances to the next node and returns the event type (`CMARK_EVENT_ENTER`,
|
||||
* `CMARK_EVENT_EXIT` or `CMARK_EVENT_DONE`).
|
||||
*/
|
||||
CMARK_EXPORT
|
||||
cmark_event_type cmark_iter_next(cmark_iter *iter);
|
||||
|
||||
/** Returns the current node.
|
||||
*/
|
||||
CMARK_EXPORT
|
||||
cmark_node *cmark_iter_get_node(cmark_iter *iter);
|
||||
|
||||
/** Returns the current event type.
|
||||
*/
|
||||
CMARK_EXPORT
|
||||
cmark_event_type cmark_iter_get_event_type(cmark_iter *iter);
|
||||
|
||||
/** Returns the root node.
|
||||
*/
|
||||
CMARK_EXPORT
|
||||
cmark_node *cmark_iter_get_root(cmark_iter *iter);
|
||||
|
||||
/** Resets the iterator so that the current node is 'current' and
|
||||
* the event type is 'event_type'. The new current node must be a
|
||||
* descendant of the root node or the root node itself.
|
||||
*/
|
||||
CMARK_EXPORT
|
||||
void cmark_iter_reset(cmark_iter *iter, cmark_node *current,
|
||||
cmark_event_type event_type);
|
||||
|
||||
/**
|
||||
* ## Accessors
|
||||
*/
|
||||
|
||||
/** Returns the user data of 'node'.
|
||||
*/
|
||||
CMARK_EXPORT void *cmark_node_get_user_data(cmark_node *node);
|
||||
|
||||
/** Sets arbitrary user data for 'node'. Returns 1 on success,
|
||||
* 0 on failure.
|
||||
*/
|
||||
CMARK_EXPORT int cmark_node_set_user_data(cmark_node *node, void *user_data);
|
||||
|
||||
/** Returns the type of 'node', or `CMARK_NODE_NONE` on error.
|
||||
*/
|
||||
CMARK_EXPORT cmark_node_type cmark_node_get_type(cmark_node *node);
|
||||
|
||||
/** Like 'cmark_node_get_type', but returns a string representation
|
||||
of the type, or `"<unknown>"`.
|
||||
*/
|
||||
CMARK_EXPORT
|
||||
const char *cmark_node_get_type_string(cmark_node *node);
|
||||
|
||||
/** Returns the string contents of 'node', or an empty
|
||||
string if none is set. Returns NULL if called on a
|
||||
node that does not have string content.
|
||||
*/
|
||||
CMARK_EXPORT const char *cmark_node_get_literal(cmark_node *node);
|
||||
|
||||
/** Sets the string contents of 'node'. Returns 1 on success,
|
||||
* 0 on failure.
|
||||
*/
|
||||
CMARK_EXPORT int cmark_node_set_literal(cmark_node *node, const char *content);
|
||||
|
||||
/** Returns the heading level of 'node', or 0 if 'node' is not a heading.
|
||||
*/
|
||||
CMARK_EXPORT int cmark_node_get_heading_level(cmark_node *node);
|
||||
|
||||
/* For backwards compatibility */
|
||||
#define cmark_node_get_header_level cmark_node_get_heading_level
|
||||
#define cmark_node_set_header_level cmark_node_set_heading_level
|
||||
|
||||
/** Sets the heading level of 'node', returning 1 on success and 0 on error.
|
||||
*/
|
||||
CMARK_EXPORT int cmark_node_set_heading_level(cmark_node *node, int level);
|
||||
|
||||
/** Returns the list type of 'node', or `CMARK_NO_LIST` if 'node'
|
||||
* is not a list.
|
||||
*/
|
||||
CMARK_EXPORT cmark_list_type cmark_node_get_list_type(cmark_node *node);
|
||||
|
||||
/** Sets the list type of 'node', returning 1 on success and 0 on error.
|
||||
*/
|
||||
CMARK_EXPORT int cmark_node_set_list_type(cmark_node *node,
|
||||
cmark_list_type type);
|
||||
|
||||
/** Returns the list delimiter type of 'node', or `CMARK_NO_DELIM` if 'node'
|
||||
* is not a list.
|
||||
*/
|
||||
CMARK_EXPORT cmark_delim_type cmark_node_get_list_delim(cmark_node *node);
|
||||
|
||||
/** Sets the list delimiter type of 'node', returning 1 on success and 0
|
||||
* on error.
|
||||
*/
|
||||
CMARK_EXPORT int cmark_node_set_list_delim(cmark_node *node,
|
||||
cmark_delim_type delim);
|
||||
|
||||
/** Returns starting number of 'node', if it is an ordered list, otherwise 0.
|
||||
*/
|
||||
CMARK_EXPORT int cmark_node_get_list_start(cmark_node *node);
|
||||
|
||||
/** Sets starting number of 'node', if it is an ordered list. Returns 1
|
||||
* on success, 0 on failure.
|
||||
*/
|
||||
CMARK_EXPORT int cmark_node_set_list_start(cmark_node *node, int start);
|
||||
|
||||
/** Returns 1 if 'node' is a tight list, 0 otherwise.
|
||||
*/
|
||||
CMARK_EXPORT int cmark_node_get_list_tight(cmark_node *node);
|
||||
|
||||
/** Sets the "tightness" of a list. Returns 1 on success, 0 on failure.
|
||||
*/
|
||||
CMARK_EXPORT int cmark_node_set_list_tight(cmark_node *node, int tight);
|
||||
|
||||
/** Returns the info string from a fenced code block.
|
||||
*/
|
||||
CMARK_EXPORT const char *cmark_node_get_fence_info(cmark_node *node);
|
||||
|
||||
/** Sets the info string in a fenced code block, returning 1 on
|
||||
* success and 0 on failure.
|
||||
*/
|
||||
CMARK_EXPORT int cmark_node_set_fence_info(cmark_node *node, const char *info);
|
||||
|
||||
/** Returns the URL of a link or image 'node', or an empty string
|
||||
if no URL is set. Returns NULL if called on a node that is
|
||||
not a link or image.
|
||||
*/
|
||||
CMARK_EXPORT const char *cmark_node_get_url(cmark_node *node);
|
||||
|
||||
/** Sets the URL of a link or image 'node'. Returns 1 on success,
|
||||
* 0 on failure.
|
||||
*/
|
||||
CMARK_EXPORT int cmark_node_set_url(cmark_node *node, const char *url);
|
||||
|
||||
/** Returns the title of a link or image 'node', or an empty
|
||||
string if no title is set. Returns NULL if called on a node
|
||||
that is not a link or image.
|
||||
*/
|
||||
CMARK_EXPORT const char *cmark_node_get_title(cmark_node *node);
|
||||
|
||||
/** Sets the title of a link or image 'node'. Returns 1 on success,
|
||||
* 0 on failure.
|
||||
*/
|
||||
CMARK_EXPORT int cmark_node_set_title(cmark_node *node, const char *title);
|
||||
|
||||
/** Returns the literal "on enter" text for a custom 'node', or
|
||||
an empty string if no on_enter is set. Returns NULL if called
|
||||
on a non-custom node.
|
||||
*/
|
||||
CMARK_EXPORT const char *cmark_node_get_on_enter(cmark_node *node);
|
||||
|
||||
/** Sets the literal text to render "on enter" for a custom 'node'.
|
||||
Any children of the node will be rendered after this text.
|
||||
Returns 1 on success 0 on failure.
|
||||
*/
|
||||
CMARK_EXPORT int cmark_node_set_on_enter(cmark_node *node,
|
||||
const char *on_enter);
|
||||
|
||||
/** Returns the literal "on exit" text for a custom 'node', or
|
||||
an empty string if no on_exit is set. Returns NULL if
|
||||
called on a non-custom node.
|
||||
*/
|
||||
CMARK_EXPORT const char *cmark_node_get_on_exit(cmark_node *node);
|
||||
|
||||
/** Sets the literal text to render "on exit" for a custom 'node'.
|
||||
Any children of the node will be rendered before this text.
|
||||
Returns 1 on success 0 on failure.
|
||||
*/
|
||||
CMARK_EXPORT int cmark_node_set_on_exit(cmark_node *node, const char *on_exit);
|
||||
|
||||
/** Returns the line on which 'node' begins.
|
||||
*/
|
||||
CMARK_EXPORT int cmark_node_get_start_line(cmark_node *node);
|
||||
|
||||
/** Returns the column at which 'node' begins.
|
||||
*/
|
||||
CMARK_EXPORT int cmark_node_get_start_column(cmark_node *node);
|
||||
|
||||
/** Returns the line on which 'node' ends.
|
||||
*/
|
||||
CMARK_EXPORT int cmark_node_get_end_line(cmark_node *node);
|
||||
|
||||
/** Returns the column at which 'node' ends.
|
||||
*/
|
||||
CMARK_EXPORT int cmark_node_get_end_column(cmark_node *node);
|
||||
|
||||
/**
|
||||
* ## Tree Manipulation
|
||||
*/
|
||||
|
||||
/** Unlinks a 'node', removing it from the tree, but not freeing its
|
||||
* memory. (Use 'cmark_node_free' for that.)
|
||||
*/
|
||||
CMARK_EXPORT void cmark_node_unlink(cmark_node *node);
|
||||
|
||||
/** Inserts 'sibling' before 'node'. Returns 1 on success, 0 on failure.
|
||||
*/
|
||||
CMARK_EXPORT int cmark_node_insert_before(cmark_node *node,
|
||||
cmark_node *sibling);
|
||||
|
||||
/** Inserts 'sibling' after 'node'. Returns 1 on success, 0 on failure.
|
||||
*/
|
||||
CMARK_EXPORT int cmark_node_insert_after(cmark_node *node, cmark_node *sibling);
|
||||
|
||||
/** Replaces 'oldnode' with 'newnode' and unlinks 'oldnode' (but does
|
||||
* not free its memory).
|
||||
* Returns 1 on success, 0 on failure.
|
||||
*/
|
||||
CMARK_EXPORT int cmark_node_replace(cmark_node *oldnode, cmark_node *newnode);
|
||||
|
||||
/** Adds 'child' to the beginning of the children of 'node'.
|
||||
* Returns 1 on success, 0 on failure.
|
||||
*/
|
||||
CMARK_EXPORT int cmark_node_prepend_child(cmark_node *node, cmark_node *child);
|
||||
|
||||
/** Adds 'child' to the end of the children of 'node'.
|
||||
* Returns 1 on success, 0 on failure.
|
||||
*/
|
||||
CMARK_EXPORT int cmark_node_append_child(cmark_node *node, cmark_node *child);
|
||||
|
||||
/** Consolidates adjacent text nodes.
|
||||
*/
|
||||
CMARK_EXPORT void cmark_consolidate_text_nodes(cmark_node *root);
|
||||
|
||||
/**
|
||||
* ## Parsing
|
||||
*
|
||||
* Simple interface:
|
||||
*
|
||||
* cmark_node *document = cmark_parse_document("Hello *world*", 13,
|
||||
* CMARK_OPT_DEFAULT);
|
||||
*
|
||||
* Streaming interface:
|
||||
*
|
||||
* cmark_parser *parser = cmark_parser_new(CMARK_OPT_DEFAULT);
|
||||
* FILE *fp = fopen("myfile.md", "rb");
|
||||
* while ((bytes = fread(buffer, 1, sizeof(buffer), fp)) > 0) {
|
||||
* cmark_parser_feed(parser, buffer, bytes);
|
||||
* if (bytes < sizeof(buffer)) {
|
||||
* break;
|
||||
* }
|
||||
* }
|
||||
* document = cmark_parser_finish(parser);
|
||||
* cmark_parser_free(parser);
|
||||
*/
|
||||
|
||||
/** Creates a new parser object.
|
||||
*/
|
||||
CMARK_EXPORT
|
||||
cmark_parser *cmark_parser_new(int options);
|
||||
|
||||
/** Creates a new parser object with the given memory allocator
|
||||
*/
|
||||
CMARK_EXPORT
|
||||
cmark_parser *cmark_parser_new_with_mem(int options, cmark_mem *mem);
|
||||
|
||||
/** Frees memory allocated for a parser object.
|
||||
*/
|
||||
CMARK_EXPORT
|
||||
void cmark_parser_free(cmark_parser *parser);
|
||||
|
||||
/** Feeds a string of length 'len' to 'parser'.
|
||||
*/
|
||||
CMARK_EXPORT
|
||||
void cmark_parser_feed(cmark_parser *parser, const char *buffer, size_t len);
|
||||
|
||||
/** Finish parsing and return a pointer to a tree of nodes.
|
||||
*/
|
||||
CMARK_EXPORT
|
||||
cmark_node *cmark_parser_finish(cmark_parser *parser);
|
||||
|
||||
/** Parse a CommonMark document in 'buffer' of length 'len'.
|
||||
* Returns a pointer to a tree of nodes. The memory allocated for
|
||||
* the node tree should be released using 'cmark_node_free'
|
||||
* when it is no longer needed.
|
||||
*/
|
||||
CMARK_EXPORT
|
||||
cmark_node *cmark_parse_document(const char *buffer, size_t len, int options);
|
||||
|
||||
/** Parse a CommonMark document in file 'f', returning a pointer to
|
||||
* a tree of nodes. The memory allocated for the node tree should be
|
||||
* released using 'cmark_node_free' when it is no longer needed.
|
||||
*/
|
||||
CMARK_EXPORT
|
||||
cmark_node *cmark_parse_file(FILE *f, int options);
|
||||
|
||||
/**
|
||||
* ## Rendering
|
||||
*/
|
||||
|
||||
/** Render a 'node' tree as XML. It is the caller's responsibility
|
||||
* to free the returned buffer.
|
||||
*/
|
||||
CMARK_EXPORT
|
||||
char *cmark_render_xml(cmark_node *root, int options);
|
||||
|
||||
/** Render a 'node' tree as an HTML fragment. It is up to the user
|
||||
* to add an appropriate header and footer. It is the caller's
|
||||
* responsibility to free the returned buffer.
|
||||
*/
|
||||
CMARK_EXPORT
|
||||
char *cmark_render_html(cmark_node *root, int options);
|
||||
|
||||
/** Render a 'node' tree as a groff man page, without the header.
|
||||
* It is the caller's responsibility to free the returned buffer.
|
||||
*/
|
||||
CMARK_EXPORT
|
||||
char *cmark_render_man(cmark_node *root, int options, int width);
|
||||
|
||||
/** Render a 'node' tree as a commonmark document.
|
||||
* It is the caller's responsibility to free the returned buffer.
|
||||
*/
|
||||
CMARK_EXPORT
|
||||
char *cmark_render_commonmark(cmark_node *root, int options, int width);
|
||||
|
||||
/** Render a 'node' tree as a LaTeX document.
|
||||
* It is the caller's responsibility to free the returned buffer.
|
||||
*/
|
||||
CMARK_EXPORT
|
||||
char *cmark_render_latex(cmark_node *root, int options, int width);
|
||||
|
||||
/**
|
||||
* ## Options
|
||||
*/
|
||||
|
||||
/** Default options.
|
||||
*/
|
||||
#define CMARK_OPT_DEFAULT 0
|
||||
|
||||
/**
|
||||
* ### Options affecting rendering
|
||||
*/
|
||||
|
||||
/** Include a `data-sourcepos` attribute on all block elements.
|
||||
*/
|
||||
#define CMARK_OPT_SOURCEPOS (1 << 1)
|
||||
|
||||
/** Render `softbreak` elements as hard line breaks.
|
||||
*/
|
||||
#define CMARK_OPT_HARDBREAKS (1 << 2)
|
||||
|
||||
/** `CMARK_OPT_SAFE` is defined here for API compatibility,
|
||||
but it no longer has any effect. "Safe" mode is now the default:
|
||||
set `CMARK_OPT_UNSAFE` to disable it.
|
||||
*/
|
||||
#define CMARK_OPT_SAFE (1 << 3)
|
||||
|
||||
/** Render raw HTML and unsafe links (`javascript:`, `vbscript:`,
|
||||
* `file:`, and `data:`, except for `image/png`, `image/gif`,
|
||||
* `image/jpeg`, or `image/webp` mime types). By default,
|
||||
* raw HTML is replaced by a placeholder HTML comment. Unsafe
|
||||
* links are replaced by empty strings.
|
||||
*/
|
||||
#define CMARK_OPT_UNSAFE (1 << 17)
|
||||
|
||||
/** Render `softbreak` elements as spaces.
|
||||
*/
|
||||
#define CMARK_OPT_NOBREAKS (1 << 4)
|
||||
|
||||
/**
|
||||
* ### Options affecting parsing
|
||||
*/
|
||||
|
||||
/** Legacy option (no effect).
|
||||
*/
|
||||
#define CMARK_OPT_NORMALIZE (1 << 8)
|
||||
|
||||
/** Validate UTF-8 in the input before parsing, replacing illegal
|
||||
* sequences with the replacement character U+FFFD.
|
||||
*/
|
||||
#define CMARK_OPT_VALIDATE_UTF8 (1 << 9)
|
||||
|
||||
/** Convert straight quotes to curly, --- to em dashes, -- to en dashes.
|
||||
*/
|
||||
#define CMARK_OPT_SMART (1 << 10)
|
||||
|
||||
/**
|
||||
* ## Version information
|
||||
*/
|
||||
|
||||
/** The library version as integer for runtime checks. Also available as
|
||||
* macro CMARK_VERSION for compile time checks.
|
||||
*
|
||||
* * Bits 16-23 contain the major version.
|
||||
* * Bits 8-15 contain the minor version.
|
||||
* * Bits 0-7 contain the patchlevel.
|
||||
*
|
||||
* In hexadecimal format, the number 0x010203 represents version 1.2.3.
|
||||
*/
|
||||
CMARK_EXPORT
|
||||
int cmark_version(void);
|
||||
|
||||
/** The library version string for runtime checks. Also available as
|
||||
* macro CMARK_VERSION_STRING for compile time checks.
|
||||
*/
|
||||
CMARK_EXPORT
|
||||
const char *cmark_version_string(void);
|
||||
|
||||
/** # AUTHORS
|
||||
*
|
||||
* John MacFarlane, Vicent Marti, Kārlis Gaņģis, Nick Wellnhofer.
|
||||
*/
|
||||
|
||||
#ifndef CMARK_NO_SHORT_NAMES
|
||||
#define NODE_DOCUMENT CMARK_NODE_DOCUMENT
|
||||
#define NODE_BLOCK_QUOTE CMARK_NODE_BLOCK_QUOTE
|
||||
#define NODE_LIST CMARK_NODE_LIST
|
||||
#define NODE_ITEM CMARK_NODE_ITEM
|
||||
#define NODE_CODE_BLOCK CMARK_NODE_CODE_BLOCK
|
||||
#define NODE_HTML_BLOCK CMARK_NODE_HTML_BLOCK
|
||||
#define NODE_CUSTOM_BLOCK CMARK_NODE_CUSTOM_BLOCK
|
||||
#define NODE_PARAGRAPH CMARK_NODE_PARAGRAPH
|
||||
#define NODE_HEADING CMARK_NODE_HEADING
|
||||
#define NODE_HEADER CMARK_NODE_HEADER
|
||||
#define NODE_THEMATIC_BREAK CMARK_NODE_THEMATIC_BREAK
|
||||
#define NODE_HRULE CMARK_NODE_HRULE
|
||||
#define NODE_TEXT CMARK_NODE_TEXT
|
||||
#define NODE_SOFTBREAK CMARK_NODE_SOFTBREAK
|
||||
#define NODE_LINEBREAK CMARK_NODE_LINEBREAK
|
||||
#define NODE_CODE CMARK_NODE_CODE
|
||||
#define NODE_HTML_INLINE CMARK_NODE_HTML_INLINE
|
||||
#define NODE_CUSTOM_INLINE CMARK_NODE_CUSTOM_INLINE
|
||||
#define NODE_EMPH CMARK_NODE_EMPH
|
||||
#define NODE_STRONG CMARK_NODE_STRONG
|
||||
#define NODE_LINK CMARK_NODE_LINK
|
||||
#define NODE_IMAGE CMARK_NODE_IMAGE
|
||||
#define BULLET_LIST CMARK_BULLET_LIST
|
||||
#define ORDERED_LIST CMARK_ORDERED_LIST
|
||||
#define PERIOD_DELIM CMARK_PERIOD_DELIM
|
||||
#define PAREN_DELIM CMARK_PAREN_DELIM
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
4
deps/cmark/src/cmarkConfig.cmake.in
vendored
Normal file
4
deps/cmark/src/cmarkConfig.cmake.in
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
@PACKAGE_INIT@
|
||||
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/cmark-targets.cmake")
|
||||
check_required_components("cmark")
|
44
deps/cmark/src/cmark_ctype.c
vendored
Normal file
44
deps/cmark/src/cmark_ctype.c
vendored
Normal file
@ -0,0 +1,44 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#include "cmark_ctype.h"
|
||||
|
||||
/** 1 = space, 2 = punct, 3 = digit, 4 = alpha, 0 = other
|
||||
*/
|
||||
static const uint8_t cmark_ctype_class[256] = {
|
||||
/* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
|
||||
/* 0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0,
|
||||
/* 1 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* 2 */ 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
/* 3 */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2,
|
||||
/* 4 */ 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
||||
/* 5 */ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 2, 2, 2, 2,
|
||||
/* 6 */ 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
||||
/* 7 */ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 2, 2, 2, 0,
|
||||
/* 8 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* 9 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* a */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* b */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* c */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* d */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* e */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* f */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
||||
|
||||
/**
|
||||
* Returns 1 if c is a "whitespace" character as defined by the spec.
|
||||
*/
|
||||
int cmark_isspace(char c) { return cmark_ctype_class[(uint8_t)c] == 1; }
|
||||
|
||||
/**
|
||||
* Returns 1 if c is an ascii punctuation character.
|
||||
*/
|
||||
int cmark_ispunct(char c) { return cmark_ctype_class[(uint8_t)c] == 2; }
|
||||
|
||||
int cmark_isalnum(char c) {
|
||||
uint8_t result;
|
||||
result = cmark_ctype_class[(uint8_t)c];
|
||||
return (result == 3 || result == 4);
|
||||
}
|
||||
|
||||
int cmark_isdigit(char c) { return cmark_ctype_class[(uint8_t)c] == 3; }
|
||||
|
||||
int cmark_isalpha(char c) { return cmark_ctype_class[(uint8_t)c] == 4; }
|
26
deps/cmark/src/cmark_ctype.h
vendored
Normal file
26
deps/cmark/src/cmark_ctype.h
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
#ifndef CMARK_CMARK_CTYPE_H
|
||||
#define CMARK_CMARK_CTYPE_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** Locale-independent versions of functions from ctype.h.
|
||||
* We want cmark to behave the same no matter what the system locale.
|
||||
*/
|
||||
|
||||
int cmark_isspace(char c);
|
||||
|
||||
int cmark_ispunct(char c);
|
||||
|
||||
int cmark_isalnum(char c);
|
||||
|
||||
int cmark_isdigit(char c);
|
||||
|
||||
int cmark_isalpha(char c);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
7
deps/cmark/src/cmark_version.h.in
vendored
Normal file
7
deps/cmark/src/cmark_version.h.in
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
#ifndef CMARK_VERSION_H
|
||||
#define CMARK_VERSION_H
|
||||
|
||||
#define CMARK_VERSION ((@PROJECT_VERSION_MAJOR@ << 16) | (@PROJECT_VERSION_MINOR@ << 8) | @PROJECT_VERSION_PATCH@)
|
||||
#define CMARK_VERSION_STRING "@PROJECT_VERSION_MAJOR@.@PROJECT_VERSION_MINOR@.@PROJECT_VERSION_PATCH@"
|
||||
|
||||
#endif
|
468
deps/cmark/src/commonmark.c
vendored
Normal file
468
deps/cmark/src/commonmark.c
vendored
Normal file
@ -0,0 +1,468 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "cmark.h"
|
||||
#include "node.h"
|
||||
#include "buffer.h"
|
||||
#include "utf8.h"
|
||||
#include "scanners.h"
|
||||
#include "render.h"
|
||||
|
||||
#define OUT(s, wrap, escaping) renderer->out(renderer, s, wrap, escaping)
|
||||
#define LIT(s) renderer->out(renderer, s, false, LITERAL)
|
||||
#define CR() renderer->cr(renderer)
|
||||
#define BLANKLINE() renderer->blankline(renderer)
|
||||
#define ENCODED_SIZE 20
|
||||
#define LISTMARKER_SIZE 20
|
||||
|
||||
// Functions to convert cmark_nodes to commonmark strings.
|
||||
|
||||
static CMARK_INLINE void outc(cmark_renderer *renderer, cmark_escaping escape,
|
||||
int32_t c, unsigned char nextc) {
|
||||
bool needs_escaping = false;
|
||||
bool follows_digit =
|
||||
renderer->buffer->size > 0 &&
|
||||
cmark_isdigit(renderer->buffer->ptr[renderer->buffer->size - 1]);
|
||||
char encoded[ENCODED_SIZE];
|
||||
int options = renderer->options;
|
||||
|
||||
needs_escaping =
|
||||
c < 0x80 && escape != LITERAL &&
|
||||
((escape == NORMAL &&
|
||||
(c < 0x20 ||
|
||||
c == '*' || c == '_' || c == '[' || c == ']' || c == '#' || c == '<' ||
|
||||
c == '>' || c == '\\' || c == '`' || c == '!' ||
|
||||
(c == '&' && cmark_isalpha(nextc)) || (c == '!' && nextc == '[') ||
|
||||
((CMARK_OPT_SMART & options) &&
|
||||
((c == '-' && nextc == '-') ||
|
||||
(c == '.' && nextc == '.') ||
|
||||
c == '"' || c == '\'')) ||
|
||||
(renderer->begin_content && (c == '-' || c == '+' || c == '=') &&
|
||||
// begin_content doesn't get set to false til we've passed digits
|
||||
// at the beginning of line, so...
|
||||
!follows_digit) ||
|
||||
(renderer->begin_content && (c == '.' || c == ')') && follows_digit &&
|
||||
(nextc == 0 || cmark_isspace(nextc))))) ||
|
||||
(escape == URL &&
|
||||
(c == '`' || c == '<' || c == '>' || cmark_isspace(c) || c == '\\' ||
|
||||
c == ')' || c == '(')) ||
|
||||
(escape == TITLE &&
|
||||
(c == '`' || c == '<' || c == '>' || c == '"' || c == '\\')));
|
||||
|
||||
if (needs_escaping) {
|
||||
if (escape == URL && cmark_isspace(c)) {
|
||||
// use percent encoding for spaces
|
||||
snprintf(encoded, ENCODED_SIZE, "%%%2X", c);
|
||||
cmark_strbuf_puts(renderer->buffer, encoded);
|
||||
renderer->column += 3;
|
||||
} else if (cmark_ispunct(c)) {
|
||||
cmark_render_ascii(renderer, "\\");
|
||||
cmark_render_code_point(renderer, c);
|
||||
} else { // render as entity
|
||||
snprintf(encoded, ENCODED_SIZE, "&#%d;", c);
|
||||
cmark_strbuf_puts(renderer->buffer, encoded);
|
||||
renderer->column += strlen(encoded);
|
||||
}
|
||||
} else {
|
||||
cmark_render_code_point(renderer, c);
|
||||
}
|
||||
}
|
||||
|
||||
static int longest_backtick_sequence(const char *code) {
|
||||
int longest = 0;
|
||||
int current = 0;
|
||||
size_t i = 0;
|
||||
size_t code_len = strlen(code);
|
||||
while (i <= code_len) {
|
||||
if (code[i] == '`') {
|
||||
current++;
|
||||
} else {
|
||||
if (current > longest) {
|
||||
longest = current;
|
||||
}
|
||||
current = 0;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
return longest;
|
||||
}
|
||||
|
||||
static int shortest_unused_backtick_sequence(const char *code) {
|
||||
// note: if the shortest sequence is >= 32, this returns 32
|
||||
// so as not to overflow the bit array.
|
||||
uint32_t used = 1;
|
||||
int current = 0;
|
||||
size_t i = 0;
|
||||
size_t code_len = strlen(code);
|
||||
while (i <= code_len) {
|
||||
if (code[i] == '`') {
|
||||
current++;
|
||||
} else {
|
||||
if (current > 0 && current < 32) {
|
||||
used |= (1U << current);
|
||||
}
|
||||
current = 0;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
// return number of first bit that is 0:
|
||||
i = 0;
|
||||
while (i < 32 && used & 1) {
|
||||
used = used >> 1;
|
||||
i++;
|
||||
}
|
||||
return (int)i;
|
||||
}
|
||||
|
||||
static bool is_autolink(cmark_node *node) {
|
||||
const unsigned char *title;
|
||||
const unsigned char *url;
|
||||
cmark_node *link_text;
|
||||
|
||||
if (node->type != CMARK_NODE_LINK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
url = node->as.link.url;
|
||||
if (url == NULL || _scan_scheme(url) == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
title = node->as.link.title;
|
||||
// if it has a title, we can't treat it as an autolink:
|
||||
if (title && title[0]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
link_text = node->first_child;
|
||||
if (link_text == NULL) {
|
||||
return false;
|
||||
}
|
||||
cmark_consolidate_text_nodes(link_text);
|
||||
if (strncmp((const char *)url, "mailto:", 7) == 0) {
|
||||
url += 7;
|
||||
}
|
||||
return link_text->data != NULL &&
|
||||
strcmp((const char *)url, (char *)link_text->data) == 0;
|
||||
}
|
||||
|
||||
static int S_render_node(cmark_renderer *renderer, cmark_node *node,
|
||||
cmark_event_type ev_type, int options) {
|
||||
cmark_node *tmp;
|
||||
int list_number;
|
||||
cmark_delim_type list_delim;
|
||||
size_t numticks;
|
||||
bool extra_spaces;
|
||||
size_t i;
|
||||
bool entering = (ev_type == CMARK_EVENT_ENTER);
|
||||
const char *info, *code, *title;
|
||||
char fencechar[2] = {'\0', '\0'};
|
||||
size_t code_len;
|
||||
char listmarker[LISTMARKER_SIZE];
|
||||
const char *emph_delim;
|
||||
bool first_in_list_item;
|
||||
bufsize_t marker_width;
|
||||
bool has_nonspace;
|
||||
bool allow_wrap = renderer->width > 0 && !(CMARK_OPT_NOBREAKS & options) &&
|
||||
!(CMARK_OPT_HARDBREAKS & options);
|
||||
|
||||
// Don't adjust tight list status til we've started the list.
|
||||
// Otherwise we lose the blank line between a paragraph and
|
||||
// a following list.
|
||||
if (entering) {
|
||||
if (node->parent && node->parent->type == CMARK_NODE_ITEM) {
|
||||
renderer->in_tight_list_item = node->parent->parent->as.list.tight;
|
||||
}
|
||||
} else {
|
||||
if (node->type == CMARK_NODE_LIST) {
|
||||
renderer->in_tight_list_item =
|
||||
node->parent &&
|
||||
node->parent->type == CMARK_NODE_ITEM &&
|
||||
node->parent->parent->as.list.tight;
|
||||
}
|
||||
}
|
||||
|
||||
switch (node->type) {
|
||||
case CMARK_NODE_DOCUMENT:
|
||||
break;
|
||||
|
||||
case CMARK_NODE_BLOCK_QUOTE:
|
||||
if (entering) {
|
||||
LIT("> ");
|
||||
renderer->begin_content = true;
|
||||
cmark_strbuf_puts(renderer->prefix, "> ");
|
||||
} else {
|
||||
cmark_strbuf_truncate(renderer->prefix, renderer->prefix->size - 2);
|
||||
BLANKLINE();
|
||||
}
|
||||
break;
|
||||
|
||||
case CMARK_NODE_LIST:
|
||||
if (!entering && node->next && (node->next->type == CMARK_NODE_LIST)) {
|
||||
// this ensures that a following indented code block or list will be
|
||||
// inteprereted correctly.
|
||||
CR();
|
||||
LIT("<!-- end list -->");
|
||||
BLANKLINE();
|
||||
}
|
||||
break;
|
||||
|
||||
case CMARK_NODE_ITEM:
|
||||
if (cmark_node_get_list_type(node->parent) == CMARK_BULLET_LIST) {
|
||||
marker_width = 4;
|
||||
} else {
|
||||
list_number = cmark_node_get_list_start(node->parent);
|
||||
list_delim = cmark_node_get_list_delim(node->parent);
|
||||
tmp = node;
|
||||
while (tmp->prev) {
|
||||
tmp = tmp->prev;
|
||||
list_number += 1;
|
||||
}
|
||||
// we ensure a width of at least 4 so
|
||||
// we get nice transition from single digits
|
||||
// to double
|
||||
snprintf(listmarker, LISTMARKER_SIZE, "%d%s%s", list_number,
|
||||
list_delim == CMARK_PAREN_DELIM ? ")" : ".",
|
||||
list_number < 10 ? " " : " ");
|
||||
marker_width = strlen(listmarker);
|
||||
}
|
||||
if (entering) {
|
||||
if (cmark_node_get_list_type(node->parent) == CMARK_BULLET_LIST) {
|
||||
LIT(" - ");
|
||||
renderer->begin_content = true;
|
||||
} else {
|
||||
LIT(listmarker);
|
||||
renderer->begin_content = true;
|
||||
}
|
||||
for (i = marker_width; i--;) {
|
||||
cmark_strbuf_putc(renderer->prefix, ' ');
|
||||
}
|
||||
} else {
|
||||
cmark_strbuf_truncate(renderer->prefix,
|
||||
renderer->prefix->size - marker_width);
|
||||
CR();
|
||||
}
|
||||
break;
|
||||
|
||||
case CMARK_NODE_HEADING:
|
||||
if (entering) {
|
||||
for (i = cmark_node_get_heading_level(node); i > 0; i--) {
|
||||
LIT("#");
|
||||
}
|
||||
LIT(" ");
|
||||
renderer->begin_content = true;
|
||||
renderer->no_linebreaks = true;
|
||||
} else {
|
||||
renderer->no_linebreaks = false;
|
||||
BLANKLINE();
|
||||
}
|
||||
break;
|
||||
|
||||
case CMARK_NODE_CODE_BLOCK:
|
||||
|
||||
first_in_list_item = node->prev == NULL && node->parent &&
|
||||
node->parent->type == CMARK_NODE_ITEM;
|
||||
|
||||
if (!first_in_list_item) {
|
||||
BLANKLINE();
|
||||
}
|
||||
info = cmark_node_get_fence_info(node);
|
||||
fencechar[0] = strchr(info, '`') == NULL ? '`' : '~';
|
||||
code = cmark_node_get_literal(node);
|
||||
|
||||
numticks = longest_backtick_sequence(code) + 1;
|
||||
if (numticks < 3) {
|
||||
numticks = 3;
|
||||
}
|
||||
for (i = 0; i < numticks; i++) {
|
||||
LIT(fencechar);
|
||||
}
|
||||
LIT(" ");
|
||||
OUT(info, false, LITERAL);
|
||||
CR();
|
||||
OUT(cmark_node_get_literal(node), false, LITERAL);
|
||||
CR();
|
||||
for (i = 0; i < numticks; i++) {
|
||||
LIT(fencechar);
|
||||
}
|
||||
|
||||
BLANKLINE();
|
||||
break;
|
||||
|
||||
case CMARK_NODE_HTML_BLOCK:
|
||||
BLANKLINE();
|
||||
OUT(cmark_node_get_literal(node), false, LITERAL);
|
||||
BLANKLINE();
|
||||
break;
|
||||
|
||||
case CMARK_NODE_CUSTOM_BLOCK:
|
||||
BLANKLINE();
|
||||
OUT(entering ? cmark_node_get_on_enter(node) : cmark_node_get_on_exit(node),
|
||||
false, LITERAL);
|
||||
BLANKLINE();
|
||||
break;
|
||||
|
||||
case CMARK_NODE_THEMATIC_BREAK:
|
||||
BLANKLINE();
|
||||
LIT("-----");
|
||||
BLANKLINE();
|
||||
break;
|
||||
|
||||
case CMARK_NODE_PARAGRAPH:
|
||||
if (!entering) {
|
||||
BLANKLINE();
|
||||
}
|
||||
break;
|
||||
|
||||
case CMARK_NODE_TEXT:
|
||||
OUT(cmark_node_get_literal(node), allow_wrap, NORMAL);
|
||||
break;
|
||||
|
||||
case CMARK_NODE_LINEBREAK:
|
||||
if (!(CMARK_OPT_HARDBREAKS & options)) {
|
||||
LIT(" ");
|
||||
}
|
||||
CR();
|
||||
break;
|
||||
|
||||
case CMARK_NODE_SOFTBREAK:
|
||||
if (CMARK_OPT_HARDBREAKS & options) {
|
||||
LIT(" ");
|
||||
CR();
|
||||
} else if (!renderer->no_linebreaks && renderer->width == 0 &&
|
||||
!(CMARK_OPT_HARDBREAKS & options) &&
|
||||
!(CMARK_OPT_NOBREAKS & options)) {
|
||||
CR();
|
||||
} else {
|
||||
OUT(" ", allow_wrap, LITERAL);
|
||||
}
|
||||
break;
|
||||
|
||||
case CMARK_NODE_CODE:
|
||||
code = cmark_node_get_literal(node);
|
||||
code_len = strlen(code);
|
||||
numticks = shortest_unused_backtick_sequence(code);
|
||||
has_nonspace = false;
|
||||
for (i=0; i < code_len; i++) {
|
||||
if (code[i] != ' ') {
|
||||
has_nonspace = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
extra_spaces = code_len == 0 ||
|
||||
code[0] == '`' || code[code_len - 1] == '`' ||
|
||||
(has_nonspace && code[0] == ' ' && code[code_len - 1] == ' ');
|
||||
for (i = 0; i < numticks; i++) {
|
||||
LIT("`");
|
||||
}
|
||||
if (extra_spaces) {
|
||||
LIT(" ");
|
||||
}
|
||||
OUT(cmark_node_get_literal(node), allow_wrap, LITERAL);
|
||||
if (extra_spaces) {
|
||||
LIT(" ");
|
||||
}
|
||||
for (i = 0; i < numticks; i++) {
|
||||
LIT("`");
|
||||
}
|
||||
break;
|
||||
|
||||
case CMARK_NODE_HTML_INLINE:
|
||||
OUT(cmark_node_get_literal(node), false, LITERAL);
|
||||
break;
|
||||
|
||||
case CMARK_NODE_CUSTOM_INLINE:
|
||||
OUT(entering ? cmark_node_get_on_enter(node) : cmark_node_get_on_exit(node),
|
||||
false, LITERAL);
|
||||
break;
|
||||
|
||||
case CMARK_NODE_STRONG:
|
||||
if (entering) {
|
||||
LIT("**");
|
||||
} else {
|
||||
LIT("**");
|
||||
}
|
||||
break;
|
||||
|
||||
case CMARK_NODE_EMPH:
|
||||
// If we have EMPH(EMPH(x)), we need to use *_x_*
|
||||
// because **x** is STRONG(x):
|
||||
if (node->parent && node->parent->type == CMARK_NODE_EMPH &&
|
||||
node->next == NULL && node->prev == NULL) {
|
||||
emph_delim = "_";
|
||||
} else {
|
||||
emph_delim = "*";
|
||||
}
|
||||
if (entering) {
|
||||
LIT(emph_delim);
|
||||
} else {
|
||||
LIT(emph_delim);
|
||||
}
|
||||
break;
|
||||
|
||||
case CMARK_NODE_LINK:
|
||||
if (is_autolink(node)) {
|
||||
if (entering) {
|
||||
LIT("<");
|
||||
if (strncmp(cmark_node_get_url(node), "mailto:", 7) == 0) {
|
||||
LIT((const char *)cmark_node_get_url(node) + 7);
|
||||
} else {
|
||||
LIT((const char *)cmark_node_get_url(node));
|
||||
}
|
||||
LIT(">");
|
||||
// return signal to skip contents of node...
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
if (entering) {
|
||||
LIT("[");
|
||||
} else {
|
||||
LIT("](");
|
||||
OUT(cmark_node_get_url(node), false, URL);
|
||||
title = cmark_node_get_title(node);
|
||||
if (strlen(title) > 0) {
|
||||
LIT(" \"");
|
||||
OUT(title, false, TITLE);
|
||||
LIT("\"");
|
||||
}
|
||||
LIT(")");
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case CMARK_NODE_IMAGE:
|
||||
if (entering) {
|
||||
LIT(";
|
||||
OUT(cmark_node_get_url(node), false, URL);
|
||||
title = cmark_node_get_title(node);
|
||||
if (strlen(title) > 0) {
|
||||
OUT(" \"", allow_wrap, LITERAL);
|
||||
OUT(title, false, TITLE);
|
||||
LIT("\"");
|
||||
}
|
||||
LIT(")");
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
char *cmark_render_commonmark(cmark_node *root, int options, int width) {
|
||||
if (options & CMARK_OPT_HARDBREAKS) {
|
||||
// disable breaking on width, since it has
|
||||
// a different meaning with OPT_HARDBREAKS
|
||||
width = 0;
|
||||
}
|
||||
return cmark_render(root, options, width, outc, S_render_node);
|
||||
}
|
76
deps/cmark/src/config.h.in
vendored
Normal file
76
deps/cmark/src/config.h.in
vendored
Normal file
@ -0,0 +1,76 @@
|
||||
#ifndef CMARK_CONFIG_H
|
||||
#define CMARK_CONFIG_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#cmakedefine HAVE_STDBOOL_H
|
||||
|
||||
#ifdef HAVE_STDBOOL_H
|
||||
#include <stdbool.h>
|
||||
#elif !defined(__cplusplus)
|
||||
typedef char bool;
|
||||
#endif
|
||||
|
||||
#cmakedefine HAVE___BUILTIN_EXPECT
|
||||
|
||||
#cmakedefine HAVE___ATTRIBUTE__
|
||||
|
||||
#ifdef HAVE___ATTRIBUTE__
|
||||
#define CMARK_ATTRIBUTE(list) __attribute__ (list)
|
||||
#else
|
||||
#define CMARK_ATTRIBUTE(list)
|
||||
#endif
|
||||
|
||||
#ifndef CMARK_INLINE
|
||||
#if defined(_MSC_VER) && !defined(__cplusplus)
|
||||
#define CMARK_INLINE __inline
|
||||
#else
|
||||
#define CMARK_INLINE inline
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* snprintf and vsnprintf fallbacks for MSVC before 2015,
|
||||
due to Valentin Milea http://stackoverflow.com/questions/2915672/
|
||||
*/
|
||||
|
||||
#if defined(_MSC_VER) && _MSC_VER < 1900
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#define snprintf c99_snprintf
|
||||
#define vsnprintf c99_vsnprintf
|
||||
|
||||
CMARK_INLINE int c99_vsnprintf(char *outBuf, size_t size, const char *format, va_list ap)
|
||||
{
|
||||
int count = -1;
|
||||
|
||||
if (size != 0)
|
||||
count = _vsnprintf_s(outBuf, size, _TRUNCATE, format, ap);
|
||||
if (count == -1)
|
||||
count = _vscprintf(format, ap);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
CMARK_INLINE int c99_snprintf(char *outBuf, size_t size, const char *format, ...)
|
||||
{
|
||||
int count;
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, format);
|
||||
count = c99_vsnprintf(outBuf, size, format, ap);
|
||||
va_end(ap);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
2138
deps/cmark/src/entities.inc
vendored
Normal file
2138
deps/cmark/src/entities.inc
vendored
Normal file
File diff suppressed because it is too large
Load Diff
51
deps/cmark/src/houdini.h
vendored
Normal file
51
deps/cmark/src/houdini.h
vendored
Normal file
@ -0,0 +1,51 @@
|
||||
#ifndef CMARK_HOUDINI_H
|
||||
#define CMARK_HOUDINI_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include "config.h"
|
||||
#include "buffer.h"
|
||||
|
||||
#ifdef HAVE___BUILTIN_EXPECT
|
||||
#define likely(x) __builtin_expect((x), 1)
|
||||
#define unlikely(x) __builtin_expect((x), 0)
|
||||
#else
|
||||
#define likely(x) (x)
|
||||
#define unlikely(x) (x)
|
||||
#endif
|
||||
|
||||
#ifdef HOUDINI_USE_LOCALE
|
||||
#define _isxdigit(c) isxdigit(c)
|
||||
#define _isdigit(c) isdigit(c)
|
||||
#else
|
||||
/*
|
||||
* Helper _isdigit methods -- do not trust the current locale
|
||||
* */
|
||||
#define _isxdigit(c) (strchr("0123456789ABCDEFabcdef", (c)) != NULL)
|
||||
#define _isdigit(c) ((c) >= '0' && (c) <= '9')
|
||||
#endif
|
||||
|
||||
#define HOUDINI_ESCAPED_SIZE(x) (((x)*12) / 10)
|
||||
#define HOUDINI_UNESCAPED_SIZE(x) (x)
|
||||
|
||||
extern bufsize_t houdini_unescape_ent(cmark_strbuf *ob, const uint8_t *src,
|
||||
bufsize_t size);
|
||||
extern int houdini_escape_html(cmark_strbuf *ob, const uint8_t *src,
|
||||
bufsize_t size);
|
||||
extern int houdini_escape_html0(cmark_strbuf *ob, const uint8_t *src,
|
||||
bufsize_t size, int secure);
|
||||
extern int houdini_unescape_html(cmark_strbuf *ob, const uint8_t *src,
|
||||
bufsize_t size);
|
||||
extern void houdini_unescape_html_f(cmark_strbuf *ob, const uint8_t *src,
|
||||
bufsize_t size);
|
||||
extern int houdini_escape_href(cmark_strbuf *ob, const uint8_t *src,
|
||||
bufsize_t size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
100
deps/cmark/src/houdini_href_e.c
vendored
Normal file
100
deps/cmark/src/houdini_href_e.c
vendored
Normal file
@ -0,0 +1,100 @@
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "houdini.h"
|
||||
|
||||
/*
|
||||
* The following characters will not be escaped:
|
||||
*
|
||||
* -_.+!*'(),%#@?=;:/,+&$~ alphanum
|
||||
*
|
||||
* Note that this character set is the addition of:
|
||||
*
|
||||
* - The characters which are safe to be in an URL
|
||||
* - The characters which are *not* safe to be in
|
||||
* an URL because they are RESERVED characters.
|
||||
*
|
||||
* We assume (lazily) that any RESERVED char that
|
||||
* appears inside an URL is actually meant to
|
||||
* have its native function (i.e. as an URL
|
||||
* component/separator) and hence needs no escaping.
|
||||
*
|
||||
* There are two exceptions: the characters & (amp)
|
||||
* and ' (single quote) do not appear in the table.
|
||||
* They are meant to appear in the URL as components,
|
||||
* yet they require special HTML-entity escaping
|
||||
* to generate valid HTML markup.
|
||||
*
|
||||
* All other characters will be escaped to %XX.
|
||||
*
|
||||
*/
|
||||
static const char HREF_SAFE[] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
|
||||
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
};
|
||||
|
||||
int houdini_escape_href(cmark_strbuf *ob, const uint8_t *src, bufsize_t size) {
|
||||
static const uint8_t hex_chars[] = "0123456789ABCDEF";
|
||||
bufsize_t i = 0, org;
|
||||
uint8_t hex_str[3];
|
||||
|
||||
hex_str[0] = '%';
|
||||
|
||||
while (i < size) {
|
||||
org = i;
|
||||
while (i < size && HREF_SAFE[src[i]] != 0)
|
||||
i++;
|
||||
|
||||
if (likely(i > org))
|
||||
cmark_strbuf_put(ob, src + org, i - org);
|
||||
|
||||
/* escaping */
|
||||
if (i >= size)
|
||||
break;
|
||||
|
||||
switch (src[i]) {
|
||||
/* amp appears all the time in URLs, but needs
|
||||
* HTML-entity escaping to be inside an href */
|
||||
case '&':
|
||||
cmark_strbuf_puts(ob, "&");
|
||||
break;
|
||||
|
||||
/* the single quote is a valid URL character
|
||||
* according to the standard; it needs HTML
|
||||
* entity escaping too */
|
||||
case '\'':
|
||||
cmark_strbuf_puts(ob, "'");
|
||||
break;
|
||||
|
||||
/* the space can be escaped to %20 or a plus
|
||||
* sign. we're going with the generic escape
|
||||
* for now. the plus thing is more commonly seen
|
||||
* when building GET strings */
|
||||
#if 0
|
||||
case ' ':
|
||||
cmark_strbuf_putc(ob, '+');
|
||||
break;
|
||||
#endif
|
||||
|
||||
/* every other character goes with a %XX escaping */
|
||||
default:
|
||||
hex_str[1] = hex_chars[(src[i] >> 4) & 0xF];
|
||||
hex_str[2] = hex_chars[src[i] & 0xF];
|
||||
cmark_strbuf_put(ob, hex_str, 3);
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
66
deps/cmark/src/houdini_html_e.c
vendored
Normal file
66
deps/cmark/src/houdini_html_e.c
vendored
Normal file
@ -0,0 +1,66 @@
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "houdini.h"
|
||||
|
||||
/**
|
||||
* According to the OWASP rules:
|
||||
*
|
||||
* & --> &
|
||||
* < --> <
|
||||
* > --> >
|
||||
* " --> "
|
||||
* ' --> ' ' is not recommended
|
||||
* / --> / forward slash is included as it helps end an HTML entity
|
||||
*
|
||||
*/
|
||||
static const char HTML_ESCAPE_TABLE[] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 2, 3, 0, 0, 0, 0, 0, 0, 0, 4,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
};
|
||||
|
||||
static const char *HTML_ESCAPES[] = {"", """, "&", "'",
|
||||
"/", "<", ">"};
|
||||
|
||||
int houdini_escape_html0(cmark_strbuf *ob, const uint8_t *src, bufsize_t size,
|
||||
int secure) {
|
||||
bufsize_t i = 0, org, esc = 0;
|
||||
|
||||
while (i < size) {
|
||||
org = i;
|
||||
while (i < size && (esc = HTML_ESCAPE_TABLE[src[i]]) == 0)
|
||||
i++;
|
||||
|
||||
if (i > org)
|
||||
cmark_strbuf_put(ob, src + org, i - org);
|
||||
|
||||
/* escaping */
|
||||
if (unlikely(i >= size))
|
||||
break;
|
||||
|
||||
/* The forward slash is only escaped in secure mode */
|
||||
if ((src[i] == '/' || src[i] == '\'') && !secure) {
|
||||
cmark_strbuf_putc(ob, src[i]);
|
||||
} else {
|
||||
cmark_strbuf_puts(ob, HTML_ESCAPES[esc]);
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int houdini_escape_html(cmark_strbuf *ob, const uint8_t *src, bufsize_t size) {
|
||||
return houdini_escape_html0(ob, src, size, 1);
|
||||
}
|
153
deps/cmark/src/houdini_html_u.c
vendored
Normal file
153
deps/cmark/src/houdini_html_u.c
vendored
Normal file
@ -0,0 +1,153 @@
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "buffer.h"
|
||||
#include "houdini.h"
|
||||
#include "utf8.h"
|
||||
#include "entities.inc"
|
||||
|
||||
/* Binary tree lookup code for entities added by JGM */
|
||||
|
||||
static const unsigned char *S_lookup(int i, int low, int hi,
|
||||
const unsigned char *s, int len) {
|
||||
int j;
|
||||
int cmp =
|
||||
strncmp((const char *)s, (const char *)cmark_entities[i].entity, len);
|
||||
if (cmp == 0 && cmark_entities[i].entity[len] == 0) {
|
||||
return (const unsigned char *)cmark_entities[i].bytes;
|
||||
} else if (cmp <= 0 && i > low) {
|
||||
j = i - ((i - low) / 2);
|
||||
if (j == i)
|
||||
j -= 1;
|
||||
return S_lookup(j, low, i - 1, s, len);
|
||||
} else if (cmp > 0 && i < hi) {
|
||||
j = i + ((hi - i) / 2);
|
||||
if (j == i)
|
||||
j += 1;
|
||||
return S_lookup(j, i + 1, hi, s, len);
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static const unsigned char *S_lookup_entity(const unsigned char *s, int len) {
|
||||
return S_lookup(CMARK_NUM_ENTITIES / 2, 0, CMARK_NUM_ENTITIES - 1, s, len);
|
||||
}
|
||||
|
||||
bufsize_t houdini_unescape_ent(cmark_strbuf *ob, const uint8_t *src,
|
||||
bufsize_t size) {
|
||||
bufsize_t i = 0;
|
||||
|
||||
if (size >= 3 && src[0] == '#') {
|
||||
int codepoint = 0;
|
||||
int num_digits = 0;
|
||||
int max_digits = 7;
|
||||
|
||||
if (_isdigit(src[1])) {
|
||||
for (i = 1; i < size && _isdigit(src[i]); ++i) {
|
||||
codepoint = (codepoint * 10) + (src[i] - '0');
|
||||
|
||||
if (codepoint >= 0x110000) {
|
||||
// Keep counting digits but
|
||||
// avoid integer overflow.
|
||||
codepoint = 0x110000;
|
||||
}
|
||||
}
|
||||
|
||||
num_digits = i - 1;
|
||||
max_digits = 7;
|
||||
}
|
||||
|
||||
else if (src[1] == 'x' || src[1] == 'X') {
|
||||
for (i = 2; i < size && _isxdigit(src[i]); ++i) {
|
||||
codepoint = (codepoint * 16) + ((src[i] | 32) % 39 - 9);
|
||||
|
||||
if (codepoint >= 0x110000) {
|
||||
// Keep counting digits but
|
||||
// avoid integer overflow.
|
||||
codepoint = 0x110000;
|
||||
}
|
||||
}
|
||||
|
||||
num_digits = i - 2;
|
||||
max_digits = 6;
|
||||
}
|
||||
|
||||
if (num_digits >= 1 && num_digits <= max_digits &&
|
||||
i < size && src[i] == ';') {
|
||||
if (codepoint == 0 || (codepoint >= 0xD800 && codepoint < 0xE000) ||
|
||||
codepoint >= 0x110000) {
|
||||
codepoint = 0xFFFD;
|
||||
}
|
||||
cmark_utf8proc_encode_char(codepoint, ob);
|
||||
return i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
else {
|
||||
if (size > CMARK_ENTITY_MAX_LENGTH)
|
||||
size = CMARK_ENTITY_MAX_LENGTH;
|
||||
|
||||
for (i = CMARK_ENTITY_MIN_LENGTH; i < size; ++i) {
|
||||
if (src[i] == ' ')
|
||||
break;
|
||||
|
||||
if (src[i] == ';') {
|
||||
const unsigned char *entity = S_lookup_entity(src, i);
|
||||
|
||||
if (entity != NULL) {
|
||||
cmark_strbuf_puts(ob, (const char *)entity);
|
||||
return i + 1;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int houdini_unescape_html(cmark_strbuf *ob, const uint8_t *src,
|
||||
bufsize_t size) {
|
||||
bufsize_t i = 0, org, ent;
|
||||
|
||||
while (i < size) {
|
||||
org = i;
|
||||
while (i < size && src[i] != '&')
|
||||
i++;
|
||||
|
||||
if (likely(i > org)) {
|
||||
if (unlikely(org == 0)) {
|
||||
if (i >= size)
|
||||
return 0;
|
||||
|
||||
cmark_strbuf_grow(ob, HOUDINI_UNESCAPED_SIZE(size));
|
||||
}
|
||||
|
||||
cmark_strbuf_put(ob, src + org, i - org);
|
||||
}
|
||||
|
||||
/* escaping */
|
||||
if (i >= size)
|
||||
break;
|
||||
|
||||
i++;
|
||||
|
||||
ent = houdini_unescape_ent(ob, src + i, size - i);
|
||||
i += ent;
|
||||
|
||||
/* not really an entity */
|
||||
if (ent == 0)
|
||||
cmark_strbuf_putc(ob, '&');
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void houdini_unescape_html_f(cmark_strbuf *ob, const uint8_t *src,
|
||||
bufsize_t size) {
|
||||
if (!houdini_unescape_html(ob, src, size))
|
||||
cmark_strbuf_put(ob, src, size);
|
||||
}
|
341
deps/cmark/src/html.c
vendored
Normal file
341
deps/cmark/src/html.c
vendored
Normal file
@ -0,0 +1,341 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include "cmark_ctype.h"
|
||||
#include "config.h"
|
||||
#include "cmark.h"
|
||||
#include "node.h"
|
||||
#include "buffer.h"
|
||||
#include "houdini.h"
|
||||
#include "scanners.h"
|
||||
|
||||
#define BUFFER_SIZE 100
|
||||
|
||||
// Functions to convert cmark_nodes to HTML strings.
|
||||
|
||||
static void escape_html(cmark_strbuf *dest, const unsigned char *source,
|
||||
bufsize_t length) {
|
||||
houdini_escape_html0(dest, source, length, 0);
|
||||
}
|
||||
|
||||
static CMARK_INLINE void cr(cmark_strbuf *html) {
|
||||
if (html->size && html->ptr[html->size - 1] != '\n')
|
||||
cmark_strbuf_putc(html, '\n');
|
||||
}
|
||||
|
||||
struct render_state {
|
||||
cmark_strbuf *html;
|
||||
cmark_node *plain;
|
||||
};
|
||||
|
||||
static void S_render_sourcepos(cmark_node *node, cmark_strbuf *html,
|
||||
int options) {
|
||||
char buffer[BUFFER_SIZE];
|
||||
if (CMARK_OPT_SOURCEPOS & options) {
|
||||
snprintf(buffer, BUFFER_SIZE, " data-sourcepos=\"%d:%d-%d:%d\"",
|
||||
cmark_node_get_start_line(node), cmark_node_get_start_column(node),
|
||||
cmark_node_get_end_line(node), cmark_node_get_end_column(node));
|
||||
cmark_strbuf_puts(html, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
static int S_render_node(cmark_node *node, cmark_event_type ev_type,
|
||||
struct render_state *state, int options) {
|
||||
cmark_node *parent;
|
||||
cmark_node *grandparent;
|
||||
cmark_strbuf *html = state->html;
|
||||
char start_heading[] = "<h0";
|
||||
char end_heading[] = "</h0";
|
||||
bool tight;
|
||||
char buffer[BUFFER_SIZE];
|
||||
|
||||
bool entering = (ev_type == CMARK_EVENT_ENTER);
|
||||
|
||||
if (state->plain == node) { // back at original node
|
||||
state->plain = NULL;
|
||||
}
|
||||
|
||||
if (state->plain != NULL) {
|
||||
switch (node->type) {
|
||||
case CMARK_NODE_TEXT:
|
||||
case CMARK_NODE_CODE:
|
||||
case CMARK_NODE_HTML_INLINE:
|
||||
escape_html(html, node->data, node->len);
|
||||
break;
|
||||
|
||||
case CMARK_NODE_LINEBREAK:
|
||||
case CMARK_NODE_SOFTBREAK:
|
||||
cmark_strbuf_putc(html, ' ');
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
switch (node->type) {
|
||||
case CMARK_NODE_DOCUMENT:
|
||||
break;
|
||||
|
||||
case CMARK_NODE_BLOCK_QUOTE:
|
||||
if (entering) {
|
||||
cr(html);
|
||||
cmark_strbuf_puts(html, "<blockquote");
|
||||
S_render_sourcepos(node, html, options);
|
||||
cmark_strbuf_puts(html, ">\n");
|
||||
} else {
|
||||
cr(html);
|
||||
cmark_strbuf_puts(html, "</blockquote>\n");
|
||||
}
|
||||
break;
|
||||
|
||||
case CMARK_NODE_LIST: {
|
||||
cmark_list_type list_type = (cmark_list_type)node->as.list.list_type;
|
||||
int start = node->as.list.start;
|
||||
|
||||
if (entering) {
|
||||
cr(html);
|
||||
if (list_type == CMARK_BULLET_LIST) {
|
||||
cmark_strbuf_puts(html, "<ul");
|
||||
S_render_sourcepos(node, html, options);
|
||||
cmark_strbuf_puts(html, ">\n");
|
||||
} else if (start == 1) {
|
||||
cmark_strbuf_puts(html, "<ol");
|
||||
S_render_sourcepos(node, html, options);
|
||||
cmark_strbuf_puts(html, ">\n");
|
||||
} else {
|
||||
snprintf(buffer, BUFFER_SIZE, "<ol start=\"%d\"", start);
|
||||
cmark_strbuf_puts(html, buffer);
|
||||
S_render_sourcepos(node, html, options);
|
||||
cmark_strbuf_puts(html, ">\n");
|
||||
}
|
||||
} else {
|
||||
cmark_strbuf_puts(html,
|
||||
list_type == CMARK_BULLET_LIST ? "</ul>\n" : "</ol>\n");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case CMARK_NODE_ITEM:
|
||||
if (entering) {
|
||||
cr(html);
|
||||
cmark_strbuf_puts(html, "<li");
|
||||
S_render_sourcepos(node, html, options);
|
||||
cmark_strbuf_putc(html, '>');
|
||||
} else {
|
||||
cmark_strbuf_puts(html, "</li>\n");
|
||||
}
|
||||
break;
|
||||
|
||||
case CMARK_NODE_HEADING:
|
||||
if (entering) {
|
||||
cr(html);
|
||||
start_heading[2] = (char)('0' + node->as.heading.level);
|
||||
cmark_strbuf_puts(html, start_heading);
|
||||
S_render_sourcepos(node, html, options);
|
||||
cmark_strbuf_putc(html, '>');
|
||||
} else {
|
||||
end_heading[3] = (char)('0' + node->as.heading.level);
|
||||
cmark_strbuf_puts(html, end_heading);
|
||||
cmark_strbuf_puts(html, ">\n");
|
||||
}
|
||||
break;
|
||||
|
||||
case CMARK_NODE_CODE_BLOCK:
|
||||
cr(html);
|
||||
|
||||
if (node->as.code.info == NULL || node->as.code.info[0] == 0) {
|
||||
cmark_strbuf_puts(html, "<pre");
|
||||
S_render_sourcepos(node, html, options);
|
||||
cmark_strbuf_puts(html, "><code>");
|
||||
} else {
|
||||
bufsize_t first_tag = 0;
|
||||
while (node->as.code.info[first_tag] &&
|
||||
!cmark_isspace(node->as.code.info[first_tag])) {
|
||||
first_tag += 1;
|
||||
}
|
||||
|
||||
cmark_strbuf_puts(html, "<pre");
|
||||
S_render_sourcepos(node, html, options);
|
||||
cmark_strbuf_puts(html, "><code class=\"language-");
|
||||
escape_html(html, node->as.code.info, first_tag);
|
||||
cmark_strbuf_puts(html, "\">");
|
||||
}
|
||||
|
||||
escape_html(html, node->data, node->len);
|
||||
cmark_strbuf_puts(html, "</code></pre>\n");
|
||||
break;
|
||||
|
||||
case CMARK_NODE_HTML_BLOCK:
|
||||
cr(html);
|
||||
if (!(options & CMARK_OPT_UNSAFE)) {
|
||||
cmark_strbuf_puts(html, "<!-- raw HTML omitted -->");
|
||||
} else {
|
||||
cmark_strbuf_put(html, node->data, node->len);
|
||||
}
|
||||
cr(html);
|
||||
break;
|
||||
|
||||
case CMARK_NODE_CUSTOM_BLOCK: {
|
||||
unsigned char *block = entering ? node->as.custom.on_enter :
|
||||
node->as.custom.on_exit;
|
||||
cr(html);
|
||||
if (block) {
|
||||
cmark_strbuf_puts(html, (char *)block);
|
||||
}
|
||||
cr(html);
|
||||
break;
|
||||
}
|
||||
|
||||
case CMARK_NODE_THEMATIC_BREAK:
|
||||
cr(html);
|
||||
cmark_strbuf_puts(html, "<hr");
|
||||
S_render_sourcepos(node, html, options);
|
||||
cmark_strbuf_puts(html, " />\n");
|
||||
break;
|
||||
|
||||
case CMARK_NODE_PARAGRAPH:
|
||||
parent = cmark_node_parent(node);
|
||||
grandparent = cmark_node_parent(parent);
|
||||
if (grandparent != NULL && grandparent->type == CMARK_NODE_LIST) {
|
||||
tight = grandparent->as.list.tight;
|
||||
} else {
|
||||
tight = false;
|
||||
}
|
||||
if (!tight) {
|
||||
if (entering) {
|
||||
cr(html);
|
||||
cmark_strbuf_puts(html, "<p");
|
||||
S_render_sourcepos(node, html, options);
|
||||
cmark_strbuf_putc(html, '>');
|
||||
} else {
|
||||
cmark_strbuf_puts(html, "</p>\n");
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case CMARK_NODE_TEXT:
|
||||
escape_html(html, node->data, node->len);
|
||||
break;
|
||||
|
||||
case CMARK_NODE_LINEBREAK:
|
||||
cmark_strbuf_puts(html, "<br />\n");
|
||||
break;
|
||||
|
||||
case CMARK_NODE_SOFTBREAK:
|
||||
if (options & CMARK_OPT_HARDBREAKS) {
|
||||
cmark_strbuf_puts(html, "<br />\n");
|
||||
} else if (options & CMARK_OPT_NOBREAKS) {
|
||||
cmark_strbuf_putc(html, ' ');
|
||||
} else {
|
||||
cmark_strbuf_putc(html, '\n');
|
||||
}
|
||||
break;
|
||||
|
||||
case CMARK_NODE_CODE:
|
||||
cmark_strbuf_puts(html, "<code>");
|
||||
escape_html(html, node->data, node->len);
|
||||
cmark_strbuf_puts(html, "</code>");
|
||||
break;
|
||||
|
||||
case CMARK_NODE_HTML_INLINE:
|
||||
if (!(options & CMARK_OPT_UNSAFE)) {
|
||||
cmark_strbuf_puts(html, "<!-- raw HTML omitted -->");
|
||||
} else {
|
||||
cmark_strbuf_put(html, node->data, node->len);
|
||||
}
|
||||
break;
|
||||
|
||||
case CMARK_NODE_CUSTOM_INLINE: {
|
||||
unsigned char *block = entering ? node->as.custom.on_enter :
|
||||
node->as.custom.on_exit;
|
||||
if (block) {
|
||||
cmark_strbuf_puts(html, (char *)block);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case CMARK_NODE_STRONG:
|
||||
if (entering) {
|
||||
cmark_strbuf_puts(html, "<strong>");
|
||||
} else {
|
||||
cmark_strbuf_puts(html, "</strong>");
|
||||
}
|
||||
break;
|
||||
|
||||
case CMARK_NODE_EMPH:
|
||||
if (entering) {
|
||||
cmark_strbuf_puts(html, "<em>");
|
||||
} else {
|
||||
cmark_strbuf_puts(html, "</em>");
|
||||
}
|
||||
break;
|
||||
|
||||
case CMARK_NODE_LINK:
|
||||
if (entering) {
|
||||
cmark_strbuf_puts(html, "<a href=\"");
|
||||
if (node->as.link.url && ((options & CMARK_OPT_UNSAFE) ||
|
||||
!(_scan_dangerous_url(node->as.link.url)))) {
|
||||
houdini_escape_href(html, node->as.link.url,
|
||||
strlen((char *)node->as.link.url));
|
||||
}
|
||||
if (node->as.link.title) {
|
||||
cmark_strbuf_puts(html, "\" title=\"");
|
||||
escape_html(html, node->as.link.title,
|
||||
strlen((char *)node->as.link.title));
|
||||
}
|
||||
cmark_strbuf_puts(html, "\">");
|
||||
} else {
|
||||
cmark_strbuf_puts(html, "</a>");
|
||||
}
|
||||
break;
|
||||
|
||||
case CMARK_NODE_IMAGE:
|
||||
if (entering) {
|
||||
cmark_strbuf_puts(html, "<img src=\"");
|
||||
if (node->as.link.url && ((options & CMARK_OPT_UNSAFE) ||
|
||||
!(_scan_dangerous_url(node->as.link.url)))) {
|
||||
houdini_escape_href(html, node->as.link.url,
|
||||
strlen((char *)node->as.link.url));
|
||||
}
|
||||
cmark_strbuf_puts(html, "\" alt=\"");
|
||||
state->plain = node;
|
||||
} else {
|
||||
if (node->as.link.title) {
|
||||
cmark_strbuf_puts(html, "\" title=\"");
|
||||
escape_html(html, node->as.link.title,
|
||||
strlen((char *)node->as.link.title));
|
||||
}
|
||||
|
||||
cmark_strbuf_puts(html, "\" />");
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
|
||||
// cmark_strbuf_putc(html, 'x');
|
||||
return 1;
|
||||
}
|
||||
|
||||
char *cmark_render_html(cmark_node *root, int options) {
|
||||
char *result;
|
||||
cmark_strbuf html = CMARK_BUF_INIT(root->mem);
|
||||
cmark_event_type ev_type;
|
||||
cmark_node *cur;
|
||||
struct render_state state = {&html, NULL};
|
||||
cmark_iter *iter = cmark_iter_new(root);
|
||||
|
||||
while ((ev_type = cmark_iter_next(iter)) != CMARK_EVENT_DONE) {
|
||||
cur = cmark_iter_get_node(iter);
|
||||
S_render_node(cur, ev_type, &state, options);
|
||||
}
|
||||
result = (char *)cmark_strbuf_detach(&html);
|
||||
|
||||
cmark_iter_free(iter);
|
||||
return result;
|
||||
}
|
1483
deps/cmark/src/inlines.c
vendored
Normal file
1483
deps/cmark/src/inlines.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
24
deps/cmark/src/inlines.h
vendored
Normal file
24
deps/cmark/src/inlines.h
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
#ifndef CMARK_INLINES_H
|
||||
#define CMARK_INLINES_H
|
||||
|
||||
#include "chunk.h"
|
||||
#include "references.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
unsigned char *cmark_clean_url(cmark_mem *mem, cmark_chunk *url);
|
||||
unsigned char *cmark_clean_title(cmark_mem *mem, cmark_chunk *title);
|
||||
|
||||
void cmark_parse_inlines(cmark_mem *mem, cmark_node *parent,
|
||||
cmark_reference_map *refmap, int options);
|
||||
|
||||
bufsize_t cmark_parse_reference_inline(cmark_mem *mem, cmark_chunk *input,
|
||||
cmark_reference_map *refmap);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
122
deps/cmark/src/iterator.c
vendored
Normal file
122
deps/cmark/src/iterator.c
vendored
Normal file
@ -0,0 +1,122 @@
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "node.h"
|
||||
#include "cmark.h"
|
||||
#include "iterator.h"
|
||||
|
||||
static const int S_leaf_mask =
|
||||
(1 << CMARK_NODE_HTML_BLOCK) | (1 << CMARK_NODE_THEMATIC_BREAK) |
|
||||
(1 << CMARK_NODE_CODE_BLOCK) | (1 << CMARK_NODE_TEXT) |
|
||||
(1 << CMARK_NODE_SOFTBREAK) | (1 << CMARK_NODE_LINEBREAK) |
|
||||
(1 << CMARK_NODE_CODE) | (1 << CMARK_NODE_HTML_INLINE);
|
||||
|
||||
cmark_iter *cmark_iter_new(cmark_node *root) {
|
||||
if (root == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
cmark_mem *mem = root->mem;
|
||||
cmark_iter *iter = (cmark_iter *)mem->calloc(1, sizeof(cmark_iter));
|
||||
iter->mem = mem;
|
||||
iter->root = root;
|
||||
iter->cur.ev_type = CMARK_EVENT_NONE;
|
||||
iter->cur.node = NULL;
|
||||
iter->next.ev_type = CMARK_EVENT_ENTER;
|
||||
iter->next.node = root;
|
||||
return iter;
|
||||
}
|
||||
|
||||
void cmark_iter_free(cmark_iter *iter) { iter->mem->free(iter); }
|
||||
|
||||
static bool S_is_leaf(cmark_node *node) {
|
||||
return ((1 << node->type) & S_leaf_mask) != 0;
|
||||
}
|
||||
|
||||
cmark_event_type cmark_iter_next(cmark_iter *iter) {
|
||||
cmark_event_type ev_type = iter->next.ev_type;
|
||||
cmark_node *node = iter->next.node;
|
||||
|
||||
iter->cur.ev_type = ev_type;
|
||||
iter->cur.node = node;
|
||||
|
||||
if (ev_type == CMARK_EVENT_DONE) {
|
||||
return ev_type;
|
||||
}
|
||||
|
||||
/* roll forward to next item, setting both fields */
|
||||
if (ev_type == CMARK_EVENT_ENTER && !S_is_leaf(node)) {
|
||||
if (node->first_child == NULL) {
|
||||
/* stay on this node but exit */
|
||||
iter->next.ev_type = CMARK_EVENT_EXIT;
|
||||
} else {
|
||||
iter->next.ev_type = CMARK_EVENT_ENTER;
|
||||
iter->next.node = node->first_child;
|
||||
}
|
||||
} else if (node == iter->root) {
|
||||
/* don't move past root */
|
||||
iter->next.ev_type = CMARK_EVENT_DONE;
|
||||
iter->next.node = NULL;
|
||||
} else if (node->next) {
|
||||
iter->next.ev_type = CMARK_EVENT_ENTER;
|
||||
iter->next.node = node->next;
|
||||
} else if (node->parent) {
|
||||
iter->next.ev_type = CMARK_EVENT_EXIT;
|
||||
iter->next.node = node->parent;
|
||||
} else {
|
||||
assert(false);
|
||||
iter->next.ev_type = CMARK_EVENT_DONE;
|
||||
iter->next.node = NULL;
|
||||
}
|
||||
|
||||
return ev_type;
|
||||
}
|
||||
|
||||
void cmark_iter_reset(cmark_iter *iter, cmark_node *current,
|
||||
cmark_event_type event_type) {
|
||||
iter->next.ev_type = event_type;
|
||||
iter->next.node = current;
|
||||
cmark_iter_next(iter);
|
||||
}
|
||||
|
||||
cmark_node *cmark_iter_get_node(cmark_iter *iter) { return iter->cur.node; }
|
||||
|
||||
cmark_event_type cmark_iter_get_event_type(cmark_iter *iter) {
|
||||
return iter->cur.ev_type;
|
||||
}
|
||||
|
||||
cmark_node *cmark_iter_get_root(cmark_iter *iter) { return iter->root; }
|
||||
|
||||
void cmark_consolidate_text_nodes(cmark_node *root) {
|
||||
if (root == NULL) {
|
||||
return;
|
||||
}
|
||||
cmark_iter *iter = cmark_iter_new(root);
|
||||
cmark_strbuf buf = CMARK_BUF_INIT(iter->mem);
|
||||
cmark_event_type ev_type;
|
||||
cmark_node *cur, *tmp, *next;
|
||||
|
||||
while ((ev_type = cmark_iter_next(iter)) != CMARK_EVENT_DONE) {
|
||||
cur = cmark_iter_get_node(iter);
|
||||
if (ev_type == CMARK_EVENT_ENTER && cur->type == CMARK_NODE_TEXT &&
|
||||
cur->next && cur->next->type == CMARK_NODE_TEXT) {
|
||||
cmark_strbuf_clear(&buf);
|
||||
cmark_strbuf_put(&buf, cur->data, cur->len);
|
||||
tmp = cur->next;
|
||||
while (tmp && tmp->type == CMARK_NODE_TEXT) {
|
||||
cmark_iter_next(iter); // advance pointer
|
||||
cmark_strbuf_put(&buf, tmp->data, tmp->len);
|
||||
cur->end_column = tmp->end_column;
|
||||
next = tmp->next;
|
||||
cmark_node_free(tmp);
|
||||
tmp = next;
|
||||
}
|
||||
iter->mem->free(cur->data);
|
||||
cur->len = buf.size;
|
||||
cur->data = cmark_strbuf_detach(&buf);
|
||||
}
|
||||
}
|
||||
|
||||
cmark_strbuf_free(&buf);
|
||||
cmark_iter_free(iter);
|
||||
}
|
26
deps/cmark/src/iterator.h
vendored
Normal file
26
deps/cmark/src/iterator.h
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
#ifndef CMARK_ITERATOR_H
|
||||
#define CMARK_ITERATOR_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "cmark.h"
|
||||
|
||||
typedef struct {
|
||||
cmark_event_type ev_type;
|
||||
cmark_node *node;
|
||||
} cmark_iter_state;
|
||||
|
||||
struct cmark_iter {
|
||||
cmark_mem *mem;
|
||||
cmark_node *root;
|
||||
cmark_iter_state cur;
|
||||
cmark_iter_state next;
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
453
deps/cmark/src/latex.c
vendored
Normal file
453
deps/cmark/src/latex.c
vendored
Normal file
@ -0,0 +1,453 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "cmark.h"
|
||||
#include "node.h"
|
||||
#include "buffer.h"
|
||||
#include "utf8.h"
|
||||
#include "scanners.h"
|
||||
#include "render.h"
|
||||
|
||||
#define OUT(s, wrap, escaping) renderer->out(renderer, s, wrap, escaping)
|
||||
#define LIT(s) renderer->out(renderer, s, false, LITERAL)
|
||||
#define CR() renderer->cr(renderer)
|
||||
#define BLANKLINE() renderer->blankline(renderer)
|
||||
#define LIST_NUMBER_STRING_SIZE 20
|
||||
|
||||
static CMARK_INLINE void outc(cmark_renderer *renderer, cmark_escaping escape,
|
||||
int32_t c, unsigned char nextc) {
|
||||
if (escape == LITERAL) {
|
||||
cmark_render_code_point(renderer, c);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (c) {
|
||||
case 123: // '{'
|
||||
case 125: // '}'
|
||||
case 35: // '#'
|
||||
case 37: // '%'
|
||||
case 38: // '&'
|
||||
cmark_render_ascii(renderer, "\\");
|
||||
cmark_render_code_point(renderer, c);
|
||||
break;
|
||||
case 36: // '$'
|
||||
case 95: // '_'
|
||||
if (escape == NORMAL) {
|
||||
cmark_render_ascii(renderer, "\\");
|
||||
}
|
||||
cmark_render_code_point(renderer, c);
|
||||
break;
|
||||
case 45: // '-'
|
||||
if (nextc == 45) { // prevent ligature
|
||||
cmark_render_ascii(renderer, "-{}");
|
||||
} else {
|
||||
cmark_render_ascii(renderer, "-");
|
||||
}
|
||||
break;
|
||||
case 126: // '~'
|
||||
if (escape == NORMAL) {
|
||||
cmark_render_ascii(renderer, "\\textasciitilde{}");
|
||||
} else {
|
||||
cmark_render_code_point(renderer, c);
|
||||
}
|
||||
break;
|
||||
case 94: // '^'
|
||||
cmark_render_ascii(renderer, "\\^{}");
|
||||
break;
|
||||
case 92: // '\\'
|
||||
if (escape == URL) {
|
||||
// / acts as path sep even on windows:
|
||||
cmark_render_ascii(renderer, "/");
|
||||
} else {
|
||||
cmark_render_ascii(renderer, "\\textbackslash{}");
|
||||
}
|
||||
break;
|
||||
case 124: // '|'
|
||||
cmark_render_ascii(renderer, "\\textbar{}");
|
||||
break;
|
||||
case 60: // '<'
|
||||
cmark_render_ascii(renderer, "\\textless{}");
|
||||
break;
|
||||
case 62: // '>'
|
||||
cmark_render_ascii(renderer, "\\textgreater{}");
|
||||
break;
|
||||
case 91: // '['
|
||||
case 93: // ']'
|
||||
cmark_render_ascii(renderer, "{");
|
||||
cmark_render_code_point(renderer, c);
|
||||
cmark_render_ascii(renderer, "}");
|
||||
break;
|
||||
case 34: // '"'
|
||||
cmark_render_ascii(renderer, "\\textquotedbl{}");
|
||||
// requires \usepackage[T1]{fontenc}
|
||||
break;
|
||||
case 39: // '\''
|
||||
cmark_render_ascii(renderer, "\\textquotesingle{}");
|
||||
// requires \usepackage{textcomp}
|
||||
break;
|
||||
case 160: // nbsp
|
||||
cmark_render_ascii(renderer, "~");
|
||||
break;
|
||||
case 8230: // hellip
|
||||
cmark_render_ascii(renderer, "\\ldots{}");
|
||||
break;
|
||||
case 8216: // lsquo
|
||||
if (escape == NORMAL) {
|
||||
cmark_render_ascii(renderer, "`");
|
||||
} else {
|
||||
cmark_render_code_point(renderer, c);
|
||||
}
|
||||
break;
|
||||
case 8217: // rsquo
|
||||
if (escape == NORMAL) {
|
||||
cmark_render_ascii(renderer, "\'");
|
||||
} else {
|
||||
cmark_render_code_point(renderer, c);
|
||||
}
|
||||
break;
|
||||
case 8220: // ldquo
|
||||
if (escape == NORMAL) {
|
||||
cmark_render_ascii(renderer, "``");
|
||||
} else {
|
||||
cmark_render_code_point(renderer, c);
|
||||
}
|
||||
break;
|
||||
case 8221: // rdquo
|
||||
if (escape == NORMAL) {
|
||||
cmark_render_ascii(renderer, "''");
|
||||
} else {
|
||||
cmark_render_code_point(renderer, c);
|
||||
}
|
||||
break;
|
||||
case 8212: // emdash
|
||||
if (escape == NORMAL) {
|
||||
cmark_render_ascii(renderer, "---");
|
||||
} else {
|
||||
cmark_render_code_point(renderer, c);
|
||||
}
|
||||
break;
|
||||
case 8211: // endash
|
||||
if (escape == NORMAL) {
|
||||
cmark_render_ascii(renderer, "--");
|
||||
} else {
|
||||
cmark_render_code_point(renderer, c);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
cmark_render_code_point(renderer, c);
|
||||
}
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
NO_LINK,
|
||||
URL_AUTOLINK,
|
||||
EMAIL_AUTOLINK,
|
||||
NORMAL_LINK,
|
||||
INTERNAL_LINK
|
||||
} link_type;
|
||||
|
||||
static link_type get_link_type(cmark_node *node) {
|
||||
size_t title_len, url_len;
|
||||
cmark_node *link_text;
|
||||
char *realurl;
|
||||
int realurllen;
|
||||
bool isemail = false;
|
||||
|
||||
if (node->type != CMARK_NODE_LINK) {
|
||||
return NO_LINK;
|
||||
}
|
||||
|
||||
const char *url = cmark_node_get_url(node);
|
||||
cmark_chunk url_chunk = cmark_chunk_literal(url);
|
||||
|
||||
if (url && *url == '#') {
|
||||
return INTERNAL_LINK;
|
||||
}
|
||||
|
||||
url_len = strlen(url);
|
||||
if (url_len == 0 || scan_scheme(&url_chunk, 0) == 0) {
|
||||
return NO_LINK;
|
||||
}
|
||||
|
||||
const char *title = cmark_node_get_title(node);
|
||||
title_len = strlen(title);
|
||||
// if it has a title, we can't treat it as an autolink:
|
||||
if (title_len == 0) {
|
||||
|
||||
link_text = node->first_child;
|
||||
cmark_consolidate_text_nodes(link_text);
|
||||
|
||||
if (!link_text)
|
||||
return NO_LINK;
|
||||
|
||||
realurl = (char *)url;
|
||||
realurllen = (int)url_len;
|
||||
if (strncmp(realurl, "mailto:", 7) == 0) {
|
||||
realurl += 7;
|
||||
realurllen -= 7;
|
||||
isemail = true;
|
||||
}
|
||||
if (realurllen == link_text->len &&
|
||||
strncmp(realurl, (char *)link_text->data,
|
||||
link_text->len) == 0) {
|
||||
if (isemail) {
|
||||
return EMAIL_AUTOLINK;
|
||||
} else {
|
||||
return URL_AUTOLINK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NORMAL_LINK;
|
||||
}
|
||||
|
||||
static int S_get_enumlevel(cmark_node *node) {
|
||||
int enumlevel = 0;
|
||||
cmark_node *tmp = node;
|
||||
while (tmp) {
|
||||
if (tmp->type == CMARK_NODE_LIST &&
|
||||
cmark_node_get_list_type(node) == CMARK_ORDERED_LIST) {
|
||||
enumlevel++;
|
||||
}
|
||||
tmp = tmp->parent;
|
||||
}
|
||||
return enumlevel;
|
||||
}
|
||||
|
||||
static int S_render_node(cmark_renderer *renderer, cmark_node *node,
|
||||
cmark_event_type ev_type, int options) {
|
||||
int list_number;
|
||||
int enumlevel;
|
||||
char list_number_string[LIST_NUMBER_STRING_SIZE];
|
||||
bool entering = (ev_type == CMARK_EVENT_ENTER);
|
||||
cmark_list_type list_type;
|
||||
bool allow_wrap = renderer->width > 0 && !(CMARK_OPT_NOBREAKS & options);
|
||||
|
||||
// avoid warning about unused parameter:
|
||||
(void)(options);
|
||||
|
||||
switch (node->type) {
|
||||
case CMARK_NODE_DOCUMENT:
|
||||
break;
|
||||
|
||||
case CMARK_NODE_BLOCK_QUOTE:
|
||||
if (entering) {
|
||||
LIT("\\begin{quote}");
|
||||
CR();
|
||||
} else {
|
||||
LIT("\\end{quote}");
|
||||
BLANKLINE();
|
||||
}
|
||||
break;
|
||||
|
||||
case CMARK_NODE_LIST:
|
||||
list_type = cmark_node_get_list_type(node);
|
||||
if (entering) {
|
||||
LIT("\\begin{");
|
||||
LIT(list_type == CMARK_ORDERED_LIST ? "enumerate" : "itemize");
|
||||
LIT("}");
|
||||
CR();
|
||||
list_number = cmark_node_get_list_start(node);
|
||||
if (list_number > 1) {
|
||||
enumlevel = S_get_enumlevel(node);
|
||||
// latex normally supports only five levels
|
||||
if (enumlevel >= 1 && enumlevel <= 5) {
|
||||
snprintf(list_number_string, LIST_NUMBER_STRING_SIZE, "%d",
|
||||
list_number - 1); // the next item will increment this
|
||||
LIT("\\setcounter{enum");
|
||||
switch (enumlevel) {
|
||||
case 1: LIT("i"); break;
|
||||
case 2: LIT("ii"); break;
|
||||
case 3: LIT("iii"); break;
|
||||
case 4: LIT("iv"); break;
|
||||
case 5: LIT("v"); break;
|
||||
default: LIT("i"); break;
|
||||
}
|
||||
LIT("}{");
|
||||
OUT(list_number_string, false, NORMAL);
|
||||
LIT("}");
|
||||
}
|
||||
CR();
|
||||
}
|
||||
} else {
|
||||
LIT("\\end{");
|
||||
LIT(list_type == CMARK_ORDERED_LIST ? "enumerate" : "itemize");
|
||||
LIT("}");
|
||||
BLANKLINE();
|
||||
}
|
||||
break;
|
||||
|
||||
case CMARK_NODE_ITEM:
|
||||
if (entering) {
|
||||
LIT("\\item ");
|
||||
} else {
|
||||
CR();
|
||||
}
|
||||
break;
|
||||
|
||||
case CMARK_NODE_HEADING:
|
||||
if (entering) {
|
||||
switch (cmark_node_get_heading_level(node)) {
|
||||
case 1:
|
||||
LIT("\\section");
|
||||
break;
|
||||
case 2:
|
||||
LIT("\\subsection");
|
||||
break;
|
||||
case 3:
|
||||
LIT("\\subsubsection");
|
||||
break;
|
||||
case 4:
|
||||
LIT("\\paragraph");
|
||||
break;
|
||||
case 5:
|
||||
LIT("\\subparagraph");
|
||||
break;
|
||||
}
|
||||
LIT("{");
|
||||
} else {
|
||||
LIT("}");
|
||||
BLANKLINE();
|
||||
}
|
||||
break;
|
||||
|
||||
case CMARK_NODE_CODE_BLOCK:
|
||||
CR();
|
||||
LIT("\\begin{verbatim}");
|
||||
CR();
|
||||
OUT(cmark_node_get_literal(node), false, LITERAL);
|
||||
CR();
|
||||
LIT("\\end{verbatim}");
|
||||
BLANKLINE();
|
||||
break;
|
||||
|
||||
case CMARK_NODE_HTML_BLOCK:
|
||||
break;
|
||||
|
||||
case CMARK_NODE_CUSTOM_BLOCK:
|
||||
CR();
|
||||
OUT(entering ? cmark_node_get_on_enter(node) : cmark_node_get_on_exit(node),
|
||||
false, LITERAL);
|
||||
CR();
|
||||
break;
|
||||
|
||||
case CMARK_NODE_THEMATIC_BREAK:
|
||||
BLANKLINE();
|
||||
LIT("\\begin{center}\\rule{0.5\\linewidth}{\\linethickness}\\end{center}");
|
||||
BLANKLINE();
|
||||
break;
|
||||
|
||||
case CMARK_NODE_PARAGRAPH:
|
||||
if (!entering) {
|
||||
BLANKLINE();
|
||||
}
|
||||
break;
|
||||
|
||||
case CMARK_NODE_TEXT:
|
||||
OUT(cmark_node_get_literal(node), allow_wrap, NORMAL);
|
||||
break;
|
||||
|
||||
case CMARK_NODE_LINEBREAK:
|
||||
LIT("\\\\");
|
||||
CR();
|
||||
break;
|
||||
|
||||
case CMARK_NODE_SOFTBREAK:
|
||||
if (options & CMARK_OPT_HARDBREAKS) {
|
||||
LIT("\\\\");
|
||||
CR();
|
||||
} else if (renderer->width == 0 && !(CMARK_OPT_NOBREAKS & options)) {
|
||||
CR();
|
||||
} else {
|
||||
OUT(" ", allow_wrap, NORMAL);
|
||||
}
|
||||
break;
|
||||
|
||||
case CMARK_NODE_CODE:
|
||||
LIT("\\texttt{");
|
||||
OUT(cmark_node_get_literal(node), false, NORMAL);
|
||||
LIT("}");
|
||||
break;
|
||||
|
||||
case CMARK_NODE_HTML_INLINE:
|
||||
break;
|
||||
|
||||
case CMARK_NODE_CUSTOM_INLINE:
|
||||
OUT(entering ? cmark_node_get_on_enter(node) : cmark_node_get_on_exit(node),
|
||||
false, LITERAL);
|
||||
break;
|
||||
|
||||
case CMARK_NODE_STRONG:
|
||||
if (entering) {
|
||||
LIT("\\textbf{");
|
||||
} else {
|
||||
LIT("}");
|
||||
}
|
||||
break;
|
||||
|
||||
case CMARK_NODE_EMPH:
|
||||
if (entering) {
|
||||
LIT("\\emph{");
|
||||
} else {
|
||||
LIT("}");
|
||||
}
|
||||
break;
|
||||
|
||||
case CMARK_NODE_LINK:
|
||||
if (entering) {
|
||||
const char *url = cmark_node_get_url(node);
|
||||
// requires \usepackage{hyperref}
|
||||
switch (get_link_type(node)) {
|
||||
case URL_AUTOLINK:
|
||||
LIT("\\url{");
|
||||
OUT(url, false, URL);
|
||||
LIT("}");
|
||||
return 0; // Don't process further nodes to avoid double-rendering artefacts
|
||||
case EMAIL_AUTOLINK:
|
||||
LIT("\\href{");
|
||||
OUT(url, false, URL);
|
||||
LIT("}\\nolinkurl{");
|
||||
break;
|
||||
case NORMAL_LINK:
|
||||
LIT("\\href{");
|
||||
OUT(url, false, URL);
|
||||
LIT("}{");
|
||||
break;
|
||||
case INTERNAL_LINK:
|
||||
LIT("\\protect\\hyperlink{");
|
||||
OUT(url + 1, false, URL);
|
||||
LIT("}{");
|
||||
break;
|
||||
case NO_LINK:
|
||||
LIT("{"); // error?
|
||||
}
|
||||
} else {
|
||||
LIT("}");
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case CMARK_NODE_IMAGE:
|
||||
if (entering) {
|
||||
LIT("\\protect\\includegraphics{");
|
||||
// requires \include{graphicx}
|
||||
OUT(cmark_node_get_url(node), false, URL);
|
||||
LIT("}");
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
char *cmark_render_latex(cmark_node *root, int options, int width) {
|
||||
return cmark_render(root, options, width, outc, S_render_node);
|
||||
}
|
10
deps/cmark/src/libcmark.pc.in
vendored
Normal file
10
deps/cmark/src/libcmark.pc.in
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
prefix=@CMAKE_INSTALL_PREFIX@
|
||||
exec_prefix=@CMAKE_INSTALL_PREFIX@
|
||||
libdir=@CMAKE_INSTALL_FULL_LIBDIR@
|
||||
includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@
|
||||
|
||||
Name: libcmark
|
||||
Description: CommonMark parsing, rendering, and manipulation
|
||||
Version: @PROJECT_VERSION@
|
||||
Libs: -L${libdir} -lcmark
|
||||
Cflags: -I${includedir}
|
213
deps/cmark/src/main.c
vendored
Normal file
213
deps/cmark/src/main.c
vendored
Normal file
@ -0,0 +1,213 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include "config.h"
|
||||
#include "cmark.h"
|
||||
#include "node.h"
|
||||
|
||||
#if defined(__OpenBSD__)
|
||||
# include <sys/param.h>
|
||||
# if OpenBSD >= 201605
|
||||
# define USE_PLEDGE
|
||||
# include <unistd.h>
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32) && !defined(__CYGWIN__)
|
||||
#include <io.h>
|
||||
#include <fcntl.h>
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
FORMAT_NONE,
|
||||
FORMAT_HTML,
|
||||
FORMAT_XML,
|
||||
FORMAT_MAN,
|
||||
FORMAT_COMMONMARK,
|
||||
FORMAT_LATEX
|
||||
} writer_format;
|
||||
|
||||
void print_usage(void) {
|
||||
printf("Usage: cmark [FILE*]\n");
|
||||
printf("Options:\n");
|
||||
printf(" --to, -t FORMAT Specify output format (html, xml, man, "
|
||||
"commonmark, latex)\n");
|
||||
printf(" --width WIDTH Specify wrap width (default 0 = nowrap)\n");
|
||||
printf(" --sourcepos Include source position attribute\n");
|
||||
printf(" --hardbreaks Treat newlines as hard line breaks\n");
|
||||
printf(" --nobreaks Render soft line breaks as spaces\n");
|
||||
printf(" --safe Omit raw HTML and dangerous URLs\n");
|
||||
printf(" --unsafe Render raw HTML and dangerous URLs\n");
|
||||
printf(" --smart Use smart punctuation\n");
|
||||
printf(" --validate-utf8 Replace invalid UTF-8 sequences with U+FFFD\n");
|
||||
printf(" --help, -h Print usage information\n");
|
||||
printf(" --version Print version\n");
|
||||
}
|
||||
|
||||
static void print_document(cmark_node *document, writer_format writer,
|
||||
int options, int width) {
|
||||
char *result;
|
||||
|
||||
switch (writer) {
|
||||
case FORMAT_HTML:
|
||||
result = cmark_render_html(document, options);
|
||||
break;
|
||||
case FORMAT_XML:
|
||||
result = cmark_render_xml(document, options);
|
||||
break;
|
||||
case FORMAT_MAN:
|
||||
result = cmark_render_man(document, options, width);
|
||||
break;
|
||||
case FORMAT_COMMONMARK:
|
||||
result = cmark_render_commonmark(document, options, width);
|
||||
break;
|
||||
case FORMAT_LATEX:
|
||||
result = cmark_render_latex(document, options, width);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Unknown format %d\n", writer);
|
||||
exit(1);
|
||||
}
|
||||
printf("%s", result);
|
||||
document->mem->free(result);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
int i, numfps = 0;
|
||||
int *files;
|
||||
char buffer[4096];
|
||||
cmark_parser *parser;
|
||||
size_t bytes;
|
||||
cmark_node *document;
|
||||
int width = 0;
|
||||
char *unparsed;
|
||||
writer_format writer = FORMAT_HTML;
|
||||
int options = CMARK_OPT_DEFAULT;
|
||||
|
||||
#ifdef USE_PLEDGE
|
||||
if (pledge("stdio rpath", NULL) != 0) {
|
||||
perror("pledge");
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32) && !defined(__CYGWIN__)
|
||||
_setmode(_fileno(stdin), _O_BINARY);
|
||||
_setmode(_fileno(stdout), _O_BINARY);
|
||||
#endif
|
||||
|
||||
files = (int *)calloc(argc, sizeof(*files));
|
||||
|
||||
for (i = 1; i < argc; i++) {
|
||||
if (strcmp(argv[i], "--version") == 0) {
|
||||
printf("cmark %s", CMARK_VERSION_STRING);
|
||||
printf(" - CommonMark converter\n(C) 2014-2016 John MacFarlane\n");
|
||||
exit(0);
|
||||
} else if (strcmp(argv[i], "--sourcepos") == 0) {
|
||||
options |= CMARK_OPT_SOURCEPOS;
|
||||
} else if (strcmp(argv[i], "--hardbreaks") == 0) {
|
||||
options |= CMARK_OPT_HARDBREAKS;
|
||||
} else if (strcmp(argv[i], "--nobreaks") == 0) {
|
||||
options |= CMARK_OPT_NOBREAKS;
|
||||
} else if (strcmp(argv[i], "--smart") == 0) {
|
||||
options |= CMARK_OPT_SMART;
|
||||
} else if (strcmp(argv[i], "--safe") == 0) {
|
||||
options |= CMARK_OPT_SAFE;
|
||||
} else if (strcmp(argv[i], "--unsafe") == 0) {
|
||||
options |= CMARK_OPT_UNSAFE;
|
||||
} else if (strcmp(argv[i], "--validate-utf8") == 0) {
|
||||
options |= CMARK_OPT_VALIDATE_UTF8;
|
||||
} else if ((strcmp(argv[i], "--help") == 0) ||
|
||||
(strcmp(argv[i], "-h") == 0)) {
|
||||
print_usage();
|
||||
exit(0);
|
||||
} else if (strcmp(argv[i], "--width") == 0) {
|
||||
i += 1;
|
||||
if (i < argc) {
|
||||
width = (int)strtol(argv[i], &unparsed, 10);
|
||||
if (unparsed && strlen(unparsed) > 0) {
|
||||
fprintf(stderr, "failed parsing width '%s' at '%s'\n", argv[i],
|
||||
unparsed);
|
||||
exit(1);
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr, "--width requires an argument\n");
|
||||
exit(1);
|
||||
}
|
||||
} else if ((strcmp(argv[i], "-t") == 0) || (strcmp(argv[i], "--to") == 0)) {
|
||||
i += 1;
|
||||
if (i < argc) {
|
||||
if (strcmp(argv[i], "man") == 0) {
|
||||
writer = FORMAT_MAN;
|
||||
} else if (strcmp(argv[i], "html") == 0) {
|
||||
writer = FORMAT_HTML;
|
||||
} else if (strcmp(argv[i], "xml") == 0) {
|
||||
writer = FORMAT_XML;
|
||||
} else if (strcmp(argv[i], "commonmark") == 0) {
|
||||
writer = FORMAT_COMMONMARK;
|
||||
} else if (strcmp(argv[i], "latex") == 0) {
|
||||
writer = FORMAT_LATEX;
|
||||
} else {
|
||||
fprintf(stderr, "Unknown format %s\n", argv[i]);
|
||||
exit(1);
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr, "No argument provided for %s\n", argv[i - 1]);
|
||||
exit(1);
|
||||
}
|
||||
} else if (*argv[i] == '-') {
|
||||
print_usage();
|
||||
exit(1);
|
||||
} else { // treat as file argument
|
||||
files[numfps++] = i;
|
||||
}
|
||||
}
|
||||
|
||||
parser = cmark_parser_new(options);
|
||||
for (i = 0; i < numfps; i++) {
|
||||
FILE *fp = fopen(argv[files[i]], "rb");
|
||||
if (fp == NULL) {
|
||||
fprintf(stderr, "Error opening file %s: %s\n", argv[files[i]],
|
||||
strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
while ((bytes = fread(buffer, 1, sizeof(buffer), fp)) > 0) {
|
||||
cmark_parser_feed(parser, buffer, bytes);
|
||||
if (bytes < sizeof(buffer)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
if (numfps == 0) {
|
||||
|
||||
while ((bytes = fread(buffer, 1, sizeof(buffer), stdin)) > 0) {
|
||||
cmark_parser_feed(parser, buffer, bytes);
|
||||
if (bytes < sizeof(buffer)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef USE_PLEDGE
|
||||
if (pledge("stdio", NULL) != 0) {
|
||||
perror("pledge");
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
document = cmark_parser_finish(parser);
|
||||
cmark_parser_free(parser);
|
||||
|
||||
print_document(document, writer, options, width);
|
||||
|
||||
cmark_node_free(document);
|
||||
|
||||
free(files);
|
||||
|
||||
return 0;
|
||||
}
|
281
deps/cmark/src/man.c
vendored
Normal file
281
deps/cmark/src/man.c
vendored
Normal file
@ -0,0 +1,281 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "cmark.h"
|
||||
#include "node.h"
|
||||
#include "buffer.h"
|
||||
#include "utf8.h"
|
||||
#include "render.h"
|
||||
|
||||
#define OUT(s, wrap, escaping) renderer->out(renderer, s, wrap, escaping)
|
||||
#define LIT(s) renderer->out(renderer, s, false, LITERAL)
|
||||
#define CR() renderer->cr(renderer)
|
||||
#define BLANKLINE() renderer->blankline(renderer)
|
||||
#define LIST_NUMBER_SIZE 20
|
||||
|
||||
// Functions to convert cmark_nodes to groff man strings.
|
||||
static void S_outc(cmark_renderer *renderer, cmark_escaping escape, int32_t c,
|
||||
unsigned char nextc) {
|
||||
(void)(nextc);
|
||||
|
||||
if (escape == LITERAL) {
|
||||
cmark_render_code_point(renderer, c);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (c) {
|
||||
case 46:
|
||||
if (renderer->begin_line) {
|
||||
cmark_render_ascii(renderer, "\\&.");
|
||||
} else {
|
||||
cmark_render_code_point(renderer, c);
|
||||
}
|
||||
break;
|
||||
case 39:
|
||||
if (renderer->begin_line) {
|
||||
cmark_render_ascii(renderer, "\\&'");
|
||||
} else {
|
||||
cmark_render_code_point(renderer, c);
|
||||
}
|
||||
break;
|
||||
case 45:
|
||||
cmark_render_ascii(renderer, "\\-");
|
||||
break;
|
||||
case 92:
|
||||
cmark_render_ascii(renderer, "\\e");
|
||||
break;
|
||||
case 8216: // left single quote
|
||||
cmark_render_ascii(renderer, "\\[oq]");
|
||||
break;
|
||||
case 8217: // right single quote
|
||||
cmark_render_ascii(renderer, "\\[cq]");
|
||||
break;
|
||||
case 8220: // left double quote
|
||||
cmark_render_ascii(renderer, "\\[lq]");
|
||||
break;
|
||||
case 8221: // right double quote
|
||||
cmark_render_ascii(renderer, "\\[rq]");
|
||||
break;
|
||||
case 8212: // em dash
|
||||
cmark_render_ascii(renderer, "\\[em]");
|
||||
break;
|
||||
case 8211: // en dash
|
||||
cmark_render_ascii(renderer, "\\[en]");
|
||||
break;
|
||||
default:
|
||||
cmark_render_code_point(renderer, c);
|
||||
}
|
||||
}
|
||||
|
||||
static int S_render_node(cmark_renderer *renderer, cmark_node *node,
|
||||
cmark_event_type ev_type, int options) {
|
||||
cmark_node *tmp;
|
||||
int list_number;
|
||||
bool entering = (ev_type == CMARK_EVENT_ENTER);
|
||||
bool allow_wrap = renderer->width > 0 && !(CMARK_OPT_NOBREAKS & options);
|
||||
struct block_number *new_block_number;
|
||||
cmark_mem *allocator = cmark_get_default_mem_allocator();
|
||||
|
||||
// avoid unused parameter error:
|
||||
(void)(options);
|
||||
|
||||
// indent inside nested lists
|
||||
if (renderer->block_number_in_list_item &&
|
||||
node->type < CMARK_NODE_FIRST_INLINE) {
|
||||
if (entering) {
|
||||
renderer->block_number_in_list_item->number += 1;
|
||||
if (renderer->block_number_in_list_item->number == 2) {
|
||||
CR();
|
||||
LIT(".RS"); // indent
|
||||
CR();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch (node->type) {
|
||||
case CMARK_NODE_DOCUMENT:
|
||||
break;
|
||||
|
||||
case CMARK_NODE_BLOCK_QUOTE:
|
||||
if (entering) {
|
||||
CR();
|
||||
LIT(".RS");
|
||||
CR();
|
||||
} else {
|
||||
CR();
|
||||
LIT(".RE");
|
||||
CR();
|
||||
}
|
||||
break;
|
||||
|
||||
case CMARK_NODE_LIST:
|
||||
break;
|
||||
|
||||
case CMARK_NODE_ITEM:
|
||||
if (entering) {
|
||||
new_block_number = allocator->calloc(1, sizeof(struct block_number));
|
||||
new_block_number->number = 0;
|
||||
new_block_number->parent = renderer->block_number_in_list_item;
|
||||
renderer->block_number_in_list_item = new_block_number;
|
||||
CR();
|
||||
LIT(".IP ");
|
||||
if (cmark_node_get_list_type(node->parent) == CMARK_BULLET_LIST) {
|
||||
LIT("\\[bu] 2");
|
||||
} else {
|
||||
list_number = cmark_node_get_list_start(node->parent);
|
||||
tmp = node;
|
||||
while (tmp->prev) {
|
||||
tmp = tmp->prev;
|
||||
list_number += 1;
|
||||
}
|
||||
char list_number_s[LIST_NUMBER_SIZE];
|
||||
snprintf(list_number_s, LIST_NUMBER_SIZE, "\"%d.\" 4", list_number);
|
||||
LIT(list_number_s);
|
||||
}
|
||||
CR();
|
||||
} else {
|
||||
if (renderer->block_number_in_list_item) {
|
||||
if (renderer->block_number_in_list_item->number >= 2) {
|
||||
CR();
|
||||
LIT(".RE"); // de-indent
|
||||
}
|
||||
new_block_number = renderer->block_number_in_list_item;
|
||||
renderer->block_number_in_list_item =
|
||||
renderer->block_number_in_list_item->parent;
|
||||
allocator->free(new_block_number);
|
||||
}
|
||||
CR();
|
||||
}
|
||||
break;
|
||||
|
||||
case CMARK_NODE_HEADING:
|
||||
if (entering) {
|
||||
CR();
|
||||
LIT(cmark_node_get_heading_level(node) == 1 ? ".SH" : ".SS");
|
||||
CR();
|
||||
} else {
|
||||
CR();
|
||||
}
|
||||
break;
|
||||
|
||||
case CMARK_NODE_CODE_BLOCK:
|
||||
CR();
|
||||
LIT(".IP\n.nf\n\\f[C]\n");
|
||||
OUT(cmark_node_get_literal(node), false, NORMAL);
|
||||
CR();
|
||||
LIT("\\f[]\n.fi");
|
||||
CR();
|
||||
break;
|
||||
|
||||
case CMARK_NODE_HTML_BLOCK:
|
||||
break;
|
||||
|
||||
case CMARK_NODE_CUSTOM_BLOCK:
|
||||
CR();
|
||||
OUT(entering ? cmark_node_get_on_enter(node) : cmark_node_get_on_exit(node),
|
||||
false, LITERAL);
|
||||
CR();
|
||||
break;
|
||||
|
||||
case CMARK_NODE_THEMATIC_BREAK:
|
||||
CR();
|
||||
LIT(".PP\n * * * * *");
|
||||
CR();
|
||||
break;
|
||||
|
||||
case CMARK_NODE_PARAGRAPH:
|
||||
if (entering) {
|
||||
// no blank line if first paragraph in list:
|
||||
if (node->parent && node->parent->type == CMARK_NODE_ITEM &&
|
||||
node->prev == NULL) {
|
||||
// no blank line or .PP
|
||||
} else {
|
||||
CR();
|
||||
LIT(".PP");
|
||||
CR();
|
||||
}
|
||||
} else {
|
||||
CR();
|
||||
}
|
||||
break;
|
||||
|
||||
case CMARK_NODE_TEXT:
|
||||
OUT(cmark_node_get_literal(node), allow_wrap, NORMAL);
|
||||
break;
|
||||
|
||||
case CMARK_NODE_LINEBREAK:
|
||||
LIT(".PD 0\n.P\n.PD");
|
||||
CR();
|
||||
break;
|
||||
|
||||
case CMARK_NODE_SOFTBREAK:
|
||||
if (options & CMARK_OPT_HARDBREAKS) {
|
||||
LIT(".PD 0\n.P\n.PD");
|
||||
CR();
|
||||
} else if (renderer->width == 0 && !(CMARK_OPT_NOBREAKS & options)) {
|
||||
CR();
|
||||
} else {
|
||||
OUT(" ", allow_wrap, LITERAL);
|
||||
}
|
||||
break;
|
||||
|
||||
case CMARK_NODE_CODE:
|
||||
LIT("\\f[C]");
|
||||
OUT(cmark_node_get_literal(node), allow_wrap, NORMAL);
|
||||
LIT("\\f[]");
|
||||
break;
|
||||
|
||||
case CMARK_NODE_HTML_INLINE:
|
||||
break;
|
||||
|
||||
case CMARK_NODE_CUSTOM_INLINE:
|
||||
OUT(entering ? cmark_node_get_on_enter(node) : cmark_node_get_on_exit(node),
|
||||
false, LITERAL);
|
||||
break;
|
||||
|
||||
case CMARK_NODE_STRONG:
|
||||
if (entering) {
|
||||
LIT("\\f[B]");
|
||||
} else {
|
||||
LIT("\\f[]");
|
||||
}
|
||||
break;
|
||||
|
||||
case CMARK_NODE_EMPH:
|
||||
if (entering) {
|
||||
LIT("\\f[I]");
|
||||
} else {
|
||||
LIT("\\f[]");
|
||||
}
|
||||
break;
|
||||
|
||||
case CMARK_NODE_LINK:
|
||||
if (!entering) {
|
||||
LIT(" (");
|
||||
OUT(cmark_node_get_url(node), allow_wrap, URL);
|
||||
LIT(")");
|
||||
}
|
||||
break;
|
||||
|
||||
case CMARK_NODE_IMAGE:
|
||||
if (entering) {
|
||||
LIT("[IMAGE: ");
|
||||
} else {
|
||||
LIT("]");
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
char *cmark_render_man(cmark_node *root, int options, int width) {
|
||||
return cmark_render(root, options, width, S_outc, S_render_node);
|
||||
}
|
872
deps/cmark/src/node.c
vendored
Normal file
872
deps/cmark/src/node.c
vendored
Normal file
@ -0,0 +1,872 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "node.h"
|
||||
|
||||
static void S_node_unlink(cmark_node *node);
|
||||
|
||||
static CMARK_INLINE bool S_is_block(cmark_node *node) {
|
||||
if (node == NULL) {
|
||||
return false;
|
||||
}
|
||||
return node->type >= CMARK_NODE_FIRST_BLOCK &&
|
||||
node->type <= CMARK_NODE_LAST_BLOCK;
|
||||
}
|
||||
|
||||
static CMARK_INLINE bool S_is_inline(cmark_node *node) {
|
||||
if (node == NULL) {
|
||||
return false;
|
||||
}
|
||||
return node->type >= CMARK_NODE_FIRST_INLINE &&
|
||||
node->type <= CMARK_NODE_LAST_INLINE;
|
||||
}
|
||||
|
||||
static bool S_can_contain(cmark_node *node, cmark_node *child) {
|
||||
if (node == NULL || child == NULL || node == child) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Verify that child is not an ancestor of node.
|
||||
if (child->first_child != NULL) {
|
||||
cmark_node *cur = node->parent;
|
||||
|
||||
while (cur != NULL) {
|
||||
if (cur == child) {
|
||||
return false;
|
||||
}
|
||||
cur = cur->parent;
|
||||
}
|
||||
}
|
||||
|
||||
if (child->type == CMARK_NODE_DOCUMENT) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (node->type) {
|
||||
case CMARK_NODE_DOCUMENT:
|
||||
case CMARK_NODE_BLOCK_QUOTE:
|
||||
case CMARK_NODE_ITEM:
|
||||
return S_is_block(child) && child->type != CMARK_NODE_ITEM;
|
||||
|
||||
case CMARK_NODE_LIST:
|
||||
return child->type == CMARK_NODE_ITEM;
|
||||
|
||||
case CMARK_NODE_CUSTOM_BLOCK:
|
||||
return true;
|
||||
|
||||
case CMARK_NODE_PARAGRAPH:
|
||||
case CMARK_NODE_HEADING:
|
||||
case CMARK_NODE_EMPH:
|
||||
case CMARK_NODE_STRONG:
|
||||
case CMARK_NODE_LINK:
|
||||
case CMARK_NODE_IMAGE:
|
||||
case CMARK_NODE_CUSTOM_INLINE:
|
||||
return S_is_inline(child);
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
cmark_node *cmark_node_new_with_mem(cmark_node_type type, cmark_mem *mem) {
|
||||
cmark_node *node = (cmark_node *)mem->calloc(1, sizeof(*node));
|
||||
node->mem = mem;
|
||||
node->type = (uint16_t)type;
|
||||
|
||||
switch (node->type) {
|
||||
case CMARK_NODE_HEADING:
|
||||
node->as.heading.level = 1;
|
||||
break;
|
||||
|
||||
case CMARK_NODE_LIST: {
|
||||
cmark_list *list = &node->as.list;
|
||||
list->list_type = CMARK_BULLET_LIST;
|
||||
list->start = 0;
|
||||
list->tight = false;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
cmark_node *cmark_node_new(cmark_node_type type) {
|
||||
extern cmark_mem DEFAULT_MEM_ALLOCATOR;
|
||||
return cmark_node_new_with_mem(type, &DEFAULT_MEM_ALLOCATOR);
|
||||
}
|
||||
|
||||
// Free a cmark_node list and any children.
|
||||
static void S_free_nodes(cmark_node *e) {
|
||||
cmark_mem *mem = e->mem;
|
||||
cmark_node *next;
|
||||
while (e != NULL) {
|
||||
switch (e->type) {
|
||||
case CMARK_NODE_CODE_BLOCK:
|
||||
mem->free(e->data);
|
||||
mem->free(e->as.code.info);
|
||||
break;
|
||||
case CMARK_NODE_TEXT:
|
||||
case CMARK_NODE_HTML_INLINE:
|
||||
case CMARK_NODE_CODE:
|
||||
case CMARK_NODE_HTML_BLOCK:
|
||||
mem->free(e->data);
|
||||
break;
|
||||
case CMARK_NODE_LINK:
|
||||
case CMARK_NODE_IMAGE:
|
||||
mem->free(e->as.link.url);
|
||||
mem->free(e->as.link.title);
|
||||
break;
|
||||
case CMARK_NODE_CUSTOM_BLOCK:
|
||||
case CMARK_NODE_CUSTOM_INLINE:
|
||||
mem->free(e->as.custom.on_enter);
|
||||
mem->free(e->as.custom.on_exit);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (e->last_child) {
|
||||
// Splice children into list
|
||||
e->last_child->next = e->next;
|
||||
e->next = e->first_child;
|
||||
}
|
||||
next = e->next;
|
||||
mem->free(e);
|
||||
e = next;
|
||||
}
|
||||
}
|
||||
|
||||
void cmark_node_free(cmark_node *node) {
|
||||
S_node_unlink(node);
|
||||
node->next = NULL;
|
||||
S_free_nodes(node);
|
||||
}
|
||||
|
||||
cmark_node_type cmark_node_get_type(cmark_node *node) {
|
||||
if (node == NULL) {
|
||||
return CMARK_NODE_NONE;
|
||||
} else {
|
||||
return (cmark_node_type)node->type;
|
||||
}
|
||||
}
|
||||
|
||||
const char *cmark_node_get_type_string(cmark_node *node) {
|
||||
if (node == NULL) {
|
||||
return "NONE";
|
||||
}
|
||||
|
||||
switch (node->type) {
|
||||
case CMARK_NODE_NONE:
|
||||
return "none";
|
||||
case CMARK_NODE_DOCUMENT:
|
||||
return "document";
|
||||
case CMARK_NODE_BLOCK_QUOTE:
|
||||
return "block_quote";
|
||||
case CMARK_NODE_LIST:
|
||||
return "list";
|
||||
case CMARK_NODE_ITEM:
|
||||
return "item";
|
||||
case CMARK_NODE_CODE_BLOCK:
|
||||
return "code_block";
|
||||
case CMARK_NODE_HTML_BLOCK:
|
||||
return "html_block";
|
||||
case CMARK_NODE_CUSTOM_BLOCK:
|
||||
return "custom_block";
|
||||
case CMARK_NODE_PARAGRAPH:
|
||||
return "paragraph";
|
||||
case CMARK_NODE_HEADING:
|
||||
return "heading";
|
||||
case CMARK_NODE_THEMATIC_BREAK:
|
||||
return "thematic_break";
|
||||
case CMARK_NODE_TEXT:
|
||||
return "text";
|
||||
case CMARK_NODE_SOFTBREAK:
|
||||
return "softbreak";
|
||||
case CMARK_NODE_LINEBREAK:
|
||||
return "linebreak";
|
||||
case CMARK_NODE_CODE:
|
||||
return "code";
|
||||
case CMARK_NODE_HTML_INLINE:
|
||||
return "html_inline";
|
||||
case CMARK_NODE_CUSTOM_INLINE:
|
||||
return "custom_inline";
|
||||
case CMARK_NODE_EMPH:
|
||||
return "emph";
|
||||
case CMARK_NODE_STRONG:
|
||||
return "strong";
|
||||
case CMARK_NODE_LINK:
|
||||
return "link";
|
||||
case CMARK_NODE_IMAGE:
|
||||
return "image";
|
||||
}
|
||||
|
||||
return "<unknown>";
|
||||
}
|
||||
|
||||
cmark_node *cmark_node_next(cmark_node *node) {
|
||||
if (node == NULL) {
|
||||
return NULL;
|
||||
} else {
|
||||
return node->next;
|
||||
}
|
||||
}
|
||||
|
||||
cmark_node *cmark_node_previous(cmark_node *node) {
|
||||
if (node == NULL) {
|
||||
return NULL;
|
||||
} else {
|
||||
return node->prev;
|
||||
}
|
||||
}
|
||||
|
||||
cmark_node *cmark_node_parent(cmark_node *node) {
|
||||
if (node == NULL) {
|
||||
return NULL;
|
||||
} else {
|
||||
return node->parent;
|
||||
}
|
||||
}
|
||||
|
||||
cmark_node *cmark_node_first_child(cmark_node *node) {
|
||||
if (node == NULL) {
|
||||
return NULL;
|
||||
} else {
|
||||
return node->first_child;
|
||||
}
|
||||
}
|
||||
|
||||
cmark_node *cmark_node_last_child(cmark_node *node) {
|
||||
if (node == NULL) {
|
||||
return NULL;
|
||||
} else {
|
||||
return node->last_child;
|
||||
}
|
||||
}
|
||||
|
||||
static bufsize_t cmark_set_cstr(cmark_mem *mem, unsigned char **dst,
|
||||
const char *src) {
|
||||
unsigned char *old = *dst;
|
||||
bufsize_t len;
|
||||
|
||||
if (src && src[0]) {
|
||||
len = (bufsize_t)strlen(src);
|
||||
*dst = (unsigned char *)mem->realloc(NULL, len + 1);
|
||||
memcpy(*dst, src, len + 1);
|
||||
} else {
|
||||
len = 0;
|
||||
*dst = NULL;
|
||||
}
|
||||
if (old) {
|
||||
mem->free(old);
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
void *cmark_node_get_user_data(cmark_node *node) {
|
||||
if (node == NULL) {
|
||||
return NULL;
|
||||
} else {
|
||||
return node->user_data;
|
||||
}
|
||||
}
|
||||
|
||||
int cmark_node_set_user_data(cmark_node *node, void *user_data) {
|
||||
if (node == NULL) {
|
||||
return 0;
|
||||
}
|
||||
node->user_data = user_data;
|
||||
return 1;
|
||||
}
|
||||
|
||||
const char *cmark_node_get_literal(cmark_node *node) {
|
||||
if (node == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
switch (node->type) {
|
||||
case CMARK_NODE_HTML_BLOCK:
|
||||
case CMARK_NODE_TEXT:
|
||||
case CMARK_NODE_HTML_INLINE:
|
||||
case CMARK_NODE_CODE:
|
||||
case CMARK_NODE_CODE_BLOCK:
|
||||
return node->data ? (char *)node->data : "";
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int cmark_node_set_literal(cmark_node *node, const char *content) {
|
||||
if (node == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (node->type) {
|
||||
case CMARK_NODE_HTML_BLOCK:
|
||||
case CMARK_NODE_TEXT:
|
||||
case CMARK_NODE_HTML_INLINE:
|
||||
case CMARK_NODE_CODE:
|
||||
case CMARK_NODE_CODE_BLOCK:
|
||||
node->len = cmark_set_cstr(node->mem, &node->data, content);
|
||||
return 1;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cmark_node_get_heading_level(cmark_node *node) {
|
||||
if (node == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (node->type) {
|
||||
case CMARK_NODE_HEADING:
|
||||
return node->as.heading.level;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cmark_node_set_heading_level(cmark_node *node, int level) {
|
||||
if (node == NULL || level < 1 || level > 6) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (node->type) {
|
||||
case CMARK_NODE_HEADING:
|
||||
node->as.heading.level = level;
|
||||
return 1;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
cmark_list_type cmark_node_get_list_type(cmark_node *node) {
|
||||
if (node == NULL) {
|
||||
return CMARK_NO_LIST;
|
||||
}
|
||||
|
||||
if (node->type == CMARK_NODE_LIST) {
|
||||
return (cmark_list_type)node->as.list.list_type;
|
||||
} else {
|
||||
return CMARK_NO_LIST;
|
||||
}
|
||||
}
|
||||
|
||||
int cmark_node_set_list_type(cmark_node *node, cmark_list_type type) {
|
||||
if (!(type == CMARK_BULLET_LIST || type == CMARK_ORDERED_LIST)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (node == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (node->type == CMARK_NODE_LIST) {
|
||||
node->as.list.list_type = (unsigned char)type;
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
cmark_delim_type cmark_node_get_list_delim(cmark_node *node) {
|
||||
if (node == NULL) {
|
||||
return CMARK_NO_DELIM;
|
||||
}
|
||||
|
||||
if (node->type == CMARK_NODE_LIST) {
|
||||
return (cmark_delim_type)node->as.list.delimiter;
|
||||
} else {
|
||||
return CMARK_NO_DELIM;
|
||||
}
|
||||
}
|
||||
|
||||
int cmark_node_set_list_delim(cmark_node *node, cmark_delim_type delim) {
|
||||
if (!(delim == CMARK_PERIOD_DELIM || delim == CMARK_PAREN_DELIM)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (node == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (node->type == CMARK_NODE_LIST) {
|
||||
node->as.list.delimiter = (unsigned char)delim;
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int cmark_node_get_list_start(cmark_node *node) {
|
||||
if (node == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (node->type == CMARK_NODE_LIST) {
|
||||
return node->as.list.start;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int cmark_node_set_list_start(cmark_node *node, int start) {
|
||||
if (node == NULL || start < 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (node->type == CMARK_NODE_LIST) {
|
||||
node->as.list.start = start;
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int cmark_node_get_list_tight(cmark_node *node) {
|
||||
if (node == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (node->type == CMARK_NODE_LIST) {
|
||||
return node->as.list.tight;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int cmark_node_set_list_tight(cmark_node *node, int tight) {
|
||||
if (node == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (node->type == CMARK_NODE_LIST) {
|
||||
node->as.list.tight = tight == 1;
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
const char *cmark_node_get_fence_info(cmark_node *node) {
|
||||
if (node == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (node->type == CMARK_NODE_CODE_BLOCK) {
|
||||
return node->as.code.info ? (char *)node->as.code.info : "";
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int cmark_node_set_fence_info(cmark_node *node, const char *info) {
|
||||
if (node == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (node->type == CMARK_NODE_CODE_BLOCK) {
|
||||
cmark_set_cstr(node->mem, &node->as.code.info, info);
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
const char *cmark_node_get_url(cmark_node *node) {
|
||||
if (node == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
switch (node->type) {
|
||||
case CMARK_NODE_LINK:
|
||||
case CMARK_NODE_IMAGE:
|
||||
return node->as.link.url ? (char *)node->as.link.url : "";
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int cmark_node_set_url(cmark_node *node, const char *url) {
|
||||
if (node == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (node->type) {
|
||||
case CMARK_NODE_LINK:
|
||||
case CMARK_NODE_IMAGE:
|
||||
cmark_set_cstr(node->mem, &node->as.link.url, url);
|
||||
return 1;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *cmark_node_get_title(cmark_node *node) {
|
||||
if (node == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
switch (node->type) {
|
||||
case CMARK_NODE_LINK:
|
||||
case CMARK_NODE_IMAGE:
|
||||
return node->as.link.title ? (char *)node->as.link.title : "";
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int cmark_node_set_title(cmark_node *node, const char *title) {
|
||||
if (node == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (node->type) {
|
||||
case CMARK_NODE_LINK:
|
||||
case CMARK_NODE_IMAGE:
|
||||
cmark_set_cstr(node->mem, &node->as.link.title, title);
|
||||
return 1;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *cmark_node_get_on_enter(cmark_node *node) {
|
||||
if (node == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
switch (node->type) {
|
||||
case CMARK_NODE_CUSTOM_INLINE:
|
||||
case CMARK_NODE_CUSTOM_BLOCK:
|
||||
return node->as.custom.on_enter ? (char *)node->as.custom.on_enter : "";
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int cmark_node_set_on_enter(cmark_node *node, const char *on_enter) {
|
||||
if (node == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (node->type) {
|
||||
case CMARK_NODE_CUSTOM_INLINE:
|
||||
case CMARK_NODE_CUSTOM_BLOCK:
|
||||
cmark_set_cstr(node->mem, &node->as.custom.on_enter, on_enter);
|
||||
return 1;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *cmark_node_get_on_exit(cmark_node *node) {
|
||||
if (node == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
switch (node->type) {
|
||||
case CMARK_NODE_CUSTOM_INLINE:
|
||||
case CMARK_NODE_CUSTOM_BLOCK:
|
||||
return node->as.custom.on_exit ? (char *)node->as.custom.on_exit : "";
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int cmark_node_set_on_exit(cmark_node *node, const char *on_exit) {
|
||||
if (node == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (node->type) {
|
||||
case CMARK_NODE_CUSTOM_INLINE:
|
||||
case CMARK_NODE_CUSTOM_BLOCK:
|
||||
cmark_set_cstr(node->mem, &node->as.custom.on_exit, on_exit);
|
||||
return 1;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cmark_node_get_start_line(cmark_node *node) {
|
||||
if (node == NULL) {
|
||||
return 0;
|
||||
}
|
||||
return node->start_line;
|
||||
}
|
||||
|
||||
int cmark_node_get_start_column(cmark_node *node) {
|
||||
if (node == NULL) {
|
||||
return 0;
|
||||
}
|
||||
return node->start_column;
|
||||
}
|
||||
|
||||
int cmark_node_get_end_line(cmark_node *node) {
|
||||
if (node == NULL) {
|
||||
return 0;
|
||||
}
|
||||
return node->end_line;
|
||||
}
|
||||
|
||||
int cmark_node_get_end_column(cmark_node *node) {
|
||||
if (node == NULL) {
|
||||
return 0;
|
||||
}
|
||||
return node->end_column;
|
||||
}
|
||||
|
||||
// Unlink a node without adjusting its next, prev, and parent pointers.
|
||||
static void S_node_unlink(cmark_node *node) {
|
||||
if (node == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (node->prev) {
|
||||
node->prev->next = node->next;
|
||||
}
|
||||
if (node->next) {
|
||||
node->next->prev = node->prev;
|
||||
}
|
||||
|
||||
// Adjust first_child and last_child of parent.
|
||||
cmark_node *parent = node->parent;
|
||||
if (parent) {
|
||||
if (parent->first_child == node) {
|
||||
parent->first_child = node->next;
|
||||
}
|
||||
if (parent->last_child == node) {
|
||||
parent->last_child = node->prev;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cmark_node_unlink(cmark_node *node) {
|
||||
S_node_unlink(node);
|
||||
|
||||
node->next = NULL;
|
||||
node->prev = NULL;
|
||||
node->parent = NULL;
|
||||
}
|
||||
|
||||
int cmark_node_insert_before(cmark_node *node, cmark_node *sibling) {
|
||||
if (node == NULL || sibling == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!node->parent || !S_can_contain(node->parent, sibling)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
S_node_unlink(sibling);
|
||||
|
||||
cmark_node *old_prev = node->prev;
|
||||
|
||||
// Insert 'sibling' between 'old_prev' and 'node'.
|
||||
if (old_prev) {
|
||||
old_prev->next = sibling;
|
||||
}
|
||||
sibling->prev = old_prev;
|
||||
sibling->next = node;
|
||||
node->prev = sibling;
|
||||
|
||||
// Set new parent.
|
||||
cmark_node *parent = node->parent;
|
||||
sibling->parent = parent;
|
||||
|
||||
// Adjust first_child of parent if inserted as first child.
|
||||
if (parent && !old_prev) {
|
||||
parent->first_child = sibling;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int cmark_node_insert_after(cmark_node *node, cmark_node *sibling) {
|
||||
if (node == NULL || sibling == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!node->parent || !S_can_contain(node->parent, sibling)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
S_node_unlink(sibling);
|
||||
|
||||
cmark_node *old_next = node->next;
|
||||
|
||||
// Insert 'sibling' between 'node' and 'old_next'.
|
||||
if (old_next) {
|
||||
old_next->prev = sibling;
|
||||
}
|
||||
sibling->next = old_next;
|
||||
sibling->prev = node;
|
||||
node->next = sibling;
|
||||
|
||||
// Set new parent.
|
||||
cmark_node *parent = node->parent;
|
||||
sibling->parent = parent;
|
||||
|
||||
// Adjust last_child of parent if inserted as last child.
|
||||
if (parent && !old_next) {
|
||||
parent->last_child = sibling;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int cmark_node_replace(cmark_node *oldnode, cmark_node *newnode) {
|
||||
if (!cmark_node_insert_before(oldnode, newnode)) {
|
||||
return 0;
|
||||
}
|
||||
cmark_node_unlink(oldnode);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int cmark_node_prepend_child(cmark_node *node, cmark_node *child) {
|
||||
if (!S_can_contain(node, child)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
S_node_unlink(child);
|
||||
|
||||
cmark_node *old_first_child = node->first_child;
|
||||
|
||||
child->next = old_first_child;
|
||||
child->prev = NULL;
|
||||
child->parent = node;
|
||||
node->first_child = child;
|
||||
|
||||
if (old_first_child) {
|
||||
old_first_child->prev = child;
|
||||
} else {
|
||||
// Also set last_child if node previously had no children.
|
||||
node->last_child = child;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int cmark_node_append_child(cmark_node *node, cmark_node *child) {
|
||||
if (!S_can_contain(node, child)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
S_node_unlink(child);
|
||||
|
||||
cmark_node *old_last_child = node->last_child;
|
||||
|
||||
child->next = NULL;
|
||||
child->prev = old_last_child;
|
||||
child->parent = node;
|
||||
node->last_child = child;
|
||||
|
||||
if (old_last_child) {
|
||||
old_last_child->next = child;
|
||||
} else {
|
||||
// Also set first_child if node previously had no children.
|
||||
node->first_child = child;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void S_print_error(FILE *out, cmark_node *node, const char *elem) {
|
||||
if (out == NULL) {
|
||||
return;
|
||||
}
|
||||
fprintf(out, "Invalid '%s' in node type %s at %d:%d\n", elem,
|
||||
cmark_node_get_type_string(node), node->start_line,
|
||||
node->start_column);
|
||||
}
|
||||
|
||||
int cmark_node_check(cmark_node *node, FILE *out) {
|
||||
cmark_node *cur;
|
||||
int errors = 0;
|
||||
|
||||
if (!node) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
cur = node;
|
||||
for (;;) {
|
||||
if (cur->first_child) {
|
||||
if (cur->first_child->prev != NULL) {
|
||||
S_print_error(out, cur->first_child, "prev");
|
||||
cur->first_child->prev = NULL;
|
||||
++errors;
|
||||
}
|
||||
if (cur->first_child->parent != cur) {
|
||||
S_print_error(out, cur->first_child, "parent");
|
||||
cur->first_child->parent = cur;
|
||||
++errors;
|
||||
}
|
||||
cur = cur->first_child;
|
||||
continue;
|
||||
}
|
||||
|
||||
next_sibling:
|
||||
if (cur == node) {
|
||||
break;
|
||||
}
|
||||
if (cur->next) {
|
||||
if (cur->next->prev != cur) {
|
||||
S_print_error(out, cur->next, "prev");
|
||||
cur->next->prev = cur;
|
||||
++errors;
|
||||
}
|
||||
if (cur->next->parent != cur->parent) {
|
||||
S_print_error(out, cur->next, "parent");
|
||||
cur->next->parent = cur->parent;
|
||||
++errors;
|
||||
}
|
||||
cur = cur->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cur->parent->last_child != cur) {
|
||||
S_print_error(out, cur->parent, "last_child");
|
||||
cur->parent->last_child = cur;
|
||||
++errors;
|
||||
}
|
||||
cur = cur->parent;
|
||||
goto next_sibling;
|
||||
}
|
||||
|
||||
return errors;
|
||||
}
|
92
deps/cmark/src/node.h
vendored
Normal file
92
deps/cmark/src/node.h
vendored
Normal file
@ -0,0 +1,92 @@
|
||||
#ifndef CMARK_NODE_H
|
||||
#define CMARK_NODE_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "cmark.h"
|
||||
#include "buffer.h"
|
||||
|
||||
typedef struct {
|
||||
int marker_offset;
|
||||
int padding;
|
||||
int start;
|
||||
unsigned char list_type;
|
||||
unsigned char delimiter;
|
||||
unsigned char bullet_char;
|
||||
bool tight;
|
||||
} cmark_list;
|
||||
|
||||
typedef struct {
|
||||
unsigned char *info;
|
||||
uint8_t fence_length;
|
||||
uint8_t fence_offset;
|
||||
unsigned char fence_char;
|
||||
int8_t fenced;
|
||||
} cmark_code;
|
||||
|
||||
typedef struct {
|
||||
int internal_offset;
|
||||
int8_t level;
|
||||
bool setext;
|
||||
} cmark_heading;
|
||||
|
||||
typedef struct {
|
||||
unsigned char *url;
|
||||
unsigned char *title;
|
||||
} cmark_link;
|
||||
|
||||
typedef struct {
|
||||
unsigned char *on_enter;
|
||||
unsigned char *on_exit;
|
||||
} cmark_custom;
|
||||
|
||||
enum cmark_node__internal_flags {
|
||||
CMARK_NODE__OPEN = (1 << 0),
|
||||
CMARK_NODE__LAST_LINE_BLANK = (1 << 1),
|
||||
CMARK_NODE__LAST_LINE_CHECKED = (1 << 2),
|
||||
};
|
||||
|
||||
struct cmark_node {
|
||||
cmark_mem *mem;
|
||||
|
||||
struct cmark_node *next;
|
||||
struct cmark_node *prev;
|
||||
struct cmark_node *parent;
|
||||
struct cmark_node *first_child;
|
||||
struct cmark_node *last_child;
|
||||
|
||||
void *user_data;
|
||||
|
||||
unsigned char *data;
|
||||
bufsize_t len;
|
||||
|
||||
int start_line;
|
||||
int start_column;
|
||||
int end_line;
|
||||
int end_column;
|
||||
uint16_t type;
|
||||
uint16_t flags;
|
||||
|
||||
union {
|
||||
cmark_list list;
|
||||
cmark_code code;
|
||||
cmark_heading heading;
|
||||
cmark_link link;
|
||||
cmark_custom custom;
|
||||
int html_block_type;
|
||||
} as;
|
||||
};
|
||||
|
||||
CMARK_EXPORT int cmark_node_check(cmark_node *node, FILE *out);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
42
deps/cmark/src/parser.h
vendored
Normal file
42
deps/cmark/src/parser.h
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
#ifndef CMARK_AST_H
|
||||
#define CMARK_AST_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include "references.h"
|
||||
#include "node.h"
|
||||
#include "buffer.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define MAX_LINK_LABEL_LENGTH 1000
|
||||
|
||||
struct cmark_parser {
|
||||
struct cmark_mem *mem;
|
||||
struct cmark_reference_map *refmap;
|
||||
struct cmark_node *root;
|
||||
struct cmark_node *current;
|
||||
int line_number;
|
||||
bufsize_t offset;
|
||||
bufsize_t column;
|
||||
bufsize_t first_nonspace;
|
||||
bufsize_t first_nonspace_column;
|
||||
bufsize_t thematic_break_kill_pos;
|
||||
int indent;
|
||||
bool blank;
|
||||
bool partially_consumed_tab;
|
||||
cmark_strbuf curline;
|
||||
bufsize_t last_line_length;
|
||||
cmark_strbuf linebuf;
|
||||
cmark_strbuf content;
|
||||
int options;
|
||||
bool last_buffer_ended_with_cr;
|
||||
unsigned int total_size;
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
171
deps/cmark/src/references.c
vendored
Normal file
171
deps/cmark/src/references.c
vendored
Normal file
@ -0,0 +1,171 @@
|
||||
#include "cmark.h"
|
||||
#include "utf8.h"
|
||||
#include "parser.h"
|
||||
#include "references.h"
|
||||
#include "inlines.h"
|
||||
#include "chunk.h"
|
||||
|
||||
static void reference_free(cmark_reference_map *map, cmark_reference *ref) {
|
||||
cmark_mem *mem = map->mem;
|
||||
if (ref != NULL) {
|
||||
mem->free(ref->label);
|
||||
mem->free(ref->url);
|
||||
mem->free(ref->title);
|
||||
mem->free(ref);
|
||||
}
|
||||
}
|
||||
|
||||
// normalize reference: collapse internal whitespace to single space,
|
||||
// remove leading/trailing whitespace, case fold
|
||||
// Return NULL if the reference name is actually empty (i.e. composed
|
||||
// solely from whitespace)
|
||||
static unsigned char *normalize_reference(cmark_mem *mem, cmark_chunk *ref) {
|
||||
cmark_strbuf normalized = CMARK_BUF_INIT(mem);
|
||||
unsigned char *result;
|
||||
|
||||
if (ref == NULL)
|
||||
return NULL;
|
||||
|
||||
if (ref->len == 0)
|
||||
return NULL;
|
||||
|
||||
cmark_utf8proc_case_fold(&normalized, ref->data, ref->len);
|
||||
cmark_strbuf_trim(&normalized);
|
||||
cmark_strbuf_normalize_whitespace(&normalized);
|
||||
|
||||
result = cmark_strbuf_detach(&normalized);
|
||||
assert(result);
|
||||
|
||||
if (result[0] == '\0') {
|
||||
mem->free(result);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void cmark_reference_create(cmark_reference_map *map, cmark_chunk *label,
|
||||
cmark_chunk *url, cmark_chunk *title) {
|
||||
cmark_reference *ref;
|
||||
unsigned char *reflabel = normalize_reference(map->mem, label);
|
||||
|
||||
/* empty reference name, or composed from only whitespace */
|
||||
if (reflabel == NULL)
|
||||
return;
|
||||
|
||||
assert(map->sorted == NULL);
|
||||
|
||||
ref = (cmark_reference *)map->mem->calloc(1, sizeof(*ref));
|
||||
ref->label = reflabel;
|
||||
ref->url = cmark_clean_url(map->mem, url);
|
||||
ref->title = cmark_clean_title(map->mem, title);
|
||||
ref->age = map->size;
|
||||
ref->next = map->refs;
|
||||
|
||||
if (ref->url != NULL)
|
||||
ref->size += strlen((char*)ref->url);
|
||||
if (ref->title != NULL)
|
||||
ref->size += strlen((char*)ref->title);
|
||||
|
||||
map->refs = ref;
|
||||
map->size++;
|
||||
}
|
||||
|
||||
static int
|
||||
labelcmp(const unsigned char *a, const unsigned char *b) {
|
||||
return strcmp((const char *)a, (const char *)b);
|
||||
}
|
||||
|
||||
static int
|
||||
refcmp(const void *p1, const void *p2) {
|
||||
cmark_reference *r1 = *(cmark_reference **)p1;
|
||||
cmark_reference *r2 = *(cmark_reference **)p2;
|
||||
int res = labelcmp(r1->label, r2->label);
|
||||
return res ? res : ((int)r1->age - (int)r2->age);
|
||||
}
|
||||
|
||||
static int
|
||||
refsearch(const void *label, const void *p2) {
|
||||
cmark_reference *ref = *(cmark_reference **)p2;
|
||||
return labelcmp((const unsigned char *)label, ref->label);
|
||||
}
|
||||
|
||||
static void sort_references(cmark_reference_map *map) {
|
||||
unsigned int i = 0, last = 0, size = map->size;
|
||||
cmark_reference *r = map->refs, **sorted = NULL;
|
||||
|
||||
sorted = (cmark_reference **)map->mem->calloc(size, sizeof(cmark_reference *));
|
||||
while (r) {
|
||||
sorted[i++] = r;
|
||||
r = r->next;
|
||||
}
|
||||
|
||||
qsort(sorted, size, sizeof(cmark_reference *), refcmp);
|
||||
|
||||
for (i = 1; i < size; i++) {
|
||||
if (labelcmp(sorted[i]->label, sorted[last]->label) != 0)
|
||||
sorted[++last] = sorted[i];
|
||||
}
|
||||
map->sorted = sorted;
|
||||
map->size = last + 1;
|
||||
}
|
||||
|
||||
// Returns reference if refmap contains a reference with matching
|
||||
// label, otherwise NULL.
|
||||
cmark_reference *cmark_reference_lookup(cmark_reference_map *map,
|
||||
cmark_chunk *label) {
|
||||
cmark_reference **ref = NULL;
|
||||
cmark_reference *r = NULL;
|
||||
unsigned char *norm;
|
||||
|
||||
if (label->len < 1 || label->len > MAX_LINK_LABEL_LENGTH)
|
||||
return NULL;
|
||||
|
||||
if (map == NULL || !map->size)
|
||||
return NULL;
|
||||
|
||||
norm = normalize_reference(map->mem, label);
|
||||
if (norm == NULL)
|
||||
return NULL;
|
||||
|
||||
if (!map->sorted)
|
||||
sort_references(map);
|
||||
|
||||
ref = (cmark_reference **)bsearch(norm, map->sorted, map->size, sizeof(cmark_reference *),
|
||||
refsearch);
|
||||
map->mem->free(norm);
|
||||
|
||||
if (ref != NULL) {
|
||||
r = ref[0];
|
||||
/* Check for expansion limit */
|
||||
if (map->max_ref_size && r->size > map->max_ref_size - map->ref_size)
|
||||
return NULL;
|
||||
map->ref_size += r->size;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
void cmark_reference_map_free(cmark_reference_map *map) {
|
||||
cmark_reference *ref;
|
||||
|
||||
if (map == NULL)
|
||||
return;
|
||||
|
||||
ref = map->refs;
|
||||
while (ref) {
|
||||
cmark_reference *next = ref->next;
|
||||
reference_free(map, ref);
|
||||
ref = next;
|
||||
}
|
||||
|
||||
map->mem->free(map->sorted);
|
||||
map->mem->free(map);
|
||||
}
|
||||
|
||||
cmark_reference_map *cmark_reference_map_new(cmark_mem *mem) {
|
||||
cmark_reference_map *map =
|
||||
(cmark_reference_map *)mem->calloc(1, sizeof(cmark_reference_map));
|
||||
map->mem = mem;
|
||||
return map;
|
||||
}
|
43
deps/cmark/src/references.h
vendored
Normal file
43
deps/cmark/src/references.h
vendored
Normal file
@ -0,0 +1,43 @@
|
||||
#ifndef CMARK_REFERENCES_H
|
||||
#define CMARK_REFERENCES_H
|
||||
|
||||
#include "chunk.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct cmark_reference {
|
||||
struct cmark_reference *next;
|
||||
unsigned char *label;
|
||||
unsigned char *url;
|
||||
unsigned char *title;
|
||||
unsigned int age;
|
||||
unsigned int size;
|
||||
};
|
||||
|
||||
typedef struct cmark_reference cmark_reference;
|
||||
|
||||
struct cmark_reference_map {
|
||||
cmark_mem *mem;
|
||||
cmark_reference *refs;
|
||||
cmark_reference **sorted;
|
||||
unsigned int size;
|
||||
unsigned int ref_size;
|
||||
unsigned int max_ref_size;
|
||||
};
|
||||
|
||||
typedef struct cmark_reference_map cmark_reference_map;
|
||||
|
||||
cmark_reference_map *cmark_reference_map_new(cmark_mem *mem);
|
||||
void cmark_reference_map_free(cmark_reference_map *map);
|
||||
cmark_reference *cmark_reference_lookup(cmark_reference_map *map,
|
||||
cmark_chunk *label);
|
||||
extern void cmark_reference_create(cmark_reference_map *map, cmark_chunk *label,
|
||||
cmark_chunk *url, cmark_chunk *title);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
193
deps/cmark/src/render.c
vendored
Normal file
193
deps/cmark/src/render.c
vendored
Normal file
@ -0,0 +1,193 @@
|
||||
#include <stdlib.h>
|
||||
#include "buffer.h"
|
||||
#include "cmark.h"
|
||||
#include "utf8.h"
|
||||
#include "render.h"
|
||||
#include "node.h"
|
||||
#include "cmark_ctype.h"
|
||||
|
||||
static CMARK_INLINE void S_cr(cmark_renderer *renderer) {
|
||||
if (renderer->need_cr < 1) {
|
||||
renderer->need_cr = 1;
|
||||
}
|
||||
}
|
||||
|
||||
static CMARK_INLINE void S_blankline(cmark_renderer *renderer) {
|
||||
if (renderer->need_cr < 2) {
|
||||
renderer->need_cr = 2;
|
||||
}
|
||||
}
|
||||
|
||||
static void S_out(cmark_renderer *renderer, const char *source, bool wrap,
|
||||
cmark_escaping escape) {
|
||||
int length = strlen(source);
|
||||
unsigned char nextc;
|
||||
int32_t c;
|
||||
int i = 0;
|
||||
int last_nonspace;
|
||||
int len;
|
||||
int k = renderer->buffer->size - 1;
|
||||
|
||||
wrap = wrap && !renderer->no_linebreaks;
|
||||
|
||||
if (renderer->in_tight_list_item && renderer->need_cr > 1) {
|
||||
renderer->need_cr = 1;
|
||||
}
|
||||
while (renderer->need_cr) {
|
||||
if (k < 0 || renderer->buffer->ptr[k] == '\n') {
|
||||
k -= 1;
|
||||
} else {
|
||||
cmark_strbuf_putc(renderer->buffer, '\n');
|
||||
if (renderer->need_cr > 1) {
|
||||
cmark_strbuf_put(renderer->buffer, renderer->prefix->ptr,
|
||||
renderer->prefix->size);
|
||||
}
|
||||
}
|
||||
renderer->column = 0;
|
||||
renderer->last_breakable = 0;
|
||||
renderer->begin_line = true;
|
||||
renderer->begin_content = true;
|
||||
renderer->need_cr -= 1;
|
||||
}
|
||||
|
||||
while (i < length) {
|
||||
if (renderer->begin_line) {
|
||||
cmark_strbuf_put(renderer->buffer, renderer->prefix->ptr,
|
||||
renderer->prefix->size);
|
||||
// note: this assumes prefix is ascii:
|
||||
renderer->column = renderer->prefix->size;
|
||||
}
|
||||
|
||||
len = cmark_utf8proc_iterate((const uint8_t *)source + i, length - i, &c);
|
||||
if (len == -1) { // error condition
|
||||
return; // return without rendering rest of string
|
||||
}
|
||||
nextc = source[i + len];
|
||||
if (c == 32 && wrap) {
|
||||
if (!renderer->begin_line) {
|
||||
last_nonspace = renderer->buffer->size;
|
||||
cmark_strbuf_putc(renderer->buffer, ' ');
|
||||
renderer->column += 1;
|
||||
renderer->begin_line = false;
|
||||
renderer->begin_content = false;
|
||||
// skip following spaces
|
||||
while (source[i + 1] == ' ') {
|
||||
i++;
|
||||
}
|
||||
// We don't allow breaks that make a digit the first character
|
||||
// because this causes problems with commonmark output.
|
||||
if (!cmark_isdigit(source[i + 1])) {
|
||||
renderer->last_breakable = last_nonspace;
|
||||
}
|
||||
}
|
||||
|
||||
} else if (escape == LITERAL) {
|
||||
if (c == 10) {
|
||||
cmark_strbuf_putc(renderer->buffer, '\n');
|
||||
renderer->column = 0;
|
||||
renderer->begin_line = true;
|
||||
renderer->begin_content = true;
|
||||
renderer->last_breakable = 0;
|
||||
} else {
|
||||
cmark_render_code_point(renderer, c);
|
||||
renderer->begin_line = false;
|
||||
// we don't set 'begin_content' to false til we've
|
||||
// finished parsing a digit. Reason: in commonmark
|
||||
// we need to escape a potential list marker after
|
||||
// a digit:
|
||||
renderer->begin_content =
|
||||
renderer->begin_content && cmark_isdigit(c) == 1;
|
||||
}
|
||||
} else {
|
||||
(renderer->outc)(renderer, escape, c, nextc);
|
||||
renderer->begin_line = false;
|
||||
renderer->begin_content =
|
||||
renderer->begin_content && cmark_isdigit(c) == 1;
|
||||
}
|
||||
|
||||
// If adding the character went beyond width, look for an
|
||||
// earlier place where the line could be broken:
|
||||
if (renderer->width > 0 && renderer->column > renderer->width &&
|
||||
!renderer->begin_line && renderer->last_breakable > 0) {
|
||||
|
||||
// copy from last_breakable to remainder
|
||||
unsigned char *src = renderer->buffer->ptr +
|
||||
renderer->last_breakable + 1;
|
||||
bufsize_t remainder_len = renderer->buffer->size -
|
||||
renderer->last_breakable - 1;
|
||||
unsigned char *remainder =
|
||||
(unsigned char *)renderer->mem->realloc(NULL, remainder_len);
|
||||
memcpy(remainder, src, remainder_len);
|
||||
// truncate at last_breakable
|
||||
cmark_strbuf_truncate(renderer->buffer, renderer->last_breakable);
|
||||
// add newline, prefix, and remainder
|
||||
cmark_strbuf_putc(renderer->buffer, '\n');
|
||||
cmark_strbuf_put(renderer->buffer, renderer->prefix->ptr,
|
||||
renderer->prefix->size);
|
||||
cmark_strbuf_put(renderer->buffer, remainder, remainder_len);
|
||||
renderer->column = renderer->prefix->size + remainder_len;
|
||||
renderer->mem->free(remainder);
|
||||
renderer->last_breakable = 0;
|
||||
renderer->begin_line = false;
|
||||
renderer->begin_content = false;
|
||||
}
|
||||
|
||||
i += len;
|
||||
}
|
||||
}
|
||||
|
||||
// Assumes no newlines, assumes ascii content:
|
||||
void cmark_render_ascii(cmark_renderer *renderer, const char *s) {
|
||||
int origsize = renderer->buffer->size;
|
||||
cmark_strbuf_puts(renderer->buffer, s);
|
||||
renderer->column += renderer->buffer->size - origsize;
|
||||
}
|
||||
|
||||
void cmark_render_code_point(cmark_renderer *renderer, uint32_t c) {
|
||||
cmark_utf8proc_encode_char(c, renderer->buffer);
|
||||
renderer->column += 1;
|
||||
}
|
||||
|
||||
char *cmark_render(cmark_node *root, int options, int width,
|
||||
void (*outc)(cmark_renderer *, cmark_escaping, int32_t,
|
||||
unsigned char),
|
||||
int (*render_node)(cmark_renderer *renderer,
|
||||
cmark_node *node,
|
||||
cmark_event_type ev_type, int options)) {
|
||||
cmark_mem *mem = root->mem;
|
||||
cmark_strbuf pref = CMARK_BUF_INIT(mem);
|
||||
cmark_strbuf buf = CMARK_BUF_INIT(mem);
|
||||
cmark_node *cur;
|
||||
cmark_event_type ev_type;
|
||||
char *result;
|
||||
cmark_iter *iter = cmark_iter_new(root);
|
||||
|
||||
cmark_renderer renderer = {options,
|
||||
mem, &buf, &pref, 0, width,
|
||||
0, 0, true, true, false,
|
||||
false, NULL,
|
||||
outc, S_cr, S_blankline, S_out};
|
||||
|
||||
while ((ev_type = cmark_iter_next(iter)) != CMARK_EVENT_DONE) {
|
||||
cur = cmark_iter_get_node(iter);
|
||||
if (!render_node(&renderer, cur, ev_type, options)) {
|
||||
// a false value causes us to skip processing
|
||||
// the node's contents. this is used for
|
||||
// autolinks.
|
||||
cmark_iter_reset(iter, cur, CMARK_EVENT_EXIT);
|
||||
}
|
||||
}
|
||||
|
||||
// ensure final newline
|
||||
if (renderer.buffer->size == 0 || renderer.buffer->ptr[renderer.buffer->size - 1] != '\n') {
|
||||
cmark_strbuf_putc(renderer.buffer, '\n');
|
||||
}
|
||||
|
||||
result = (char *)cmark_strbuf_detach(renderer.buffer);
|
||||
|
||||
cmark_iter_free(iter);
|
||||
cmark_strbuf_free(renderer.prefix);
|
||||
cmark_strbuf_free(renderer.buffer);
|
||||
|
||||
return result;
|
||||
}
|
55
deps/cmark/src/render.h
vendored
Normal file
55
deps/cmark/src/render.h
vendored
Normal file
@ -0,0 +1,55 @@
|
||||
#ifndef CMARK_RENDER_H
|
||||
#define CMARK_RENDER_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "buffer.h"
|
||||
|
||||
typedef enum { LITERAL, NORMAL, TITLE, URL } cmark_escaping;
|
||||
|
||||
struct block_number {
|
||||
int number;
|
||||
struct block_number *parent;
|
||||
};
|
||||
|
||||
struct cmark_renderer {
|
||||
int options;
|
||||
cmark_mem *mem;
|
||||
cmark_strbuf *buffer;
|
||||
cmark_strbuf *prefix;
|
||||
int column;
|
||||
int width;
|
||||
int need_cr;
|
||||
bufsize_t last_breakable;
|
||||
bool begin_line;
|
||||
bool begin_content;
|
||||
bool no_linebreaks;
|
||||
bool in_tight_list_item;
|
||||
struct block_number *block_number_in_list_item;
|
||||
void (*outc)(struct cmark_renderer *, cmark_escaping, int32_t, unsigned char);
|
||||
void (*cr)(struct cmark_renderer *);
|
||||
void (*blankline)(struct cmark_renderer *);
|
||||
void (*out)(struct cmark_renderer *, const char *, bool, cmark_escaping);
|
||||
};
|
||||
|
||||
typedef struct cmark_renderer cmark_renderer;
|
||||
|
||||
void cmark_render_ascii(cmark_renderer *renderer, const char *s);
|
||||
|
||||
void cmark_render_code_point(cmark_renderer *renderer, uint32_t c);
|
||||
|
||||
char *cmark_render(cmark_node *root, int options, int width,
|
||||
void (*outc)(cmark_renderer *, cmark_escaping, int32_t,
|
||||
unsigned char),
|
||||
int (*render_node)(cmark_renderer *renderer,
|
||||
cmark_node *node,
|
||||
cmark_event_type ev_type, int options));
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
13634
deps/cmark/src/scanners.c
vendored
Normal file
13634
deps/cmark/src/scanners.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
61
deps/cmark/src/scanners.h
vendored
Normal file
61
deps/cmark/src/scanners.h
vendored
Normal file
@ -0,0 +1,61 @@
|
||||
#include "cmark.h"
|
||||
#include "chunk.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
bufsize_t _scan_at(bufsize_t (*scanner)(const unsigned char *), cmark_chunk *c,
|
||||
bufsize_t offset);
|
||||
bufsize_t _scan_scheme(const unsigned char *p);
|
||||
bufsize_t _scan_autolink_uri(const unsigned char *p);
|
||||
bufsize_t _scan_autolink_email(const unsigned char *p);
|
||||
bufsize_t _scan_html_tag(const unsigned char *p);
|
||||
bufsize_t _scan_html_comment(const unsigned char *p);
|
||||
bufsize_t _scan_html_pi(const unsigned char *p);
|
||||
bufsize_t _scan_html_declaration(const unsigned char *p);
|
||||
bufsize_t _scan_html_cdata(const unsigned char *p);
|
||||
bufsize_t _scan_html_block_start(const unsigned char *p);
|
||||
bufsize_t _scan_html_block_start_7(const unsigned char *p);
|
||||
bufsize_t _scan_html_block_end_1(const unsigned char *p);
|
||||
bufsize_t _scan_html_block_end_2(const unsigned char *p);
|
||||
bufsize_t _scan_html_block_end_3(const unsigned char *p);
|
||||
bufsize_t _scan_html_block_end_4(const unsigned char *p);
|
||||
bufsize_t _scan_html_block_end_5(const unsigned char *p);
|
||||
bufsize_t _scan_link_title(const unsigned char *p);
|
||||
bufsize_t _scan_spacechars(const unsigned char *p);
|
||||
bufsize_t _scan_atx_heading_start(const unsigned char *p);
|
||||
bufsize_t _scan_setext_heading_line(const unsigned char *p);
|
||||
bufsize_t _scan_open_code_fence(const unsigned char *p);
|
||||
bufsize_t _scan_close_code_fence(const unsigned char *p);
|
||||
bufsize_t _scan_entity(const unsigned char *p);
|
||||
bufsize_t _scan_dangerous_url(const unsigned char *p);
|
||||
|
||||
#define scan_scheme(c, n) _scan_at(&_scan_scheme, c, n)
|
||||
#define scan_autolink_uri(c, n) _scan_at(&_scan_autolink_uri, c, n)
|
||||
#define scan_autolink_email(c, n) _scan_at(&_scan_autolink_email, c, n)
|
||||
#define scan_html_tag(c, n) _scan_at(&_scan_html_tag, c, n)
|
||||
#define scan_html_comment(c, n) _scan_at(&_scan_html_comment, c, n)
|
||||
#define scan_html_pi(c, n) _scan_at(&_scan_html_pi, c, n)
|
||||
#define scan_html_declaration(c, n) _scan_at(&_scan_html_declaration, c, n)
|
||||
#define scan_html_cdata(c, n) _scan_at(&_scan_html_cdata, c, n)
|
||||
#define scan_html_block_start(c, n) _scan_at(&_scan_html_block_start, c, n)
|
||||
#define scan_html_block_start_7(c, n) _scan_at(&_scan_html_block_start_7, c, n)
|
||||
#define scan_html_block_end_1(c, n) _scan_at(&_scan_html_block_end_1, c, n)
|
||||
#define scan_html_block_end_2(c, n) _scan_at(&_scan_html_block_end_2, c, n)
|
||||
#define scan_html_block_end_3(c, n) _scan_at(&_scan_html_block_end_3, c, n)
|
||||
#define scan_html_block_end_4(c, n) _scan_at(&_scan_html_block_end_4, c, n)
|
||||
#define scan_html_block_end_5(c, n) _scan_at(&_scan_html_block_end_5, c, n)
|
||||
#define scan_link_title(c, n) _scan_at(&_scan_link_title, c, n)
|
||||
#define scan_spacechars(c, n) _scan_at(&_scan_spacechars, c, n)
|
||||
#define scan_atx_heading_start(c, n) _scan_at(&_scan_atx_heading_start, c, n)
|
||||
#define scan_setext_heading_line(c, n) \
|
||||
_scan_at(&_scan_setext_heading_line, c, n)
|
||||
#define scan_open_code_fence(c, n) _scan_at(&_scan_open_code_fence, c, n)
|
||||
#define scan_close_code_fence(c, n) _scan_at(&_scan_close_code_fence, c, n)
|
||||
#define scan_entity(c, n) _scan_at(&_scan_entity, c, n)
|
||||
#define scan_dangerous_url(c, n) _scan_at(&_scan_dangerous_url, c, n)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
344
deps/cmark/src/scanners.re
vendored
Normal file
344
deps/cmark/src/scanners.re
vendored
Normal file
@ -0,0 +1,344 @@
|
||||
#include <stdlib.h>
|
||||
#include "chunk.h"
|
||||
#include "scanners.h"
|
||||
|
||||
bufsize_t _scan_at(bufsize_t (*scanner)(const unsigned char *), cmark_chunk *c, bufsize_t offset)
|
||||
{
|
||||
bufsize_t res;
|
||||
unsigned char *ptr = (unsigned char *)c->data;
|
||||
|
||||
if (ptr == NULL || offset > c->len) {
|
||||
return 0;
|
||||
} else {
|
||||
unsigned char lim = ptr[c->len];
|
||||
|
||||
ptr[c->len] = '\0';
|
||||
res = scanner(ptr + offset);
|
||||
ptr[c->len] = lim;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/*!re2c
|
||||
re2c:define:YYCTYPE = "unsigned char";
|
||||
re2c:define:YYCURSOR = p;
|
||||
re2c:define:YYMARKER = marker;
|
||||
re2c:define:YYCTXMARKER = marker;
|
||||
re2c:yyfill:enable = 0;
|
||||
|
||||
wordchar = [^\x00-\x20];
|
||||
|
||||
spacechar = [ \t\v\f\r\n];
|
||||
|
||||
reg_char = [^\\()\x00-\x20];
|
||||
|
||||
escaped_char = [\\][!"#$%&'()*+,./:;<=>?@[\\\]^_`{|}~-];
|
||||
|
||||
tagname = [A-Za-z][A-Za-z0-9-]*;
|
||||
|
||||
blocktagname = 'address'|'article'|'aside'|'base'|'basefont'|'blockquote'|'body'|'caption'|'center'|'col'|'colgroup'|'dd'|'details'|'dialog'|'dir'|'div'|'dl'|'dt'|'fieldset'|'figcaption'|'figure'|'footer'|'form'|'frame'|'frameset'|'h1'|'h2'|'h3'|'h4'|'h5'|'h6'|'head'|'header'|'hr'|'html'|'iframe'|'legend'|'li'|'link'|'main'|'menu'|'menuitem'|'nav'|'noframes'|'ol'|'optgroup'|'option'|'p'|'param'|'section'|'source'|'title'|'summary'|'table'|'tbody'|'td'|'tfoot'|'th'|'thead'|'title'|'tr'|'track'|'ul';
|
||||
|
||||
attributename = [a-zA-Z_:][a-zA-Z0-9:._-]*;
|
||||
|
||||
unquotedvalue = [^ \t\r\n\v\f"'=<>`\x00]+;
|
||||
singlequotedvalue = ['][^'\x00]*['];
|
||||
doublequotedvalue = ["][^"\x00]*["];
|
||||
|
||||
attributevalue = unquotedvalue | singlequotedvalue | doublequotedvalue;
|
||||
|
||||
attributevaluespec = spacechar* [=] spacechar* attributevalue;
|
||||
|
||||
attribute = spacechar+ attributename attributevaluespec?;
|
||||
|
||||
opentag = tagname attribute* spacechar* [/]? [>];
|
||||
closetag = [/] tagname spacechar* [>];
|
||||
|
||||
htmlcomment = "--" ([^\x00-]+ | "-" [^\x00-] | "--" [^\x00>])* "-->";
|
||||
|
||||
processinginstruction = ([^?>\x00]+ | [?][^>\x00] | [>])+;
|
||||
|
||||
declaration = [A-Z]+ [^>\x00]*;
|
||||
|
||||
cdata = "CDATA[" ([^\]\x00]+ | "]" [^\]\x00] | "]]" [^>\x00])*;
|
||||
|
||||
htmltag = opentag | closetag;
|
||||
|
||||
in_parens_nosp = [(] (reg_char|escaped_char|[\\])* [)];
|
||||
|
||||
in_double_quotes = ["] (escaped_char|[^"\x00])* ["];
|
||||
in_single_quotes = ['] (escaped_char|[^'\x00])* ['];
|
||||
in_parens = [(] (escaped_char|[^)\x00])* [)];
|
||||
|
||||
scheme = [A-Za-z][A-Za-z0-9.+-]{1,31};
|
||||
*/
|
||||
|
||||
// Try to match a scheme including colon.
|
||||
bufsize_t _scan_scheme(const unsigned char *p)
|
||||
{
|
||||
const unsigned char *marker = NULL;
|
||||
const unsigned char *start = p;
|
||||
/*!re2c
|
||||
scheme [:] { return (bufsize_t)(p - start); }
|
||||
* { return 0; }
|
||||
*/
|
||||
}
|
||||
|
||||
// Try to match URI autolink after first <, returning number of chars matched.
|
||||
bufsize_t _scan_autolink_uri(const unsigned char *p)
|
||||
{
|
||||
const unsigned char *marker = NULL;
|
||||
const unsigned char *start = p;
|
||||
/*!re2c
|
||||
scheme [:][^\x00-\x20<>]*[>] { return (bufsize_t)(p - start); }
|
||||
* { return 0; }
|
||||
*/
|
||||
}
|
||||
|
||||
// Try to match email autolink after first <, returning num of chars matched.
|
||||
bufsize_t _scan_autolink_email(const unsigned char *p)
|
||||
{
|
||||
const unsigned char *marker = NULL;
|
||||
const unsigned char *start = p;
|
||||
/*!re2c
|
||||
[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+
|
||||
[@]
|
||||
[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?
|
||||
([.][a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*
|
||||
[>] { return (bufsize_t)(p - start); }
|
||||
* { return 0; }
|
||||
*/
|
||||
}
|
||||
|
||||
// Try to match an HTML tag after first <, returning num of chars matched.
|
||||
bufsize_t _scan_html_tag(const unsigned char *p)
|
||||
{
|
||||
const unsigned char *marker = NULL;
|
||||
const unsigned char *start = p;
|
||||
/*!re2c
|
||||
htmltag { return (bufsize_t)(p - start); }
|
||||
* { return 0; }
|
||||
*/
|
||||
}
|
||||
|
||||
bufsize_t _scan_html_comment(const unsigned char *p)
|
||||
{
|
||||
const unsigned char *marker = NULL;
|
||||
const unsigned char *start = p;
|
||||
/*!re2c
|
||||
htmlcomment { return (bufsize_t)(p - start); }
|
||||
* { return 0; }
|
||||
*/
|
||||
}
|
||||
|
||||
bufsize_t _scan_html_pi(const unsigned char *p)
|
||||
{
|
||||
const unsigned char *marker = NULL;
|
||||
const unsigned char *start = p;
|
||||
/*!re2c
|
||||
processinginstruction { return (bufsize_t)(p - start); }
|
||||
* { return 0; }
|
||||
*/
|
||||
}
|
||||
|
||||
bufsize_t _scan_html_declaration(const unsigned char *p)
|
||||
{
|
||||
const unsigned char *marker = NULL;
|
||||
const unsigned char *start = p;
|
||||
/*!re2c
|
||||
declaration { return (bufsize_t)(p - start); }
|
||||
* { return 0; }
|
||||
*/
|
||||
}
|
||||
|
||||
bufsize_t _scan_html_cdata(const unsigned char *p)
|
||||
{
|
||||
const unsigned char *marker = NULL;
|
||||
const unsigned char *start = p;
|
||||
/*!re2c
|
||||
cdata { return (bufsize_t)(p - start); }
|
||||
* { return 0; }
|
||||
*/
|
||||
}
|
||||
|
||||
// Try to match an HTML block tag start line, returning
|
||||
// an integer code for the type of block (1-6, matching the spec).
|
||||
// #7 is handled by a separate function, below.
|
||||
bufsize_t _scan_html_block_start(const unsigned char *p)
|
||||
{
|
||||
const unsigned char *marker = NULL;
|
||||
/*!re2c
|
||||
[<] ('script'|'pre'|'textarea'|'style') (spacechar | [>]) { return 1; }
|
||||
'<!--' { return 2; }
|
||||
'<?' { return 3; }
|
||||
'<!' [A-Za-z] { return 4; }
|
||||
'<![CDATA[' { return 5; }
|
||||
[<] [/]? blocktagname (spacechar | [/]? [>]) { return 6; }
|
||||
* { return 0; }
|
||||
*/
|
||||
}
|
||||
|
||||
// Try to match an HTML block tag start line of type 7, returning
|
||||
// 7 if successful, 0 if not.
|
||||
bufsize_t _scan_html_block_start_7(const unsigned char *p)
|
||||
{
|
||||
const unsigned char *marker = NULL;
|
||||
/*!re2c
|
||||
[<] (opentag | closetag) [\t\n\f ]* [\r\n] { return 7; }
|
||||
* { return 0; }
|
||||
*/
|
||||
}
|
||||
|
||||
// Try to match an HTML block end line of type 1
|
||||
bufsize_t _scan_html_block_end_1(const unsigned char *p)
|
||||
{
|
||||
const unsigned char *marker = NULL;
|
||||
const unsigned char *start = p;
|
||||
/*!re2c
|
||||
[^\n\x00]* [<] [/] ('script'|'pre'|'textarea'|'style') [>] { return (bufsize_t)(p - start); }
|
||||
* { return 0; }
|
||||
*/
|
||||
}
|
||||
|
||||
// Try to match an HTML block end line of type 2
|
||||
bufsize_t _scan_html_block_end_2(const unsigned char *p)
|
||||
{
|
||||
const unsigned char *marker = NULL;
|
||||
const unsigned char *start = p;
|
||||
/*!re2c
|
||||
[^\n\x00]* '-->' { return (bufsize_t)(p - start); }
|
||||
* { return 0; }
|
||||
*/
|
||||
}
|
||||
|
||||
// Try to match an HTML block end line of type 3
|
||||
bufsize_t _scan_html_block_end_3(const unsigned char *p)
|
||||
{
|
||||
const unsigned char *marker = NULL;
|
||||
const unsigned char *start = p;
|
||||
/*!re2c
|
||||
[^\n\x00]* '?>' { return (bufsize_t)(p - start); }
|
||||
* { return 0; }
|
||||
*/
|
||||
}
|
||||
|
||||
// Try to match an HTML block end line of type 4
|
||||
bufsize_t _scan_html_block_end_4(const unsigned char *p)
|
||||
{
|
||||
const unsigned char *marker = NULL;
|
||||
const unsigned char *start = p;
|
||||
/*!re2c
|
||||
[^\n\x00]* '>' { return (bufsize_t)(p - start); }
|
||||
* { return 0; }
|
||||
*/
|
||||
}
|
||||
|
||||
// Try to match an HTML block end line of type 5
|
||||
bufsize_t _scan_html_block_end_5(const unsigned char *p)
|
||||
{
|
||||
const unsigned char *marker = NULL;
|
||||
const unsigned char *start = p;
|
||||
/*!re2c
|
||||
[^\n\x00]* ']]>' { return (bufsize_t)(p - start); }
|
||||
* { return 0; }
|
||||
*/
|
||||
}
|
||||
|
||||
// Try to match a link title (in single quotes, in double quotes, or
|
||||
// in parentheses), returning number of chars matched. Allow one
|
||||
// level of internal nesting (quotes within quotes).
|
||||
bufsize_t _scan_link_title(const unsigned char *p)
|
||||
{
|
||||
const unsigned char *marker = NULL;
|
||||
const unsigned char *start = p;
|
||||
/*!re2c
|
||||
["] (escaped_char|[^"\x00])* ["] { return (bufsize_t)(p - start); }
|
||||
['] (escaped_char|[^'\x00])* ['] { return (bufsize_t)(p - start); }
|
||||
[(] (escaped_char|[^()\x00])* [)] { return (bufsize_t)(p - start); }
|
||||
* { return 0; }
|
||||
*/
|
||||
}
|
||||
|
||||
// Match space characters, including newlines.
|
||||
bufsize_t _scan_spacechars(const unsigned char *p)
|
||||
{
|
||||
const unsigned char *start = p; \
|
||||
/*!re2c
|
||||
[ \t\v\f\r\n]+ { return (bufsize_t)(p - start); }
|
||||
* { return 0; }
|
||||
*/
|
||||
}
|
||||
|
||||
// Match ATX heading start.
|
||||
bufsize_t _scan_atx_heading_start(const unsigned char *p)
|
||||
{
|
||||
const unsigned char *marker = NULL;
|
||||
const unsigned char *start = p;
|
||||
/*!re2c
|
||||
[#]{1,6} ([ \t]+|[\r\n]) { return (bufsize_t)(p - start); }
|
||||
* { return 0; }
|
||||
*/
|
||||
}
|
||||
|
||||
// Match setext heading line. Return 1 for level-1 heading,
|
||||
// 2 for level-2, 0 for no match.
|
||||
bufsize_t _scan_setext_heading_line(const unsigned char *p)
|
||||
{
|
||||
const unsigned char *marker = NULL;
|
||||
/*!re2c
|
||||
[=]+ [ \t]* [\r\n] { return 1; }
|
||||
[-]+ [ \t]* [\r\n] { return 2; }
|
||||
* { return 0; }
|
||||
*/
|
||||
}
|
||||
|
||||
// Scan an opening code fence.
|
||||
bufsize_t _scan_open_code_fence(const unsigned char *p)
|
||||
{
|
||||
const unsigned char *marker = NULL;
|
||||
const unsigned char *start = p;
|
||||
/*!re2c
|
||||
[`]{3,} / [^`\r\n\x00]*[\r\n] { return (bufsize_t)(p - start); }
|
||||
[~]{3,} / [^\r\n\x00]*[\r\n] { return (bufsize_t)(p - start); }
|
||||
* { return 0; }
|
||||
*/
|
||||
}
|
||||
|
||||
// Scan a closing code fence with length at least len.
|
||||
bufsize_t _scan_close_code_fence(const unsigned char *p)
|
||||
{
|
||||
const unsigned char *marker = NULL;
|
||||
const unsigned char *start = p;
|
||||
/*!re2c
|
||||
[`]{3,} / [ \t]*[\r\n] { return (bufsize_t)(p - start); }
|
||||
[~]{3,} / [ \t]*[\r\n] { return (bufsize_t)(p - start); }
|
||||
* { return 0; }
|
||||
*/
|
||||
}
|
||||
|
||||
// Scans an entity.
|
||||
// Returns number of chars matched.
|
||||
bufsize_t _scan_entity(const unsigned char *p)
|
||||
{
|
||||
const unsigned char *marker = NULL;
|
||||
const unsigned char *start = p;
|
||||
/*!re2c
|
||||
[&] ([#] ([Xx][A-Fa-f0-9]{1,6}|[0-9]{1,7}) |[A-Za-z][A-Za-z0-9]{1,31} ) [;]
|
||||
{ return (bufsize_t)(p - start); }
|
||||
* { return 0; }
|
||||
*/
|
||||
}
|
||||
|
||||
// Returns positive value if a URL begins in a way that is potentially
|
||||
// dangerous, with javascript:, vbscript:, file:, or data:, otherwise 0.
|
||||
bufsize_t _scan_dangerous_url(const unsigned char *p)
|
||||
{
|
||||
const unsigned char *marker = NULL;
|
||||
const unsigned char *start = p;
|
||||
/*!re2c
|
||||
'data:image/' ('png'|'gif'|'jpeg'|'webp') { return 0; }
|
||||
'javascript:' | 'vbscript:' | 'file:' | 'data:' { return (bufsize_t)(p - start); }
|
||||
* { return 0; }
|
||||
*/
|
||||
}
|
||||
|
317
deps/cmark/src/utf8.c
vendored
Normal file
317
deps/cmark/src/utf8.c
vendored
Normal file
@ -0,0 +1,317 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "cmark_ctype.h"
|
||||
#include "utf8.h"
|
||||
|
||||
static const int8_t utf8proc_utf8class[256] = {
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||
4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0};
|
||||
|
||||
static void encode_unknown(cmark_strbuf *buf) {
|
||||
static const uint8_t repl[] = {239, 191, 189};
|
||||
cmark_strbuf_put(buf, repl, 3);
|
||||
}
|
||||
|
||||
static int utf8proc_charlen(const uint8_t *str, bufsize_t str_len) {
|
||||
int length, i;
|
||||
|
||||
if (!str_len)
|
||||
return 0;
|
||||
|
||||
length = utf8proc_utf8class[str[0]];
|
||||
|
||||
if (!length)
|
||||
return -1;
|
||||
|
||||
if (str_len >= 0 && (bufsize_t)length > str_len)
|
||||
return -str_len;
|
||||
|
||||
for (i = 1; i < length; i++) {
|
||||
if ((str[i] & 0xC0) != 0x80)
|
||||
return -i;
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
// Validate a single UTF-8 character according to RFC 3629.
|
||||
static int utf8proc_valid(const uint8_t *str, bufsize_t str_len) {
|
||||
int length = utf8proc_utf8class[str[0]];
|
||||
|
||||
if (!length)
|
||||
return -1;
|
||||
|
||||
if ((bufsize_t)length > str_len)
|
||||
return -str_len;
|
||||
|
||||
switch (length) {
|
||||
case 2:
|
||||
if ((str[1] & 0xC0) != 0x80)
|
||||
return -1;
|
||||
if (str[0] < 0xC2) {
|
||||
// Overlong
|
||||
return -length;
|
||||
}
|
||||
break;
|
||||
|
||||
case 3:
|
||||
if ((str[1] & 0xC0) != 0x80)
|
||||
return -1;
|
||||
if ((str[2] & 0xC0) != 0x80)
|
||||
return -2;
|
||||
if (str[0] == 0xE0) {
|
||||
if (str[1] < 0xA0) {
|
||||
// Overlong
|
||||
return -length;
|
||||
}
|
||||
} else if (str[0] == 0xED) {
|
||||
if (str[1] >= 0xA0) {
|
||||
// Surrogate
|
||||
return -length;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 4:
|
||||
if ((str[1] & 0xC0) != 0x80)
|
||||
return -1;
|
||||
if ((str[2] & 0xC0) != 0x80)
|
||||
return -2;
|
||||
if ((str[3] & 0xC0) != 0x80)
|
||||
return -3;
|
||||
if (str[0] == 0xF0) {
|
||||
if (str[1] < 0x90) {
|
||||
// Overlong
|
||||
return -length;
|
||||
}
|
||||
} else if (str[0] >= 0xF4) {
|
||||
if (str[0] > 0xF4 || str[1] >= 0x90) {
|
||||
// Above 0x10FFFF
|
||||
return -length;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
void cmark_utf8proc_check(cmark_strbuf *ob, const uint8_t *line,
|
||||
bufsize_t size) {
|
||||
bufsize_t i = 0;
|
||||
|
||||
while (i < size) {
|
||||
bufsize_t org = i;
|
||||
int charlen = 0;
|
||||
|
||||
while (i < size) {
|
||||
if (line[i] < 0x80 && line[i] != 0) {
|
||||
i++;
|
||||
} else if (line[i] >= 0x80) {
|
||||
charlen = utf8proc_valid(line + i, size - i);
|
||||
if (charlen < 0) {
|
||||
charlen = -charlen;
|
||||
break;
|
||||
}
|
||||
i += charlen;
|
||||
} else if (line[i] == 0) {
|
||||
// ASCII NUL is technically valid but rejected
|
||||
// for security reasons.
|
||||
charlen = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i > org) {
|
||||
cmark_strbuf_put(ob, line + org, i - org);
|
||||
}
|
||||
|
||||
if (i >= size) {
|
||||
break;
|
||||
} else {
|
||||
// Invalid UTF-8
|
||||
encode_unknown(ob);
|
||||
i += charlen;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int cmark_utf8proc_iterate(const uint8_t *str, bufsize_t str_len,
|
||||
int32_t *dst) {
|
||||
int length;
|
||||
int32_t uc = -1;
|
||||
|
||||
*dst = -1;
|
||||
length = utf8proc_charlen(str, str_len);
|
||||
if (length < 0)
|
||||
return -1;
|
||||
|
||||
switch (length) {
|
||||
case 1:
|
||||
uc = str[0];
|
||||
break;
|
||||
case 2:
|
||||
uc = ((str[0] & 0x1F) << 6) + (str[1] & 0x3F);
|
||||
if (uc < 0x80)
|
||||
uc = -1;
|
||||
break;
|
||||
case 3:
|
||||
uc = ((str[0] & 0x0F) << 12) + ((str[1] & 0x3F) << 6) + (str[2] & 0x3F);
|
||||
if (uc < 0x800 || (uc >= 0xD800 && uc < 0xE000))
|
||||
uc = -1;
|
||||
break;
|
||||
case 4:
|
||||
uc = ((str[0] & 0x07) << 18) + ((str[1] & 0x3F) << 12) +
|
||||
((str[2] & 0x3F) << 6) + (str[3] & 0x3F);
|
||||
if (uc < 0x10000 || uc >= 0x110000)
|
||||
uc = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (uc < 0)
|
||||
return -1;
|
||||
|
||||
*dst = uc;
|
||||
return length;
|
||||
}
|
||||
|
||||
void cmark_utf8proc_encode_char(int32_t uc, cmark_strbuf *buf) {
|
||||
uint8_t dst[4];
|
||||
bufsize_t len = 0;
|
||||
|
||||
assert(uc >= 0);
|
||||
|
||||
if (uc < 0x80) {
|
||||
dst[0] = (uint8_t)(uc);
|
||||
len = 1;
|
||||
} else if (uc < 0x800) {
|
||||
dst[0] = (uint8_t)(0xC0 + (uc >> 6));
|
||||
dst[1] = 0x80 + (uc & 0x3F);
|
||||
len = 2;
|
||||
} else if (uc == 0xFFFF) {
|
||||
dst[0] = 0xFF;
|
||||
len = 1;
|
||||
} else if (uc == 0xFFFE) {
|
||||
dst[0] = 0xFE;
|
||||
len = 1;
|
||||
} else if (uc < 0x10000) {
|
||||
dst[0] = (uint8_t)(0xE0 + (uc >> 12));
|
||||
dst[1] = 0x80 + ((uc >> 6) & 0x3F);
|
||||
dst[2] = 0x80 + (uc & 0x3F);
|
||||
len = 3;
|
||||
} else if (uc < 0x110000) {
|
||||
dst[0] = (uint8_t)(0xF0 + (uc >> 18));
|
||||
dst[1] = 0x80 + ((uc >> 12) & 0x3F);
|
||||
dst[2] = 0x80 + ((uc >> 6) & 0x3F);
|
||||
dst[3] = 0x80 + (uc & 0x3F);
|
||||
len = 4;
|
||||
} else {
|
||||
encode_unknown(buf);
|
||||
return;
|
||||
}
|
||||
|
||||
cmark_strbuf_put(buf, dst, len);
|
||||
}
|
||||
|
||||
void cmark_utf8proc_case_fold(cmark_strbuf *dest, const uint8_t *str,
|
||||
bufsize_t len) {
|
||||
int32_t c;
|
||||
|
||||
#define bufpush(x) cmark_utf8proc_encode_char(x, dest)
|
||||
|
||||
while (len > 0) {
|
||||
bufsize_t char_len = cmark_utf8proc_iterate(str, len, &c);
|
||||
|
||||
if (char_len >= 0) {
|
||||
#include "case_fold_switch.inc"
|
||||
} else {
|
||||
encode_unknown(dest);
|
||||
char_len = -char_len;
|
||||
}
|
||||
|
||||
str += char_len;
|
||||
len -= char_len;
|
||||
}
|
||||
}
|
||||
|
||||
// matches anything in the Zs class, plus LF, CR, TAB, FF.
|
||||
int cmark_utf8proc_is_space(int32_t uc) {
|
||||
return (uc == 9 || uc == 10 || uc == 12 || uc == 13 || uc == 32 ||
|
||||
uc == 160 || uc == 5760 || (uc >= 8192 && uc <= 8202) || uc == 8239 ||
|
||||
uc == 8287 || uc == 12288);
|
||||
}
|
||||
|
||||
// matches anything in the P[cdefios] classes.
|
||||
int cmark_utf8proc_is_punctuation(int32_t uc) {
|
||||
return (
|
||||
(uc < 128 && cmark_ispunct((char)uc)) || uc == 161 || uc == 167 ||
|
||||
uc == 171 || uc == 182 || uc == 183 || uc == 187 || uc == 191 ||
|
||||
uc == 894 || uc == 903 || (uc >= 1370 && uc <= 1375) || uc == 1417 ||
|
||||
uc == 1418 || uc == 1470 || uc == 1472 || uc == 1475 || uc == 1478 ||
|
||||
uc == 1523 || uc == 1524 || uc == 1545 || uc == 1546 || uc == 1548 ||
|
||||
uc == 1549 || uc == 1563 || uc == 1566 || uc == 1567 ||
|
||||
(uc >= 1642 && uc <= 1645) || uc == 1748 || (uc >= 1792 && uc <= 1805) ||
|
||||
(uc >= 2039 && uc <= 2041) || (uc >= 2096 && uc <= 2110) || uc == 2142 ||
|
||||
uc == 2404 || uc == 2405 || uc == 2416 || uc == 2800 || uc == 3572 ||
|
||||
uc == 3663 || uc == 3674 || uc == 3675 || (uc >= 3844 && uc <= 3858) ||
|
||||
uc == 3860 || (uc >= 3898 && uc <= 3901) || uc == 3973 ||
|
||||
(uc >= 4048 && uc <= 4052) || uc == 4057 || uc == 4058 ||
|
||||
(uc >= 4170 && uc <= 4175) || uc == 4347 || (uc >= 4960 && uc <= 4968) ||
|
||||
uc == 5120 || uc == 5741 || uc == 5742 || uc == 5787 || uc == 5788 ||
|
||||
(uc >= 5867 && uc <= 5869) || uc == 5941 || uc == 5942 ||
|
||||
(uc >= 6100 && uc <= 6102) || (uc >= 6104 && uc <= 6106) ||
|
||||
(uc >= 6144 && uc <= 6154) || uc == 6468 || uc == 6469 || uc == 6686 ||
|
||||
uc == 6687 || (uc >= 6816 && uc <= 6822) || (uc >= 6824 && uc <= 6829) ||
|
||||
(uc >= 7002 && uc <= 7008) || (uc >= 7164 && uc <= 7167) ||
|
||||
(uc >= 7227 && uc <= 7231) || uc == 7294 || uc == 7295 ||
|
||||
(uc >= 7360 && uc <= 7367) || uc == 7379 || (uc >= 8208 && uc <= 8231) ||
|
||||
(uc >= 8240 && uc <= 8259) || (uc >= 8261 && uc <= 8273) ||
|
||||
(uc >= 8275 && uc <= 8286) || uc == 8317 || uc == 8318 || uc == 8333 ||
|
||||
uc == 8334 || (uc >= 8968 && uc <= 8971) || uc == 9001 || uc == 9002 ||
|
||||
(uc >= 10088 && uc <= 10101) || uc == 10181 || uc == 10182 ||
|
||||
(uc >= 10214 && uc <= 10223) || (uc >= 10627 && uc <= 10648) ||
|
||||
(uc >= 10712 && uc <= 10715) || uc == 10748 || uc == 10749 ||
|
||||
(uc >= 11513 && uc <= 11516) || uc == 11518 || uc == 11519 ||
|
||||
uc == 11632 || (uc >= 11776 && uc <= 11822) ||
|
||||
(uc >= 11824 && uc <= 11842) || (uc >= 12289 && uc <= 12291) ||
|
||||
(uc >= 12296 && uc <= 12305) || (uc >= 12308 && uc <= 12319) ||
|
||||
uc == 12336 || uc == 12349 || uc == 12448 || uc == 12539 || uc == 42238 ||
|
||||
uc == 42239 || (uc >= 42509 && uc <= 42511) || uc == 42611 ||
|
||||
uc == 42622 || (uc >= 42738 && uc <= 42743) ||
|
||||
(uc >= 43124 && uc <= 43127) || uc == 43214 || uc == 43215 ||
|
||||
(uc >= 43256 && uc <= 43258) || uc == 43310 || uc == 43311 ||
|
||||
uc == 43359 || (uc >= 43457 && uc <= 43469) || uc == 43486 ||
|
||||
uc == 43487 || (uc >= 43612 && uc <= 43615) || uc == 43742 ||
|
||||
uc == 43743 || uc == 43760 || uc == 43761 || uc == 44011 || uc == 64830 ||
|
||||
uc == 64831 || (uc >= 65040 && uc <= 65049) ||
|
||||
(uc >= 65072 && uc <= 65106) || (uc >= 65108 && uc <= 65121) ||
|
||||
uc == 65123 || uc == 65128 || uc == 65130 || uc == 65131 ||
|
||||
(uc >= 65281 && uc <= 65283) || (uc >= 65285 && uc <= 65290) ||
|
||||
(uc >= 65292 && uc <= 65295) || uc == 65306 || uc == 65307 ||
|
||||
uc == 65311 || uc == 65312 || (uc >= 65339 && uc <= 65341) ||
|
||||
uc == 65343 || uc == 65371 || uc == 65373 ||
|
||||
(uc >= 65375 && uc <= 65381) || (uc >= 65792 && uc <= 65794) ||
|
||||
uc == 66463 || uc == 66512 || uc == 66927 || uc == 67671 || uc == 67871 ||
|
||||
uc == 67903 || (uc >= 68176 && uc <= 68184) || uc == 68223 ||
|
||||
(uc >= 68336 && uc <= 68342) || (uc >= 68409 && uc <= 68415) ||
|
||||
(uc >= 68505 && uc <= 68508) || (uc >= 69703 && uc <= 69709) ||
|
||||
uc == 69819 || uc == 69820 || (uc >= 69822 && uc <= 69825) ||
|
||||
(uc >= 69952 && uc <= 69955) || uc == 70004 || uc == 70005 ||
|
||||
(uc >= 70085 && uc <= 70088) || uc == 70093 ||
|
||||
(uc >= 70200 && uc <= 70205) || uc == 70854 ||
|
||||
(uc >= 71105 && uc <= 71113) || (uc >= 71233 && uc <= 71235) ||
|
||||
(uc >= 74864 && uc <= 74868) || uc == 92782 || uc == 92783 ||
|
||||
uc == 92917 || (uc >= 92983 && uc <= 92987) || uc == 92996 ||
|
||||
uc == 113823);
|
||||
}
|
24
deps/cmark/src/utf8.h
vendored
Normal file
24
deps/cmark/src/utf8.h
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
#ifndef CMARK_UTF8_H
|
||||
#define CMARK_UTF8_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "buffer.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void cmark_utf8proc_case_fold(cmark_strbuf *dest, const uint8_t *str,
|
||||
bufsize_t len);
|
||||
void cmark_utf8proc_encode_char(int32_t uc, cmark_strbuf *buf);
|
||||
int cmark_utf8proc_iterate(const uint8_t *str, bufsize_t str_len, int32_t *dst);
|
||||
void cmark_utf8proc_check(cmark_strbuf *dest, const uint8_t *line,
|
||||
bufsize_t size);
|
||||
int cmark_utf8proc_is_space(int32_t uc);
|
||||
int cmark_utf8proc_is_punctuation(int32_t uc);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
229
deps/cmark/src/xml.c
vendored
Normal file
229
deps/cmark/src/xml.c
vendored
Normal file
@ -0,0 +1,229 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "cmark.h"
|
||||
#include "node.h"
|
||||
#include "buffer.h"
|
||||
|
||||
#define BUFFER_SIZE 100
|
||||
#define MAX_INDENT 40
|
||||
|
||||
// Functions to convert cmark_nodes to XML strings.
|
||||
|
||||
// C0 control characters, U+FFFE and U+FFF aren't allowed in XML.
|
||||
static const char XML_ESCAPE_TABLE[256] = {
|
||||
/* 0x00 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1,
|
||||
/* 0x10 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
/* 0x20 */ 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* 0x30 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 5, 0,
|
||||
/* 0x40 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* 0x50 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* 0x60 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* 0x70 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* 0x80 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* 0x90 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* 0xA0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* 0xB0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9,
|
||||
/* 0xC0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* 0xD0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* 0xE0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* 0xF0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
};
|
||||
|
||||
// U+FFFD Replacement Character encoded in UTF-8
|
||||
#define UTF8_REPL "\xEF\xBF\xBD"
|
||||
|
||||
static const char *XML_ESCAPES[] = {
|
||||
"", UTF8_REPL, """, "&", "<", ">"
|
||||
};
|
||||
|
||||
static void escape_xml(cmark_strbuf *ob, const unsigned char *src,
|
||||
bufsize_t size) {
|
||||
bufsize_t i = 0, org, esc = 0;
|
||||
|
||||
while (i < size) {
|
||||
org = i;
|
||||
while (i < size && (esc = XML_ESCAPE_TABLE[src[i]]) == 0)
|
||||
i++;
|
||||
|
||||
if (i > org)
|
||||
cmark_strbuf_put(ob, src + org, i - org);
|
||||
|
||||
if (i >= size)
|
||||
break;
|
||||
|
||||
if (esc == 9) {
|
||||
// To replace U+FFFE and U+FFFF with U+FFFD, only the last byte has to
|
||||
// be changed.
|
||||
// We know that src[i] is 0xBE or 0xBF.
|
||||
if (i >= 2 && src[i-2] == 0xEF && src[i-1] == 0xBF) {
|
||||
cmark_strbuf_putc(ob, 0xBD);
|
||||
} else {
|
||||
cmark_strbuf_putc(ob, src[i]);
|
||||
}
|
||||
} else {
|
||||
cmark_strbuf_puts(ob, XML_ESCAPES[esc]);
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
static void escape_xml_str(cmark_strbuf *dest, const unsigned char *source) {
|
||||
if (source)
|
||||
escape_xml(dest, source, strlen((char *)source));
|
||||
}
|
||||
|
||||
struct render_state {
|
||||
cmark_strbuf *xml;
|
||||
int indent;
|
||||
};
|
||||
|
||||
static CMARK_INLINE void indent(struct render_state *state) {
|
||||
int i;
|
||||
for (i = 0; i < state->indent && i < MAX_INDENT; i++) {
|
||||
cmark_strbuf_putc(state->xml, ' ');
|
||||
}
|
||||
}
|
||||
|
||||
static int S_render_node(cmark_node *node, cmark_event_type ev_type,
|
||||
struct render_state *state, int options) {
|
||||
cmark_strbuf *xml = state->xml;
|
||||
bool literal = false;
|
||||
cmark_delim_type delim;
|
||||
bool entering = (ev_type == CMARK_EVENT_ENTER);
|
||||
char buffer[BUFFER_SIZE];
|
||||
|
||||
if (entering) {
|
||||
indent(state);
|
||||
cmark_strbuf_putc(xml, '<');
|
||||
cmark_strbuf_puts(xml, cmark_node_get_type_string(node));
|
||||
|
||||
if (options & CMARK_OPT_SOURCEPOS && node->start_line != 0) {
|
||||
snprintf(buffer, BUFFER_SIZE, " sourcepos=\"%d:%d-%d:%d\"",
|
||||
node->start_line, node->start_column, node->end_line,
|
||||
node->end_column);
|
||||
cmark_strbuf_puts(xml, buffer);
|
||||
}
|
||||
|
||||
literal = false;
|
||||
|
||||
switch (node->type) {
|
||||
case CMARK_NODE_DOCUMENT:
|
||||
cmark_strbuf_puts(xml, " xmlns=\"http://commonmark.org/xml/1.0\"");
|
||||
break;
|
||||
case CMARK_NODE_TEXT:
|
||||
case CMARK_NODE_CODE:
|
||||
case CMARK_NODE_HTML_BLOCK:
|
||||
case CMARK_NODE_HTML_INLINE:
|
||||
cmark_strbuf_puts(xml, " xml:space=\"preserve\">");
|
||||
escape_xml(xml, node->data, node->len);
|
||||
cmark_strbuf_puts(xml, "</");
|
||||
cmark_strbuf_puts(xml, cmark_node_get_type_string(node));
|
||||
literal = true;
|
||||
break;
|
||||
case CMARK_NODE_LIST:
|
||||
switch (cmark_node_get_list_type(node)) {
|
||||
case CMARK_ORDERED_LIST:
|
||||
cmark_strbuf_puts(xml, " type=\"ordered\"");
|
||||
snprintf(buffer, BUFFER_SIZE, " start=\"%d\"",
|
||||
cmark_node_get_list_start(node));
|
||||
cmark_strbuf_puts(xml, buffer);
|
||||
delim = cmark_node_get_list_delim(node);
|
||||
if (delim == CMARK_PAREN_DELIM) {
|
||||
cmark_strbuf_puts(xml, " delim=\"paren\"");
|
||||
} else if (delim == CMARK_PERIOD_DELIM) {
|
||||
cmark_strbuf_puts(xml, " delim=\"period\"");
|
||||
}
|
||||
break;
|
||||
case CMARK_BULLET_LIST:
|
||||
cmark_strbuf_puts(xml, " type=\"bullet\"");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
snprintf(buffer, BUFFER_SIZE, " tight=\"%s\"",
|
||||
(cmark_node_get_list_tight(node) ? "true" : "false"));
|
||||
cmark_strbuf_puts(xml, buffer);
|
||||
break;
|
||||
case CMARK_NODE_HEADING:
|
||||
snprintf(buffer, BUFFER_SIZE, " level=\"%d\"", node->as.heading.level);
|
||||
cmark_strbuf_puts(xml, buffer);
|
||||
break;
|
||||
case CMARK_NODE_CODE_BLOCK:
|
||||
if (node->as.code.info) {
|
||||
cmark_strbuf_puts(xml, " info=\"");
|
||||
escape_xml_str(xml, node->as.code.info);
|
||||
cmark_strbuf_putc(xml, '"');
|
||||
}
|
||||
cmark_strbuf_puts(xml, " xml:space=\"preserve\">");
|
||||
escape_xml(xml, node->data, node->len);
|
||||
cmark_strbuf_puts(xml, "</");
|
||||
cmark_strbuf_puts(xml, cmark_node_get_type_string(node));
|
||||
literal = true;
|
||||
break;
|
||||
case CMARK_NODE_CUSTOM_BLOCK:
|
||||
case CMARK_NODE_CUSTOM_INLINE:
|
||||
cmark_strbuf_puts(xml, " on_enter=\"");
|
||||
escape_xml_str(xml, node->as.custom.on_enter);
|
||||
cmark_strbuf_putc(xml, '"');
|
||||
cmark_strbuf_puts(xml, " on_exit=\"");
|
||||
escape_xml_str(xml, node->as.custom.on_exit);
|
||||
cmark_strbuf_putc(xml, '"');
|
||||
break;
|
||||
case CMARK_NODE_LINK:
|
||||
case CMARK_NODE_IMAGE:
|
||||
cmark_strbuf_puts(xml, " destination=\"");
|
||||
escape_xml_str(xml, node->as.link.url);
|
||||
cmark_strbuf_putc(xml, '"');
|
||||
if (node->as.link.title) {
|
||||
cmark_strbuf_puts(xml, " title=\"");
|
||||
escape_xml_str(xml, node->as.link.title);
|
||||
cmark_strbuf_putc(xml, '"');
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (node->first_child) {
|
||||
state->indent += 2;
|
||||
} else if (!literal) {
|
||||
cmark_strbuf_puts(xml, " /");
|
||||
}
|
||||
cmark_strbuf_puts(xml, ">\n");
|
||||
|
||||
} else if (node->first_child) {
|
||||
state->indent -= 2;
|
||||
indent(state);
|
||||
cmark_strbuf_puts(xml, "</");
|
||||
cmark_strbuf_puts(xml, cmark_node_get_type_string(node));
|
||||
cmark_strbuf_puts(xml, ">\n");
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
char *cmark_render_xml(cmark_node *root, int options) {
|
||||
char *result;
|
||||
cmark_strbuf xml = CMARK_BUF_INIT(root->mem);
|
||||
cmark_event_type ev_type;
|
||||
cmark_node *cur;
|
||||
struct render_state state = {&xml, 0};
|
||||
|
||||
cmark_iter *iter = cmark_iter_new(root);
|
||||
|
||||
cmark_strbuf_puts(state.xml, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
|
||||
cmark_strbuf_puts(state.xml,
|
||||
"<!DOCTYPE document SYSTEM \"CommonMark.dtd\">\n");
|
||||
while ((ev_type = cmark_iter_next(iter)) != CMARK_EVENT_DONE) {
|
||||
cur = cmark_iter_get_node(iter);
|
||||
S_render_node(cur, ev_type, &state, options);
|
||||
}
|
||||
result = (char *)cmark_strbuf_detach(&xml);
|
||||
|
||||
cmark_iter_free(iter);
|
||||
return result;
|
||||
}
|
80
deps/cmark/test/CMakeLists.txt
vendored
Executable file
80
deps/cmark/test/CMakeLists.txt
vendored
Executable file
@ -0,0 +1,80 @@
|
||||
# To get verbose output: cmake --build build --target "test" -- ARGS='-V'
|
||||
|
||||
# By default, we run the spec tests only if python3 is available.
|
||||
# To require the spec tests, compile with -DSPEC_TESTS=1
|
||||
|
||||
if (SPEC_TESTS)
|
||||
find_package(PythonInterp 3 REQUIRED)
|
||||
else(SPEC_TESTS)
|
||||
find_package(PythonInterp 3)
|
||||
endif(SPEC_TESTS)
|
||||
|
||||
if (CMARK_SHARED OR CMARK_STATIC)
|
||||
add_test(NAME api_test COMMAND api_test)
|
||||
endif()
|
||||
|
||||
if (WIN32)
|
||||
file(TO_NATIVE_PATH ${CMAKE_BINARY_DIR}/src WIN_DLL_DIR)
|
||||
set(NEWPATH "${WIN_DLL_DIR};$ENV{PATH}")
|
||||
string(REPLACE ";" "\\;" NEWPATH "${NEWPATH}")
|
||||
set_tests_properties(api_test PROPERTIES ENVIRONMENT "PATH=${NEWPATH}")
|
||||
set(ROUNDTRIP "${CMAKE_CURRENT_SOURCE_DIR}/roundtrip.bat")
|
||||
else(WIN32)
|
||||
set(ROUNDTRIP "${CMAKE_CURRENT_SOURCE_DIR}/roundtrip.sh")
|
||||
endif(WIN32)
|
||||
|
||||
IF (PYTHONINTERP_FOUND)
|
||||
|
||||
add_test(html_normalization
|
||||
${PYTHON_EXECUTABLE} "-m" "doctest"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/normalize.py"
|
||||
)
|
||||
|
||||
if (CMARK_SHARED)
|
||||
add_test(spectest_library
|
||||
${PYTHON_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/spec_tests.py" "--no-normalize" "--spec"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/spec.txt" "--library-dir" "${CMAKE_CURRENT_BINARY_DIR}/../src"
|
||||
)
|
||||
|
||||
add_test(pathological_tests_library
|
||||
${PYTHON_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/pathological_tests.py"
|
||||
"--library-dir" "${CMAKE_CURRENT_BINARY_DIR}/../src"
|
||||
)
|
||||
|
||||
add_test(roundtriptest_library
|
||||
${PYTHON_EXECUTABLE}
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/roundtrip_tests.py"
|
||||
"--spec" "${CMAKE_CURRENT_SOURCE_DIR}/spec.txt"
|
||||
"--library-dir" "${CMAKE_CURRENT_BINARY_DIR}/../src"
|
||||
)
|
||||
|
||||
add_test(entity_library
|
||||
${PYTHON_EXECUTABLE}
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/entity_tests.py"
|
||||
"--library-dir" "${CMAKE_CURRENT_BINARY_DIR}/../src"
|
||||
)
|
||||
endif()
|
||||
|
||||
add_test(spectest_executable
|
||||
${PYTHON_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/spec_tests.py" "--no-normalize" "--spec" "${CMAKE_CURRENT_SOURCE_DIR}/spec.txt" "--program" "${CMAKE_CURRENT_BINARY_DIR}/../src/cmark"
|
||||
)
|
||||
|
||||
add_test(smartpuncttest_executable
|
||||
${PYTHON_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/spec_tests.py" "--no-normalize" "--spec" "${CMAKE_CURRENT_SOURCE_DIR}/smart_punct.txt" "--program" "${CMAKE_CURRENT_BINARY_DIR}/../src/cmark --smart"
|
||||
)
|
||||
|
||||
add_test(regressiontest_executable
|
||||
${PYTHON_EXECUTABLE}
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/spec_tests.py" "--no-normalize" "--spec"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/regression.txt" "--program"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/../src/cmark"
|
||||
)
|
||||
|
||||
ELSE(PYTHONINTERP_FOUND)
|
||||
|
||||
message("\n*** A python 3 interpreter is required to run the spec tests.\n")
|
||||
add_test(skipping_spectests
|
||||
echo "Skipping spec tests, because no python 3 interpreter is available.")
|
||||
|
||||
ENDIF(PYTHONINTERP_FOUND)
|
||||
|
36
deps/cmark/test/afl_test_cases/test.md
vendored
Normal file
36
deps/cmark/test/afl_test_cases/test.md
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
# H1
|
||||
|
||||
H2
|
||||
--
|
||||
|
||||
t ☺
|
||||
*b* **em** `c`
|
||||
≥\&\
|
||||
\_e\_
|
||||
|
||||
4) I1
|
||||
|
||||
5) I2
|
||||
> [l](/u "t")
|
||||
>
|
||||
> - [f]
|
||||
> - 
|
||||
>
|
||||
>> <ftp://hh>
|
||||
>> <u@hh>
|
||||
|
||||
~~~ l☺
|
||||
cb
|
||||
~~~
|
||||
|
||||
c1
|
||||
c2
|
||||
|
||||
***
|
||||
|
||||
<div>
|
||||
<b>x</b>
|
||||
</div>
|
||||
|
||||
[f]: /u "t"
|
||||
|
33
deps/cmark/test/cmark-fuzz.c
vendored
Normal file
33
deps/cmark/test/cmark-fuzz.c
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "cmark.h"
|
||||
|
||||
int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
|
||||
struct __attribute__((packed)) {
|
||||
int options;
|
||||
int width;
|
||||
} fuzz_config;
|
||||
|
||||
if (size >= sizeof(fuzz_config)) {
|
||||
/* The beginning of `data` is treated as fuzzer configuration */
|
||||
memcpy(&fuzz_config, data, sizeof(fuzz_config));
|
||||
|
||||
/* Mask off valid option bits */
|
||||
fuzz_config.options &= (CMARK_OPT_SOURCEPOS | CMARK_OPT_HARDBREAKS | CMARK_OPT_UNSAFE | CMARK_OPT_NOBREAKS | CMARK_OPT_NORMALIZE | CMARK_OPT_VALIDATE_UTF8 | CMARK_OPT_SMART);
|
||||
|
||||
/* Remainder of input is the markdown */
|
||||
const char *markdown = (const char *)(data + sizeof(fuzz_config));
|
||||
const size_t markdown_size = size - sizeof(fuzz_config);
|
||||
cmark_node *doc = cmark_parse_document(markdown, markdown_size, fuzz_config.options);
|
||||
|
||||
free(cmark_render_commonmark(doc, fuzz_config.options, fuzz_config.width));
|
||||
free(cmark_render_html(doc, fuzz_config.options));
|
||||
free(cmark_render_latex(doc, fuzz_config.options, fuzz_config.width));
|
||||
free(cmark_render_man(doc, fuzz_config.options, fuzz_config.width));
|
||||
free(cmark_render_xml(doc, fuzz_config.options));
|
||||
|
||||
cmark_node_free(doc);
|
||||
}
|
||||
return 0;
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user