├── .github └── workflows │ ├── ci-build.yml │ └── ci-fuzz.yml ├── .gitignore ├── CHANGELOG.md ├── CMakeLists.txt ├── LICENSE.md ├── README.md ├── md2html ├── CMakeLists.txt ├── cmdline.c ├── cmdline.h ├── md2html.1 └── md2html.c ├── scripts ├── build_entity_map.py ├── build_folding_map.py ├── build_punct_map.py ├── build_whitespace_map.py ├── coverity.sh ├── run-tests.py └── unicode │ ├── CaseFolding.txt │ └── DerivedGeneralCategory.txt ├── src ├── CMakeLists.txt ├── entity.c ├── entity.h ├── md4c-html.c ├── md4c-html.h ├── md4c-html.pc.in ├── md4c.c ├── md4c.h └── md4c.pc.in └── test ├── LICENSE.md ├── coverage.txt ├── fuzzers ├── fuzz-mdhtml.c └── seed-corpus │ ├── commonmark.md │ ├── gfm.md │ ├── latex-math.md │ └── wiki.md ├── normalize.py ├── pathological-tests.py ├── prog.py ├── regressions.txt ├── run-testsuite.py ├── spec-hard-soft-breaks.txt ├── spec-latex-math.txt ├── spec-permissive-autolinks.txt ├── spec-strikethrough.txt ├── spec-tables.txt ├── spec-tasklists.txt ├── spec-underline.txt ├── spec-wiki-links.txt └── spec.txt /.github/workflows/ci-build.yml: -------------------------------------------------------------------------------- 1 | name: Build and Test 2 | 3 | on: 4 | - pull_request 5 | - push 6 | 7 | jobs: 8 | # Linux builds. 9 | # 10 | # gcc sometimes warns (e.g. about potentially uninitialized variables) only 11 | # when some optimizations are enabled. So we build Debug as well as Release 12 | # on Linux. The Debug build also collects and uploads test coverage. 13 | linux-debug: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: Checkout 17 | uses: actions/checkout@v4 18 | - name: Configure 19 | run: CFLAGS='--coverage -Werror' cmake -DCMAKE_BUILD_TYPE=Debug -G 'Unix Makefiles' . 20 | - name: Build 21 | run: make VERBOSE=1 22 | - name: Test 23 | run: python3 ./scripts/run-tests.py 24 | - name: Create coverage report 25 | run: | 26 | sudo apt-get install -y lcov 27 | lcov --directory . --capture --output-file coverage.info 28 | lcov --remove coverage.info '/usr/*' --output-file coverage.info 29 | lcov --list coverage.info 30 | - name: Upload coverage report 31 | uses: codecov/codecov-action@v4 32 | env: 33 | CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} 34 | 35 | linux-release: 36 | runs-on: ubuntu-latest 37 | steps: 38 | - name: Checkout 39 | uses: actions/checkout@v4 40 | - name: Configure 41 | run: CFLAGS='--coverage -Werror' cmake -DCMAKE_BUILD_TYPE=Release -G 'Unix Makefiles' . 42 | - name: Build 43 | run: make VERBOSE=1 44 | - name: Test 45 | run: python3 ./scripts/run-tests.py 46 | 47 | # Windows builds. 48 | # 49 | # We do both 32 and 64-bit builds. Also note 32-bit does Debug build while 50 | # 64-bit one does Release build. (Full matrix would likely be an overkill.) 51 | windows-32-debug: 52 | runs-on: windows-latest 53 | steps: 54 | - name: Checkout 55 | uses: actions/checkout@v4 56 | - name: Dev command prompt 57 | uses: ilammy/msvc-dev-cmd@v1 58 | with: 59 | arch: x86 60 | - name: Configure 61 | run: cmake -DCMAKE_BUILD_TYPE=Debug -G "NMake Makefiles" . 62 | - name: Build 63 | run: nmake 64 | - name: Test 65 | run: python .\scripts\run-tests.py 66 | 67 | windows-64-release: 68 | runs-on: windows-latest 69 | steps: 70 | - name: Checkout 71 | uses: actions/checkout@v4 72 | - name: Dev command prompt 73 | uses: ilammy/msvc-dev-cmd@v1 74 | with: 75 | arch: x64 76 | - name: Configure 77 | run: cmake -DCMAKE_BUILD_TYPE=Release -G "NMake Makefiles" . 78 | - name: Build 79 | run: nmake 80 | - name: Test 81 | run: python .\scripts\run-tests.py 82 | -------------------------------------------------------------------------------- /.github/workflows/ci-fuzz.yml: -------------------------------------------------------------------------------- 1 | name: Fuzz Test 2 | on: 3 | pull_request: 4 | paths: 5 | - '**.c' 6 | - '**.h' 7 | jobs: 8 | Fuzzing: 9 | runs-on: ubuntu-latest 10 | permissions: 11 | security-events: write 12 | steps: 13 | - name: Build Fuzzers 14 | id: build 15 | uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master 16 | with: 17 | oss-fuzz-project-name: 'md4c' 18 | language: c 19 | - name: Run Fuzzers 20 | uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master 21 | with: 22 | oss-fuzz-project-name: 'md4c' 23 | fuzz-seconds: 600 24 | output-sarif: true 25 | - name: Upload Crash 26 | uses: actions/upload-artifact@v3 27 | if: failure() && steps.build.outcome == 'success' 28 | with: 29 | name: artifacts 30 | path: ./out/artifacts 31 | - name: Upload Sarif 32 | if: always() && steps.build.outcome == 'success' 33 | uses: github/codeql-action/upload-sarif@v2 34 | with: 35 | # Path to SARIF file relative to the root of the repository 36 | sarif_file: cifuzz-sarif/results.sarif 37 | checkout_path: cifuzz-sarif 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Object files 5 | *.o 6 | *.ko 7 | *.obj 8 | *.elf 9 | 10 | # Linker output 11 | *.ilk 12 | *.map 13 | *.exp 14 | 15 | # Precompiled Headers 16 | *.gch 17 | *.pch 18 | 19 | # Libraries 20 | *.lib 21 | *.a 22 | *.la 23 | *.lo 24 | 25 | # Shared objects (inc. Windows DLLs) 26 | *.dll 27 | *.so 28 | *.so.* 29 | *.dylib 30 | 31 | # Executables 32 | *.exe 33 | *.out 34 | *.app 35 | *.i*86 36 | *.x86_64 37 | *.hex 38 | pfft 39 | 40 | # Debug files 41 | *.dSYM/ 42 | *.su 43 | *.idb 44 | *.pdb 45 | 46 | # Kernel Module Compile Results 47 | *.mod* 48 | *.cmd 49 | .tmp_versions/ 50 | modules.order 51 | Module.symvers 52 | Mkfile.old 53 | dkms.conf 54 | 55 | # Temp files 56 | *.swp 57 | *.tmp 58 | *~ 59 | ~* 60 | *.e 61 | 62 | # autotools 63 | /aclocal.m4 64 | /autom4te.cache/ 65 | /build/ 66 | /build-aux/ 67 | conf*.dir/ 68 | /config.h 69 | /config.log 70 | /config.status 71 | /configure 72 | .deps/ 73 | .dirstamp 74 | Makefile 75 | Makefile.in 76 | stamp-* 77 | *.stamp 78 | *.stamp-* 79 | .Tpo 80 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 2 | # MD4C Change Log 3 | 4 | 5 | ## Next Version (Work in Progress) 6 | 7 | Fixes: 8 | 9 | - [#236](https://github.com/mity/md4c/issues/236): 10 | Fix quadratic time behavior caused by one-by-one walking over block lines 11 | instead of calling `md_lookup_line()`. 12 | 13 | - [#238](https://github.com/mity/md4c/issues/238): 14 | Fix quadratic time and output size behavior caused by malicious misuse of 15 | link reference definitions. 16 | 17 | - [#242](https://github.com/mity/md4c/issues/242): 18 | The strike-through extension (with flag `MD_FLAG_STRIKETHROUGH`) now follows 19 | same logic as other emphasis spans in respect to punctuation character and 20 | word boundaries. 21 | 22 | - [#248](https://github.com/mity/md4c/issues/248): 23 | Fix handling tab when removing trailing whitespace, especially in connection 24 | with ATX headers. 25 | 26 | 27 | ## Version 0.5.2 28 | 29 | Changes: 30 | 31 | * Changes mandated by CommonMark specification 0.31: 32 | 33 | - The specification expands set of Unicode characters seen by Markdown 34 | parser as a punctuation. Namely all Unicode general categories P 35 | (punctuation) and S (symbols) are now seen as such. 36 | 37 | - The definition of HTML comment has been changed so that `` and 38 | `` are also recognized as HTML comments. 39 | 40 | - HTML tags recognized as HTML block starting condition of type 4 has been 41 | updated, namely a tag `` has been removed, whereas `` 42 | added. 43 | 44 | Refer to [CommonMark 0.31.2](https://spec.commonmark.org/0.31.2/) for full 45 | specification. 46 | 47 | Fixes: 48 | 49 | - [#230](https://github.com/mity/md4c/issues/230): 50 | The fix [#223](https://github.com/mity/md4c/issues/223) in 0.5.1 release 51 | was incomplete and one corner case remained unfixed. This is now addressed. 52 | 53 | - [#231](https://github.com/mity/md4c/issues/231): 54 | `md2html --full-html` now emits `` in the HTML header. 55 | 56 | 57 | ## Version 0.5.1 58 | 59 | Changes: 60 | 61 | * LaTeX math extension (`MD_FLAG_LATEXMATHSPANS`) now requires that opener 62 | mark is not immediately preceded with alpha-numeric character and similarly 63 | that closer mark is not immediately followed with alpha-numeric character. 64 | 65 | So for example `foo$ x + y = z $` is not recognized as LaTeX equation 66 | anymore because there is no space between `foo` and the opening `$`. 67 | 68 | * Table extension (`MD_FLAG_TABLES`) now recognizes only tables with no more 69 | than 128 columns. This limit has been imposed to prevent a pathological 70 | case of quadratic output size explosion which could be used as DoS attack 71 | vector. 72 | 73 | * We are now more strict with `MD_FLAG_PERMISSIVExxxAUTOLINKS` family of 74 | extensions with respect to non-alphanumeric characters, with the aim to 75 | mitigate false positive detections. 76 | 77 | Only relatively few selected non-alphanumeric are now allowed in permissive 78 | e-mail auto-links (`MD_FLAG_PERMISSIVEEMAILAUTOLINKS`): 79 | - `.`, `-`, `_`, `+` in user name part of e-mail address; and 80 | - `.`, `-`, `_` in host part of the e-mail address. 81 | 82 | Similarly for URL and e-mail auto-links (`MD_FLAG_PERMISSIVEURLAUTOLINKS` and 83 | `MD_FLAG_PERMISSIVEWWWAUTOLINKS`): 84 | - `.`, `-`, `_` in host part of the URL; 85 | - `/`, `.`, `-`, `_` in path part of the URL; 86 | - `&`, `.`, `-`, `+`, `_`, `=`, `(`, `)` in the query part of the URL 87 | (additionally, if present, `(` and `)` must form balanced pairs); and 88 | - `.`, `-`, `+`, `_` in the fragment part of the URL. 89 | 90 | Furthermore these characters (with some exceptions like where they serve as 91 | delimiter characters, e.g. `/` for paths) are generally accepted only when 92 | an alphanumeric character both precedes and follows them (i.e. these cannot 93 | be "stacked" together). 94 | 95 | Fixes: 96 | 97 | * Fix several bugs where we haven't properly respected already resolved spans 98 | of higher precedence level in handling of permissive auto-links extensions 99 | (family of `MD_FLAG_PERMISSIVExxxAUTOLINKS` flags), LaTeX math extension 100 | (`MD_FLAG_LATEXMATHSPANS`) and wiki-links extension (`MD_FLAG_WIKILINKS`) 101 | of the form `[[label|text]]` (with pipe `|`). In some complex cases this 102 | could lead to invalid internal parser state and memory corruption. 103 | 104 | Identified with [OSS-Fuzz](https://github.com/google/oss-fuzz). 105 | 106 | * [#222](https://github.com/mity/md4c/issues/222): 107 | Fix strike-through extension (`MD_FLAG_STRIKETHROUGH`) which did not respect 108 | same rules for pairing opener and closer marks as other emphasis spans. 109 | 110 | * [#223](https://github.com/mity/md4c/issues/223): 111 | Fix incorrect handling of new-line character just at the beginning and/or 112 | end of a code span where we were not following CommonMark specification 113 | requirements correctly. 114 | 115 | 116 | ## Version 0.5.0 117 | 118 | Changes: 119 | 120 | * Changes mandated by CommonMark specification 0.30. 121 | 122 | Actually there are only very minor changes to recognition of HTML blocks: 123 | 124 | - The tag ` 554 | 555 | baz 556 | . 557 | 564 |

baz

565 | ```````````````````````````````` 566 | 567 | 568 | ## [Issue 210](https://github.com/mity/md4c/issues/210) 569 | 570 | ```````````````````````````````` example 571 | ![outer ![inner](img_inner "inner title")](img_outer "outer title") 572 | . 573 |

outer inner

574 | ```````````````````````````````` 575 | 576 | 577 | ## [Issue 212](https://github.com/mity/md4c/issues/212) 578 | 579 | ```````````````````````````````` example 580 | x 581 | |- 582 | |[*x*]() 583 | . 584 | 585 | 586 | 587 | 588 | 589 | 590 | 591 | 592 | 593 | 594 | 595 |
x
x
596 | . 597 | --ftables 598 | ```````````````````````````````` 599 | 600 | 601 | ## [Issue 213](https://github.com/mity/md4c/issues/215) 602 | 603 | ```````````````````````````````` example 604 | x 605 | |- 606 | [| 607 | 608 | [[ ]][[![|]()]] 609 | . 610 | 611 | 612 | 613 | 614 | 615 | 616 | 617 | 618 | 619 | 620 | 621 |
x
[
622 |

|

623 | . 624 | --ftables --fwiki-links 625 | ```````````````````````````````` 626 | 627 | 628 | ## [Issue 215](https://github.com/mity/md4c/issues/215) 629 | 630 | ```````````````````````````````` example 631 | title 632 | --→ 633 | . 634 |

title

635 | ```````````````````````````````` 636 | 637 | 638 | ## [Issue 216](https://github.com/mity/md4c/issues/216) 639 | 640 | ```````````````````````````````` example 641 | x 642 | . 643 |

x

644 | ```````````````````````````````` 645 | 646 | 647 | ## [Issue 217](https://github.com/mity/md4c/issues/217) 648 | 649 | ```````````````````````````````` example 650 | __!_!__ 651 | 652 | __!x!__ 653 | 654 | **!*!** 655 | 656 | --- 657 | 658 | _*__*_* 659 | 660 | _*xx*_* 661 | 662 | _*__-_- 663 | 664 | _*xx-_- 665 | . 666 |

!_!

667 |

!x!

668 |

!*!

669 |
670 |

__*

671 |

xx*

672 |

*__--

673 |

*xx--

674 | ```````````````````````````````` 675 | 676 | 677 | ## [Issue 222](https://github.com/mity/md4c/issues/222) 678 | 679 | ```````````````````````````````` example 680 | ~foo ~bar baz~ 681 | . 682 |

~foo bar baz

683 | . 684 | --fstrikethrough 685 | ```````````````````````````````` 686 | 687 | 688 | ## [Issue 223](https://github.com/mity/md4c/issues/223) 689 | 690 | If from one side (and the other has no space/newline), replace new line with 691 | space. 692 | 693 | ```````````````````````````````` example [no-normalize] 694 | ` 695 | foo` 696 | . 697 |

foo

698 | ```````````````````````````````` 699 | 700 | ```````````````````````````````` example [no-normalize] 701 | `foo 702 | ` 703 | . 704 |

foo

705 | ```````````````````````````````` 706 | 707 | If from both side, eat it. 708 | 709 | ```````````````````````````````` example [no-normalize] 710 | ` 711 | foo 712 | ` 713 | . 714 |

foo

715 | ```````````````````````````````` 716 | 717 | 718 | ## [Issue 226](https://github.com/mity/md4c/issues/226) 719 | 720 | ```````````````````````````````` example 721 | https://example.com/ 722 | https://example.com/dir/ 723 | . 724 |

https://example.com/ 725 | https://example.com/dir/

726 | . 727 | --fpermissive-url-autolinks 728 | ```````````````````````````````` 729 | 730 | 731 | ## [Issue 242](https://github.com/mity/md4c/issues/242) 732 | 733 | ```````````````````````````````` example 734 | copy ~user1/file to ~user2/file 735 | 736 | copy "~user1/file" to "~user2/file" 737 | . 738 |

copy ~user1/file to ~user2/file

739 |

copy "~user1/file" to "~user2/file"

740 | . 741 | --fstrikethrough 742 | ```````````````````````````````` 743 | 744 | 745 | ## [Issue 248](https://github.com/mity/md4c/issues/248) 746 | 747 | (These are in spec.txt, but we need the [no-normalize] flag in order to 748 | catch the whitespace issues.) 749 | 750 | ```````````````````````````````` example [no-normalize] 751 | #→Foo 752 | . 753 |

Foo

754 | ```````````````````````````````` 755 | 756 | ```````````````````````````````` example [no-normalize] 757 | Foo *bar 758 | baz*→ 759 | ==== 760 | . 761 |

Foo bar 762 | baz

763 | ```````````````````````````````` 764 | 765 | 766 | ## [Issue 250](https://github.com/mity/md4c/issues/250) 767 | 768 | Handling trailing tabulator character versus hard break. 769 | 770 | Space + space + tab + newline is not hard break: 771 | ```````````````````````````````` example [no-normalize] 772 | foo → 773 | bar 774 | . 775 |

foo 776 | bar

777 | ```````````````````````````````` 778 | 779 | Tab + space + space + newline is hard break: 780 | ```````````````````````````````` example [no-normalize] 781 | foo→ 782 | bar 783 | . 784 |

foo
785 | bar

786 | ```````````````````````````````` 787 | 788 | -------------------------------------------------------------------------------- /test/run-testsuite.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | import sys 5 | from difflib import unified_diff 6 | import argparse 7 | import re 8 | import json 9 | from prog import Prog 10 | from normalize import normalize_html 11 | 12 | if __name__ == "__main__": 13 | parser = argparse.ArgumentParser(description='Run Markdown tests.') 14 | parser.add_argument('-p', '--program', dest='program', nargs='?', default=None, 15 | help='program to test') 16 | parser.add_argument('-s', '--spec', dest='spec', nargs='?', default='spec.txt', 17 | help='path to spec') 18 | parser.add_argument('-P', '--pattern', dest='pattern', nargs='?', 19 | default=None, help='limit to sections matching regex pattern') 20 | parser.add_argument('--no-normalize', dest='normalize', 21 | action='store_const', const=False, default=True, 22 | help='do not normalize HTML') 23 | parser.add_argument('-d', '--dump-tests', dest='dump_tests', 24 | action='store_const', const=True, default=False, 25 | help='dump tests in JSON format') 26 | parser.add_argument('--debug-normalization', dest='debug_normalization', 27 | action='store_const', const=True, 28 | default=False, help='filter stdin through normalizer for testing') 29 | parser.add_argument('-n', '--number', type=int, default=None, 30 | help='only consider the test with the given number') 31 | args = parser.parse_args(sys.argv[1:]) 32 | 33 | def out(str): 34 | sys.stdout.buffer.write(str.encode('utf-8')) 35 | 36 | def print_test_header(headertext, example_number, start_line, end_line): 37 | out("Example %d (lines %d-%d) %s\n" % (example_number,start_line,end_line,headertext)) 38 | 39 | def do_test(test, normalize, result_counts): 40 | prog = Prog(cmdline=args.program, default_options=test['cmdline_options']) 41 | [retcode, actual_html, err] = prog.to_html(test['markdown']) 42 | if retcode == 0: 43 | expected_html = test['html'] 44 | unicode_error = None 45 | if normalize and not test['no_normalize']: 46 | try: 47 | passed = normalize_html(actual_html) == normalize_html(expected_html) 48 | except UnicodeDecodeError as e: 49 | unicode_error = e 50 | passed = False 51 | else: 52 | passed = actual_html == expected_html 53 | if passed: 54 | result_counts['pass'] += 1 55 | else: 56 | print_test_header(test['section'], test['example'], test['start_line'], test['end_line']) 57 | out(test['markdown'] + '\n') 58 | if unicode_error: 59 | out("Unicode error: " + str(unicode_error) + '\n') 60 | out("Expected: " + repr(expected_html) + '\n') 61 | out("Got: " + repr(actual_html) + '\n') 62 | else: 63 | expected_html_lines = expected_html.splitlines(True) 64 | actual_html_lines = actual_html.splitlines(True) 65 | for diffline in unified_diff(expected_html_lines, actual_html_lines, 66 | "expected HTML", "actual HTML"): 67 | out(diffline) 68 | out('\n') 69 | result_counts['fail'] += 1 70 | else: 71 | print_test_header(test['section'], test['example'], test['start_line'], test['end_line']) 72 | out("program returned error code %d\n" % retcode) 73 | sys.stdout.buffer.write(err) 74 | result_counts['error'] += 1 75 | 76 | def get_tests(specfile): 77 | line_number = 0 78 | start_line = 0 79 | end_line = 0 80 | example_number = 0 81 | markdown_lines = [] 82 | html_lines = [] 83 | cmdline_lines = [] 84 | state = 0 # 0 regular text, 1 markdown example, 2 html output 85 | headertext = '' 86 | tests = [] 87 | no_normalize = 0 88 | 89 | header_re = re.compile('#+ ') 90 | 91 | with open(specfile, 'r', encoding='utf-8', newline='\n') as specf: 92 | for line in specf: 93 | line_number = line_number + 1 94 | l = line.strip() 95 | if re.match("`{32} example( [a-z]{1,})?", l): 96 | state = 1 97 | if re.search("\\[no-normalize\\]", l): 98 | no_normalize = 1 99 | elif state >= 2 and l == "`" * 32: 100 | state = 0 101 | example_number = example_number + 1 102 | end_line = line_number 103 | tests.append({ 104 | "markdown":''.join(markdown_lines).replace('→',"\t"), 105 | "html":''.join(html_lines).replace('→',"\t"), 106 | "no_normalize": no_normalize, 107 | "cmdline_options":''.join(cmdline_lines), 108 | "example": example_number, 109 | "start_line": start_line, 110 | "end_line": end_line, 111 | "section": headertext,}) 112 | start_line = 0 113 | markdown_lines = [] 114 | html_lines = [] 115 | cmdline_lines = [] 116 | no_normalize = 0 117 | elif l == ".": 118 | state += 1 119 | elif state == 1: 120 | if start_line == 0: 121 | start_line = line_number - 1 122 | markdown_lines.append(line) 123 | elif state == 2: 124 | html_lines.append(line) 125 | elif state == 3: 126 | cmdline_lines.append(line) 127 | elif state == 0 and re.match(header_re, line): 128 | headertext = header_re.sub('', line).strip() 129 | return tests 130 | 131 | if __name__ == "__main__": 132 | if args.debug_normalization: 133 | out(normalize_html(sys.stdin.read())) 134 | exit(0) 135 | 136 | all_tests = get_tests(args.spec) 137 | if args.pattern: 138 | pattern_re = re.compile(args.pattern, re.IGNORECASE) 139 | else: 140 | pattern_re = re.compile('.') 141 | tests = [ test for test in all_tests if re.search(pattern_re, test['section']) and (not args.number or test['example'] == args.number) ] 142 | if args.dump_tests: 143 | out(json.dumps(tests, ensure_ascii=False, indent=2)) 144 | exit(0) 145 | else: 146 | skipped = len(all_tests) - len(tests) 147 | result_counts = {'pass': 0, 'fail': 0, 'error': 0, 'skip': skipped} 148 | for test in tests: 149 | do_test(test, args.normalize, result_counts) 150 | out("{pass} passed, {fail} failed, {error} errored, {skip} skipped\n".format(**result_counts)) 151 | exit(result_counts['fail'] + result_counts['error']) 152 | -------------------------------------------------------------------------------- /test/spec-hard-soft-breaks.txt: -------------------------------------------------------------------------------- 1 | 2 | # Hard Soft Breaks 3 | 4 | With the flag `MD_FLAG_HARD_SOFT_BREAKS`, MD4C treats all newline characters as 5 | hard breaks. 6 | 7 | ```````````````````````````````` example 8 | foo 9 | baz 10 | . 11 |

foo
12 | baz

13 | . 14 | --fhard-soft-breaks 15 | ```````````````````````````````` 16 | 17 | ```````````````````````````````` example 18 | A quote from the CommonMark Spec below: 19 | 20 | A renderer may also provide an option to 21 | render soft line breaks as hard line breaks. 22 | . 23 |

A quote from the CommonMark Spec below:

24 |

A renderer may also provide an option to
25 | render soft line breaks as hard line breaks.

26 | . 27 | --fhard-soft-breaks 28 | ```````````````````````````````` 29 | -------------------------------------------------------------------------------- /test/spec-latex-math.txt: -------------------------------------------------------------------------------- 1 | 2 | # LaTeX Math 3 | 4 | With the flag `MD_FLAG_LATEXMATHSPANS`, MD4C enables extension for recognition 5 | of LaTeX style math spans. 6 | 7 | A math span is is any text wrapped in dollars or double dollars (`$...$` or 8 | `$$...$$`). 9 | 10 | ```````````````````````````````` example 11 | $a+b=c$ Hello, world! 12 | . 13 |

a+b=c Hello, world!

14 | . 15 | --flatex-math 16 | ```````````````````````````````` 17 | 18 | However the LaTeX math spans cannot be nested: 19 | 20 | ```````````````````````````````` example 21 | $$foo $bar$ baz$$ 22 | . 23 |

$$foo bar baz$$

24 | . 25 | --flatex-math 26 | ```````````````````````````````` 27 | 28 | The opening delimiter cannot be preceded with an alphanumerical character: 29 | 30 | ```````````````````````````````` example 31 | x$a+b=c$ 32 | . 33 |

x$a+b=c$

34 | . 35 | --flatex-math 36 | ```````````````````````````````` 37 | 38 | Similarly the closing delimiter cannot be followed with an alphanumerical character: 39 | 40 | ```````````````````````````````` example 41 | $a+b=c$x 42 | . 43 |

$a+b=c$x

44 | . 45 | --flatex-math 46 | ```````````````````````````````` 47 | 48 | If the double dollar sign is used, the math span is a display math span. 49 | 50 | ```````````````````````````````` example 51 | This is a display equation: $$\int_a^b x dx$$. 52 | . 53 |

This is a display equation: \int_a^b x dx.

54 | . 55 | --flatex-math 56 | ```````````````````````````````` 57 | 58 | Math spans may span multiple lines as they are normal spans: 59 | 60 | ```````````````````````````````` example 61 | $$ 62 | \int_a^b 63 | f(x) dx 64 | $$ 65 | . 66 |

\int_a^b f(x) dx

67 | . 68 | --flatex-math 69 | ```````````````````````````````` 70 | 71 | Note though that many (simple) renderers may output the math spans just as a 72 | verbatim text. (This includes the HTML renderer used by the `md2html` utility.) 73 | 74 | Only advanced renderers which implement LaTeX math syntax can be expected to 75 | provide better results. 76 | -------------------------------------------------------------------------------- /test/spec-permissive-autolinks.txt: -------------------------------------------------------------------------------- 1 | 2 | # Permissive Autolinks 3 | 4 | Standard autolinks (as per CommonMark specification) have to be decorated with 5 | `<` and `>` so for example: 6 | 7 | ```````````````````````````````` example 8 | 9 | 10 | . 11 |

mailto:john.doe@gmail.com 12 | https://example.com

13 | ```````````````````````````````` 14 | 15 | With flags `MD_FLAG_PERMISSIVEURLAUTOLINKS`, `MD_FLAG_PERMISSIVEWWWAUTOLINKS` 16 | and `MD_FLAG_PERMISSIVEEMAILAUTOLINKS`, MD4C is able also to recognize autolinks 17 | without those marks. 18 | 19 | Example of permissive autolinks follows: 20 | 21 | ```````````````````````````````` example 22 | john.doe@gmail.com 23 | https://www.example.com 24 | www.example.com 25 | . 26 |

john.doe@gmail.com 27 | https://www.example.com 28 | www.example.com

29 | . 30 | --fpermissive-email-autolinks 31 | --fpermissive-url-autolinks 32 | --fpermissive-www-autolinks 33 | ```````````````````````````````` 34 | 35 | However as this syntax also brings some more danger of false positives, more 36 | strict rules apply to what characters may or may not form such autolinks. 37 | When a need arises to use a link which does not satisfy these restrictions, 38 | standard Markdown autolinks have to be used. 39 | 40 | First and formost, these autolinks have to be delimited from surrounded text, 41 | i.e. whitespace, beginning/end of line, or very limited punctuation must 42 | precede and follow respectively. 43 | 44 | Therefore these are not autolinks because `:` precedes or follows: 45 | 46 | ```````````````````````````````` example 47 | :john.doe@gmail.com 48 | :https://www.example.com 49 | :www.example.com 50 | . 51 |

:john.doe@gmail.com 52 | :https://www.example.com 53 | :www.example.com

54 | . 55 | --fpermissive-email-autolinks 56 | --fpermissive-url-autolinks 57 | --fpermissive-www-autolinks 58 | ```````````````````````````````` 59 | 60 | Allowed punctuation right before autolink includes only opening brackets `(`, 61 | `{` or `[`: 62 | 63 | ```````````````````````````````` example 64 | [john.doe@gmail.com 65 | (https://www.example.com 66 | {www.example.com 67 | . 68 |

[john.doe@gmail.com 69 | (https://www.example.com 70 | {www.example.com

71 | . 72 | --fpermissive-email-autolinks 73 | --fpermissive-url-autolinks 74 | --fpermissive-www-autolinks 75 | ```````````````````````````````` 76 | 77 | Correspondingly, the respective closing brackets may follow the autolinks. 78 | 79 | ```````````````````````````````` example 80 | john.doe@gmail.com] 81 | https://www.example.com) 82 | www.example.com} 83 | . 84 |

john.doe@gmail.com] 85 | https://www.example.com) 86 | www.example.com}

87 | . 88 | --fpermissive-email-autolinks 89 | --fpermissive-url-autolinks 90 | --fpermissive-www-autolinks 91 | ```````````````````````````````` 92 | 93 | Some other punctuation characters are also allowed after the autolink so that 94 | the autolinks may appear at the end of a sentence or clause (`.`, `!`, `?`, 95 | `,`, `;`): 96 | 97 | ```````````````````````````````` example 98 | Have you ever visited http://zombo.com? 99 | . 100 |

Have you ever visited http://zombo.com?

101 | . 102 | --fpermissive-url-autolinks 103 | ```````````````````````````````` 104 | 105 | Markdown emphasis mark can also precede (but only opening mark) or follow 106 | (only closer mark): 107 | 108 | ```````````````````````````````` example 109 | You may contact me at **john.doe@example.com**. 110 | . 111 |

You may contact me at john.doe@example.com.

112 | . 113 | --fpermissive-email-autolinks 114 | ```````````````````````````````` 115 | 116 | However the following is not, because in this example `*` is literal `*` and 117 | such punctuation is not allowed before the autolink: 118 | 119 | ```````````````````````````````` example 120 | *john.doe@example.com 121 | 122 | john.doe@example.com* 123 | . 124 |

*john.doe@example.com

125 |

john.doe@example.com*

126 | . 127 | --fpermissive-email-autolinks 128 | ```````````````````````````````` 129 | 130 | ## Permissive URL Autolinks 131 | 132 | Permissive URL autolinks (`MD_FLAG_PERMISSIVEURLAUTOLINKS`) are formed 133 | by mandatory URL scheme, mandatory host, optional path, optional query and 134 | optional fragment. 135 | 136 | The permissive URL autolinks recognize only `http://`, `https://` and `ftp://` 137 | as the scheme: 138 | 139 | ```````````````````````````````` example 140 | https://example.com 141 | http://example.com 142 | ftp://example.com 143 | 144 | ssh://example.com 145 | . 146 |

https://example.com 147 | http://example.com 148 | ftp://example.com

149 |

ssh://example.com

150 | . 151 | --fpermissive-url-autolinks 152 | ```````````````````````````````` 153 | 154 | The host is a sequence made of alphanumerical characters, `.`, `-` and `_`. 155 | It has to include at least two components delimited with `.`, last component 156 | has to have at least two characters, and occurrence of `.`, `-` and `_` has to 157 | be immediately preceded and followed with a letter or digit. 158 | 159 | The host specification may optionally be followed with path. Path begins with 160 | character `/` and uses it also for delimiting path components. Every path 161 | component is made of alhanumerical characters and `.`, `-`, `_`. Once again, 162 | any occurrence of `.`, `-`, `_` has to be surrounded with alphanumerical 163 | character. 164 | 165 | ```````````````````````````````` example 166 | https://example.com/images/branding/logo_272x92.png 167 | . 168 |

https://example.com/images/branding/logo_272x92.png

169 | . 170 | --fpermissive-url-autolinks 171 | ```````````````````````````````` 172 | 173 | Then optionally query may follow. The query is made of `?` and then with 174 | alhanumerical characters, `&`, `.`, `-`, `+`, `_`, `=`, `(` and `)`. Once again any 175 | of those non-alhanumerical characters has to be surrounded with alpha-numerical 176 | characters, and also brackets `(` have to be balanced `)`. 177 | 178 | ```````````````````````````````` example 179 | https://www.google.com/search?q=md4c+markdown 180 | . 181 |

https://www.google.com/search?q=md4c+markdown

182 | . 183 | --fpermissive-url-autolinks 184 | ```````````````````````````````` 185 | 186 | And finally there may be an optional fragment. 187 | 188 | ```````````````````````````````` example 189 | https://example.com#fragment 190 | . 191 |

https://example.com#fragment

192 | . 193 | --fpermissive-url-autolinks 194 | ```````````````````````````````` 195 | 196 | And finally one complex example: 197 | 198 | ```````````````````````````````` example 199 | http://commonmark.org 200 | 201 | (Visit https://encrypted.google.com/search?q=Markup+(business)) 202 | 203 | Anonymous FTP is available at ftp://foo.bar.baz. 204 | . 205 |

http://commonmark.org

206 |

(Visit https://encrypted.google.com/search?q=Markup+(business))

207 |

Anonymous FTP is available at ftp://foo.bar.baz.

208 | . 209 | --fpermissive-url-autolinks 210 | ```````````````````````````````` 211 | 212 | 213 | ## Permissive WWW Autolinks 214 | 215 | Permissive WWW autolinks (`MD_FLAG_PERMISSIVEWWWAUTOLINKS`) are very similar 216 | to the permissive URL autolinks. Actually the only difference is that instead 217 | of providing an explicit scheme, they have to begin with `www.`. 218 | 219 | ```````````````````````````````` example 220 | www.google.com/search?q=Markdown 221 | . 222 |

www.google.com/search?q=Markdown

223 | . 224 | --fpermissive-www-autolinks 225 | ```````````````````````````````` 226 | 227 | 228 | ## Permissive E-mail Autolinks 229 | 230 | Permissive E-mail autolinks (`MD_FLAG_PERMISSIVEEMAILAUTOLINKS`) impose the 231 | following limitations to the e-mail addresses: 232 | 233 | 1. The username (before the `@`) can only use alphanumerical characters and 234 | characters `.`, `-`, `_` and `+`. However every such non-alphanumerical 235 | character must be immediately preceded and followed by an alphanumerical 236 | character. 237 | 238 | For example this is not an auto-link because of that double underscore `__`. 239 | 240 | ```````````````````````````````` example 241 | john__doe@example.com 242 | . 243 |

john__doe@example.com

244 | . 245 | --fpermissive-email-autolinks 246 | ```````````````````````````````` 247 | 248 | 2. Same rules for domain as for URL and WWW autolinks apply. 249 | -------------------------------------------------------------------------------- /test/spec-strikethrough.txt: -------------------------------------------------------------------------------- 1 | 2 | # Strike-Through 3 | 4 | With the flag `MD_FLAG_STRIKETHROUGH`, MD4C enables extension for recognition 5 | of strike-through spans. 6 | 7 | Strike-through text is any text wrapped in one or two tildes (`~`). 8 | 9 | ```````````````````````````````` example 10 | ~Hi~ Hello, world! 11 | . 12 |

Hi Hello, world!

13 | . 14 | --fstrikethrough 15 | ```````````````````````````````` 16 | 17 | If the length of the opener and closer doesn't match, the strike-through is 18 | not recognized. 19 | 20 | ```````````````````````````````` example 21 | This ~text~~ is curious. 22 | . 23 |

This ~text~~ is curious.

24 | . 25 | --fstrikethrough 26 | ```````````````````````````````` 27 | 28 | Too long tilde sequence won't be recognized: 29 | 30 | ```````````````````````````````` example 31 | foo ~~~bar~~~ 32 | . 33 |

foo ~~~bar~~~

34 | . 35 | --fstrikethrough 36 | ```````````````````````````````` 37 | 38 | Also note the markers cannot open a strike-through span if they are followed 39 | with a whitespace; and similarly, then cannot close the span if they are 40 | preceded with a whitespace: 41 | 42 | ```````````````````````````````` example 43 | ~foo ~bar 44 | . 45 |

~foo ~bar

46 | . 47 | --fstrikethrough 48 | ```````````````````````````````` 49 | 50 | 51 | As with regular emphasis delimiters, a new paragraph will cause the cessation 52 | of parsing a strike-through: 53 | 54 | ```````````````````````````````` example 55 | This ~~has a 56 | 57 | new paragraph~~. 58 | . 59 |

This ~~has a

60 |

new paragraph~~.

61 | . 62 | --fstrikethrough 63 | ```````````````````````````````` 64 | -------------------------------------------------------------------------------- /test/spec-tables.txt: -------------------------------------------------------------------------------- 1 | 2 | # Tables 3 | 4 | With the flag `MD_FLAG_TABLES`, MD4C enables extension for recognition of 5 | tables. 6 | 7 | Basic table example of a table with two columns and three lines (when not 8 | counting the header) is as follows: 9 | 10 | ```````````````````````````````` example 11 | | Column 1 | Column 2 | 12 | |----------|----------| 13 | | foo | bar | 14 | | baz | qux | 15 | | quux | quuz | 16 | . 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 |
Column 1Column 2
foobar
bazqux
quuxquuz
27 | . 28 | --ftables 29 | ```````````````````````````````` 30 | 31 | The leading and succeeding pipe characters (`|`) on each line are optional: 32 | 33 | ```````````````````````````````` example 34 | Column 1 | Column 2 | 35 | ---------|--------- | 36 | foo | bar | 37 | baz | qux | 38 | quux | quuz | 39 | . 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 |
Column 1Column 2
foobar
bazqux
quuxquuz
50 | . 51 | --ftables 52 | ```````````````````````````````` 53 | 54 | ```````````````````````````````` example 55 | | Column 1 | Column 2 56 | |----------|--------- 57 | | foo | bar 58 | | baz | qux 59 | | quux | quuz 60 | . 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 |
Column 1Column 2
foobar
bazqux
quuxquuz
71 | . 72 | --ftables 73 | ```````````````````````````````` 74 | 75 | ```````````````````````````````` example 76 | Column 1 | Column 2 77 | ---------|--------- 78 | foo | bar 79 | baz | qux 80 | quux | quuz 81 | . 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 |
Column 1Column 2
foobar
bazqux
quuxquuz
92 | . 93 | --ftables 94 | ```````````````````````````````` 95 | 96 | However for one-column table, at least one pipe has to be used in the table 97 | header underline, otherwise it would be parsed as a Setext title followed by 98 | a paragraph. 99 | 100 | ```````````````````````````````` example 101 | Column 1 102 | -------- 103 | foo 104 | baz 105 | quux 106 | . 107 |

Column 1

108 |

foo 109 | baz 110 | quux

111 | . 112 | --ftables 113 | ```````````````````````````````` 114 | 115 | Leading and trailing whitespace in a table cell is ignored and the columns do 116 | not need to be aligned. 117 | 118 | ```````````````````````````````` example 119 | Column 1 |Column 2 120 | ---|--- 121 | foo | bar 122 | baz| qux 123 | quux|quuz 124 | . 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 |
Column 1Column 2
foobar
bazqux
quuxquuz
135 | . 136 | --ftables 137 | ```````````````````````````````` 138 | 139 | The table cannot interrupt a paragraph. 140 | 141 | ```````````````````````````````` example 142 | Lorem ipsum dolor sit amet. 143 | | Column 1 | Column 2 144 | | ---------|--------- 145 | | foo | bar 146 | | baz | qux 147 | | quux | quuz 148 | . 149 |

Lorem ipsum dolor sit amet. 150 | | Column 1 | Column 2 151 | | ---------|--------- 152 | | foo | bar 153 | | baz | qux 154 | | quux | quuz

155 | ```````````````````````````````` 156 | 157 | Similarly, paragraph cannot interrupt a table: 158 | 159 | ```````````````````````````````` example 160 | Column 1 | Column 2 161 | ---------|--------- 162 | foo | bar 163 | baz | qux 164 | quux | quuz 165 | Lorem ipsum dolor sit amet. 166 | . 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 |
Column 1Column 2
foobar
bazqux
quuxquuz
Lorem ipsum dolor sit amet.
178 | . 179 | --ftables 180 | ```````````````````````````````` 181 | 182 | The first, the last or both the first and the last dash in each column 183 | underline can be replaced with a colon (`:`) to request left, right or middle 184 | alignment of the respective column: 185 | 186 | ```````````````````````````````` example 187 | | Column 1 | Column 2 | Column 3 | Column 4 | 188 | |----------|:---------|:--------:|---------:| 189 | | default | left | center | right | 190 | . 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 |
Column 1Column 2Column 3Column 4
defaultleftcenterright
199 | . 200 | --ftables 201 | ```````````````````````````````` 202 | 203 | To include a literal pipe character in any cell, it has to be escaped. 204 | 205 | ```````````````````````````````` example 206 | Column 1 | Column 2 207 | ---------|--------- 208 | foo | bar 209 | baz | qux \| xyzzy 210 | quux | quuz 211 | . 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 |
Column 1Column 2
foobar
bazqux | xyzzy
quuxquuz
222 | . 223 | --ftables 224 | ```````````````````````````````` 225 | 226 | Contents of each cell is parsed as an inline text which may contents any 227 | inline Markdown spans like emphasis, strong emphasis, links etc. 228 | 229 | ```````````````````````````````` example 230 | Column 1 | Column 2 231 | ---------|--------- 232 | *foo* | bar 233 | **baz** | [qux] 234 | quux | [quuz](/url2) 235 | 236 | [qux]: /url 237 | . 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 |
Column 1Column 2
foobar
bazqux
quuxquuz
248 | . 249 | --ftables 250 | ```````````````````````````````` 251 | 252 | However pipes which are inside a code span are not recognized as cell 253 | boundaries. 254 | 255 | ```````````````````````````````` example 256 | Column 1 | Column 2 257 | ---------|--------- 258 | `foo | bar` 259 | baz | qux 260 | quux | quuz 261 | . 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 |
Column 1Column 2
foo | bar
bazqux
quuxquuz
272 | . 273 | --ftables 274 | ```````````````````````````````` 275 | -------------------------------------------------------------------------------- /test/spec-tasklists.txt: -------------------------------------------------------------------------------- 1 | 2 | # Tasklists 3 | 4 | With the flag `MD_FLAG_TASKLISTS`, MD4C enables extension for recognition of 5 | task lists. 6 | 7 | Basic task list may look as follows: 8 | 9 | ```````````````````````````````` example 10 | * [x] foo 11 | * [X] bar 12 | * [ ] baz 13 | . 14 |
    15 |
  • foo
  • 16 |
  • bar
  • 17 |
  • baz
  • 18 |
19 | . 20 | --ftasklists 21 | ```````````````````````````````` 22 | 23 | Task lists can also be in ordered lists: 24 | 25 | ```````````````````````````````` example 26 | 1. [x] foo 27 | 2. [X] bar 28 | 3. [ ] baz 29 | . 30 |
    31 |
  1. foo
  2. 32 |
  3. bar
  4. 33 |
  5. baz
  6. 34 |
35 | . 36 | --ftasklists 37 | ```````````````````````````````` 38 | 39 | Task lists can also be nested in ordinary lists: 40 | 41 | ```````````````````````````````` example 42 | * xxx: 43 | * [x] foo 44 | * [x] bar 45 | * [ ] baz 46 | * yyy: 47 | * [ ] qux 48 | * [x] quux 49 | * [ ] quuz 50 | . 51 |
    52 |
  • xxx: 53 |
      54 |
    • foo
    • 55 |
    • bar
    • 56 |
    • baz
    • 57 |
  • 58 |
  • yyy: 59 |
      60 |
    • qux
    • 61 |
    • quux
    • 62 |
    • quuz
    • 63 |
  • 64 |
65 | . 66 | --ftasklists 67 | ```````````````````````````````` 68 | 69 | Or in a parent task list: 70 | 71 | ```````````````````````````````` example 72 | 1. [x] xxx: 73 | * [x] foo 74 | * [x] bar 75 | * [ ] baz 76 | 2. [ ] yyy: 77 | * [ ] qux 78 | * [x] quux 79 | * [ ] quuz 80 | . 81 |
    82 |
  1. xxx: 83 |
      84 |
    • foo
    • 85 |
    • bar
    • 86 |
    • baz
    • 87 |
  2. 88 |
  3. yyy: 89 |
      90 |
    • qux
    • 91 |
    • quux
    • 92 |
    • quuz
    • 93 |
  4. 94 |
95 | . 96 | --ftasklists 97 | ```````````````````````````````` 98 | 99 | Also, ordinary lists can be nested in the task lists. 100 | 101 | ```````````````````````````````` example 102 | * [x] xxx: 103 | * foo 104 | * bar 105 | * baz 106 | * [ ] yyy: 107 | * qux 108 | * quux 109 | * quuz 110 | . 111 |
    112 |
  • xxx: 113 |
      114 |
    • foo
    • 115 |
    • bar
    • 116 |
    • baz
    • 117 |
  • 118 |
  • yyy: 119 |
      120 |
    • qux
    • 121 |
    • quux
    • 122 |
    • quuz
    • 123 |
  • 124 |
125 | . 126 | --ftasklists 127 | ```````````````````````````````` 128 | -------------------------------------------------------------------------------- /test/spec-underline.txt: -------------------------------------------------------------------------------- 1 | 2 | # Underline 3 | 4 | With the flag `MD_FLAG_UNDERLINE`, MD4C sees underscore `_` rather as a mark 5 | denoting an underlined span rather than an ordinary emphasis (or a strong 6 | emphasis). 7 | 8 | ```````````````````````````````` example 9 | _foo_ 10 | . 11 |

foo

12 | . 13 | --funderline 14 | ```````````````````````````````` 15 | 16 | In sequences of multiple underscores, each single one translates into an 17 | underline span mark. 18 | 19 | ```````````````````````````````` example 20 | ___foo___ 21 | . 22 |

foo

23 | . 24 | --funderline 25 | ```````````````````````````````` 26 | 27 | Intra-word underscores are not recognized as underline marks: 28 | 29 | ```````````````````````````````` example 30 | foo_bar_baz 31 | . 32 |

foo_bar_baz

33 | . 34 | --funderline 35 | ```````````````````````````````` 36 | 37 | Also the parser follows the standard understanding when the underscore can 38 | or cannot open or close a span. Therefore there is no underline in the following 39 | example because no underline can be seen as a closing mark. 40 | 41 | ```````````````````````````````` example 42 | _foo _bar 43 | . 44 |

_foo _bar

45 | . 46 | --funderline 47 | ```````````````````````````````` 48 | -------------------------------------------------------------------------------- /test/spec-wiki-links.txt: -------------------------------------------------------------------------------- 1 | 2 | # Wiki Links 3 | 4 | With the flag `MD_FLAG_WIKILINKS`, MD4C recognizes wiki links. 5 | 6 | The simple wiki-link is a wiki-link destination enclosed in `[[` followed with 7 | `]]`. 8 | 9 | ```````````````````````````````` example 10 | [[foo]] 11 | . 12 |

foo

13 | . 14 | --fwiki-links 15 | ```````````````````````````````` 16 | 17 | However wiki-link may contain an explicit label, delimited from the destination 18 | with `|`. 19 | 20 | ```````````````````````````````` example 21 | [[foo|bar]] 22 | . 23 |

bar

24 | . 25 | --fwiki-links 26 | ```````````````````````````````` 27 | 28 | A wiki-link destination cannot be empty. 29 | 30 | ```````````````````````````````` example 31 | [[]] 32 | . 33 |

[[]]

34 | . 35 | --fwiki-links 36 | ```````````````````````````````` 37 | 38 | ```````````````````````````````` example 39 | [[|foo]] 40 | . 41 |

[[|foo]]

42 | . 43 | --fwiki-links 44 | ```````````````````````````````` 45 | 46 | 47 | The wiki-link destination cannot contain a new line. 48 | 49 | ```````````````````````````````` example 50 | [[foo 51 | bar]] 52 | . 53 |

[[foo 54 | bar]]

55 | . 56 | --fwiki-links 57 | ```````````````````````````````` 58 | 59 | ```````````````````````````````` example 60 | [[foo 61 | bar|baz]] 62 | . 63 |

[[foo 64 | bar|baz]]

65 | . 66 | --fwiki-links 67 | ```````````````````````````````` 68 | 69 | The wiki-link destination is rendered verbatim; inline markup in it is not 70 | recognized. 71 | 72 | ```````````````````````````````` example 73 | [[*foo*]] 74 | . 75 |

*foo*

76 | . 77 | --fwiki-links 78 | ```````````````````````````````` 79 | 80 | ```````````````````````````````` example 81 | [[foo|![bar](bar.jpg)]] 82 | . 83 |

bar

84 | . 85 | --fwiki-links 86 | ```````````````````````````````` 87 | 88 | With multiple `|` delimiters, only the first one is recognized and the other 89 | ones are part of the label. 90 | 91 | ```````````````````````````````` example 92 | [[foo|bar|baz]] 93 | . 94 |

bar|baz

95 | . 96 | --fwiki-links 97 | ```````````````````````````````` 98 | 99 | However the delimiter `|` can be escaped with `/`. 100 | 101 | ```````````````````````````````` example 102 | [[foo\|bar|baz]] 103 | . 104 |

baz

105 | . 106 | --fwiki-links 107 | ```````````````````````````````` 108 | 109 | The label can contain inline elements. 110 | 111 | ```````````````````````````````` example 112 | [[foo|*bar*]] 113 | . 114 |

bar

115 | . 116 | --fwiki-links 117 | ```````````````````````````````` 118 | 119 | Empty explicit label is the same as using the implicit label; i.e. the verbatim 120 | destination string is used as the label. 121 | 122 | ```````````````````````````````` example 123 | [[foo|]] 124 | . 125 |

foo

126 | . 127 | --fwiki-links 128 | ```````````````````````````````` 129 | 130 | The label can span multiple lines. 131 | 132 | ```````````````````````````````` example 133 | [[foo|foo 134 | bar 135 | baz]] 136 | . 137 |

foo 138 | bar 139 | baz

140 | . 141 | --fwiki-links 142 | ```````````````````````````````` 143 | 144 | Wiki-links have higher priority than links. 145 | 146 | ```````````````````````````````` example 147 | [[foo]](foo.jpg) 148 | . 149 |

foo(foo.jpg)

150 | . 151 | --fwiki-links 152 | ```````````````````````````````` 153 | 154 | ```````````````````````````````` example 155 | [foo]: /url 156 | 157 | [[foo]] 158 | . 159 |

foo

160 | . 161 | --fwiki-links 162 | ```````````````````````````````` 163 | 164 | Wiki links can be inlined in tables. 165 | 166 | ```````````````````````````````` example 167 | | A | B | 168 | |------------------|-----| 169 | | [[foo|*bar*]] | baz | 170 | . 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 |
AB
barbaz
185 | . 186 | --fwiki-links --ftables 187 | ```````````````````````````````` 188 | 189 | Wiki-links are not prioritized over images. 190 | 191 | ```````````````````````````````` example 192 | ![[foo]](foo.jpg) 193 | . 194 |

[foo]

195 | . 196 | --fwiki-links 197 | ```````````````````````````````` 198 | 199 | Something that may look like a wiki-link at first, but turns out not to be, 200 | is recognized as a normal link. 201 | 202 | ```````````````````````````````` example 203 | [[foo] 204 | 205 | [foo]: /url 206 | . 207 |

[foo

208 | . 209 | --fwiki-links 210 | ```````````````````````````````` 211 | 212 | Escaping the opening `[` escapes only that one character, not the whole `[[` 213 | opener: 214 | 215 | ```````````````````````````````` example 216 | \[[foo]] 217 | 218 | [foo]: /url 219 | . 220 |

[foo]

221 | . 222 | --fwiki-links 223 | ```````````````````````````````` 224 | 225 | Like with other inline links, the innermost wiki-link is preferred. 226 | 227 | ```````````````````````````````` example 228 | [[foo[[bar]]]] 229 | . 230 |

[[foobar]]

231 | . 232 | --fwiki-links 233 | ```````````````````````````````` 234 | 235 | There is limit of 100 characters for the wiki-link destination. 236 | 237 | ```````````````````````````````` example 238 | [[12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901]] 239 | [[12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901|foo]] 240 | . 241 |

[[12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901]] 242 | [[12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901|foo]]

243 | . 244 | --fwiki-links 245 | ```````````````````````````````` 246 | 247 | 100 characters inside a wiki link target works. 248 | 249 | ```````````````````````````````` example 250 | [[1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890]] 251 | [[1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890|foo]] 252 | . 253 |

1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890 254 | foo

255 | . 256 | --fwiki-links 257 | ```````````````````````````````` 258 | 259 | The limit on link content does not include any characters belonging to a block 260 | quote, if the label spans multiple lines contained in a block quote. 261 | 262 | ```````````````````````````````` example 263 | > [[12345678901234567890123456789012345678901234567890|1234567890 264 | > 1234567890 265 | > 1234567890 266 | > 1234567890 267 | > 123456789]] 268 | . 269 |
270 |

1234567890 271 | 1234567890 272 | 1234567890 273 | 1234567890 274 | 123456789

275 |
276 | . 277 | --fwiki-links 278 | ```````````````````````````````` 279 | --------------------------------------------------------------------------------