Table of Contents
- include/nilorea/n_markdown.h
- Structures
- Types
- Functions
- Defines
- Detailed Description
- Types Documentation
- Functions Documentation
- function n_markdown_parse_ex
- function n_markdown_parse
- function n_markdown_has_markup_ex
- function n_markdown_has_markup
- function n_markdown_free
- Macros Documentation
- define N_MARKDOWN_TABLES
- define N_MARKDOWN_STRIKETHROUGH
- define N_MARKDOWN_STRIKE
- define N_MARKDOWN_NESTED
- define N_MARKDOWN_LINK
- define N_MARKDOWN_ITALIC
- define N_MARKDOWN_INLINE_ALL
- define N_MARKDOWN_HEADINGS
- define N_MARKDOWN_ESCAPES
- define N_MARKDOWN_CODE
- define N_MARKDOWN_BOLD
- define N_MARKDOWN_AUTOLINKS
- define N_MARKDOWN_ALL
- define N_MARKDOWN_ALIGN_RIGHT
- define N_MARKDOWN_ALIGN_LEFT
- define N_MARKDOWN_ALIGN_CENTER
- Source code
include/nilorea/n_markdown.h
Parse a SAFE, strict subset of Markdown into a block / styled-span model. More...
Structures
| Name | |
|---|---|
| struct | n_markdown_span One inline run: visible text, a style bitmask, and (for a link) an already-http(s)-validated url. |
| struct | n_markdown_inline One flow of inline runs (a paragraph, a quote, or one list item). |
| struct | n_markdown_doc A parsed document: an owned array of blocks. |
| struct | n_markdown_block One block. |
Types
| Name | |
|---|---|
| enum | n_markdown_block_kind { N_MARKDOWN_UL, N_MARKDOWN_TABLE, N_MARKDOWN_QUOTE, N_MARKDOWN_PARA = 0, N_MARKDOWN_OL, N_MARKDOWN_HEADING, N_MARKDOWN_CODE_BLOCK} Block kinds. |
Functions
| Name | |
|---|---|
| void | n_markdown_parse_ex(const char * text, size_t len, unsigned flags, n_markdown_doc * out) parse text into out, choosing which optional block kinds to recognise |
| void | n_markdown_parse(const char * text, size_t len, n_markdown_doc * out) parse text (len bytes, plain UTF-8) into out |
| int | n_markdown_has_markup_ex(const char * text, size_t len, unsigned flags) 1 if text contains any markup the given flags would render, else 0 |
| int | n_markdown_has_markup(const char * text, size_t len) 1 if text contains any renderable markup, else 0 |
| void | n_markdown_free(n_markdown_doc * doc) free everything a n_markdown_doc owns and zero it |
Defines
| Name | |
|---|---|
| N_MARKDOWN_TABLES n_markdown_parse_ex flag: recognise GitHub-style pipe tables. |
|
| N_MARKDOWN_STRIKETHROUGH n_markdown_parse_ex flag: " |
|
| N_MARKDOWN_STRIKE |
|
| N_MARKDOWN_NESTED n_markdown_parse_ex flag: indentation nests. |
|
N_MARKDOWN_LINK [[text]()](http(s)://url) style flag; url is set only when this is present |
|
| N_MARKDOWN_ITALIC italic inline style flag |
|
| N_MARKDOWN_INLINE_ALL every inline option: autolinks, strikethrough and escapes. |
|
| N_MARKDOWN_HEADINGS n_markdown_parse_ex flag: recognise "# " to "###### " headings. |
|
| N_MARKDOWN_ESCAPES n_markdown_parse_ex flag: a backslash before ASCII punctuation makes that character literal ("\not italic\"); before anything else it stays. |
|
| N_MARKDOWN_CODE inline code style flag (carries no emphasis) |
|
| N_MARKDOWN_BOLD bold inline style flag |
|
| N_MARKDOWN_AUTOLINKS n_markdown_parse_ex flag: a bare http(s) URL, or one in , is a link whose text is the URL. |
|
| N_MARKDOWN_ALL every block kind this parser knows. |
|
| N_MARKDOWN_ALIGN_RIGHT what " |
|
| N_MARKDOWN_ALIGN_LEFT table column alignment: the default, and what " |
|
| N_MARKDOWN_ALIGN_CENTER what " |
Detailed Description
Parse a SAFE, strict subset of Markdown into a block / styled-span model.
Author: Castagnier Mickael
Version: 1.0
A renderer-agnostic parser for a small, safe subset of Markdown, suitable for chat / message text. It emits a flat, renderer-friendly model rather than a DOM: each inline run carries a style bitmask (bold / italic / code / link) so a caller can turn it into styled glyph runs / DOM nodes / ANSI, etc. The recursive nesting of the source (bold containing italic) is flattened into per-span flags.
It faces hostile input, so it is bounded and safe: a pathological input truncates (caps on blocks / spans / list-items and on bold/italic recursion depth) rather than exploding, and an allocation failure keeps what it has. Unknown markup is left as literal text.
Subset: inline bold / bold, italic / italic, code, text (a non-http(s) scheme renders literally) block paragraphs, ``` fenced code blocks, > blockquotes, "- " / "* " unordered lists, "1. " ordered lists
Three inline options are available the same way: bare and http(s) autolinks, strikethrough and backslash escapes (N_MARKDOWN_INLINE_ALL).
Two more block kinds are available on request, through n_markdown_parse_ex() and its flags: "# " to "###### " headings, and GitHub-style pipe tables. They are opt-in because the default subset is what a chat message is parsed with, where a line beginning with a # is a hash tag far more often than a heading, and where a line of pipes is a line of pipes. A document parsed without a flag sees those lines as ordinary text, exactly as it did before the flags existed.
Types Documentation
enum n_markdown_block_kind
| Enumerator | Value | Description |
|---|---|---|
| N_MARKDOWN_UL | unordered list (items) | |
| N_MARKDOWN_TABLE | pipe table (items, n_cols) | |
| N_MARKDOWN_QUOTE |
blockquote | | N_MARKDOWN_PARA | 0| paragraph | | N_MARKDOWN_OL | | ordered list (items) | | N_MARKDOWN_HEADING | | "## " heading (inl, level) | | N_MARKDOWN_CODE_BLOCK | | ``` fenced code (raw code) |
Block kinds.
The last two appear only when n_markdown_parse_ex() was asked for them, so a caller that has not been updated cannot meet one.
Functions Documentation
function n_markdown_parse_ex
void n_markdown_parse_ex(
const char * text,
size_t len,
unsigned flags,
n_markdown_doc * out
)
parse text into out, choosing which optional block kinds to recognise
Parameters:
- text the UTF-8 source (may be NULL)
- len number of bytes of text
- flags any of N_MARKDOWN_HEADINGS, N_MARKDOWN_TABLES, N_MARKDOWN_AUTOLINKS, N_MARKDOWN_STRIKETHROUGH, N_MARKDOWN_ESCAPES, N_MARKDOWN_ALL, N_MARKDOWN_INLINE_ALL; unknown bits are ignored
- out the document to fill (zeroed first)
n_markdown_parse() is this with flags of zero, and that is the whole difference: the default subset is unchanged, byte for byte, so a caller that never passes a flag can never be handed a block kind it does not know.
Same guarantees as n_markdown_parse(): out is zeroed first, an allocation failure or a bounds cap truncates safely rather than failing, and the result is always released with n_markdown_free().
function n_markdown_parse
void n_markdown_parse(
const char * text,
size_t len,
n_markdown_doc * out
)
parse text (len bytes, plain UTF-8) into out
Parameters:
- text the UTF-8 source (may be NULL)
- len number of bytes of text
- out the document to fill (zeroed first)
Never fails destructively: on allocation failure or a bounds cap it keeps what it has (safe truncation). out is zeroed first. Always pair with n_markdown_free.
function n_markdown_has_markup_ex
int n_markdown_has_markup_ex(
const char * text,
size_t len,
unsigned flags
)
1 if text contains any markup the given flags would render, else 0
Parameters:
- text the UTF-8 source
- len number of bytes of text
- flags as n_markdown_parse_ex()
Return: 1 if there is renderable markup, 0 otherwise
n_markdown_has_markup() is this with flags of zero. A document that is nothing but a heading has no markup in the default subset and has markup with N_MARKDOWN_HEADINGS, which is the answer each caller wants.
function n_markdown_has_markup
int n_markdown_has_markup(
const char * text,
size_t len
)
1 if text contains any renderable markup, else 0
Parameters:
- text the UTF-8 source
- len number of bytes of text
Return: 1 if there is renderable markup, 0 otherwise
Lets a caller skip the styled path for a plain message.
function n_markdown_free
void n_markdown_free(
n_markdown_doc * doc
)
free everything a n_markdown_doc owns and zero it
Parameters:
- doc the document (safe on a zeroed doc and on NULL)
Macros Documentation
define N_MARKDOWN_TABLES
#define N_MARKDOWN_TABLES (1u << 1)
n_markdown_parse_ex flag: recognise GitHub-style pipe tables.
define N_MARKDOWN_STRIKETHROUGH
#define N_MARKDOWN_STRIKETHROUGH (1u << 3)
n_markdown_parse_ex flag: "text" carries N_MARKDOWN_STRIKE.
define N_MARKDOWN_STRIKE
#define N_MARKDOWN_STRIKE (1u << 4)
strike inline style flag (only with N_MARKDOWN_STRIKETHROUGH)
define N_MARKDOWN_NESTED
#define N_MARKDOWN_NESTED (1u << 5)
n_markdown_parse_ex flag: indentation nests.
A list item's marker, a fence, a quote or a paragraph indented under a list item starts a block one level deeper (n_markdown_block.depth) instead of being folded into the item's text; a fence may be indented, and its code keeps only the indentation beyond the fence's; an ordered list keeps the number its first item has (n_markdown_block.start), so a list cut by nested blocks goes on counting. What a model writes in a chat answer: steps with sub-points and code under them.
define N_MARKDOWN_LINK
#define N_MARKDOWN_LINK (1u << 3)
[[text]()](http(s)://url) style flag; url is set only when this is present
define N_MARKDOWN_ITALIC
#define N_MARKDOWN_ITALIC (1u << 1)
italic inline style flag
define N_MARKDOWN_INLINE_ALL
#define N_MARKDOWN_INLINE_ALL (N_MARKDOWN_AUTOLINKS | N_MARKDOWN_STRIKETHROUGH | N_MARKDOWN_ESCAPES)
every inline option: autolinks, strikethrough and escapes.
define N_MARKDOWN_HEADINGS
#define N_MARKDOWN_HEADINGS (1u << 0)
n_markdown_parse_ex flag: recognise "# " to "###### " headings.
define N_MARKDOWN_ESCAPES
#define N_MARKDOWN_ESCAPES (1u << 4)
n_markdown_parse_ex flag: a backslash before ASCII punctuation makes that character literal ("\not italic\"); before anything else it stays.
define N_MARKDOWN_CODE
#define N_MARKDOWN_CODE (1u << 2)
inline code style flag (carries no emphasis)
define N_MARKDOWN_BOLD
#define N_MARKDOWN_BOLD (1u << 0)
bold inline style flag
define N_MARKDOWN_AUTOLINKS
#define N_MARKDOWN_AUTOLINKS (1u << 2)
n_markdown_parse_ex flag: a bare http(s) URL, or one in , is a link whose text is the URL.
Trailing sentence punctuation and an unbalanced ")" are left out of it, and a bare one starts only at a word boundary.
define N_MARKDOWN_ALL
#define N_MARKDOWN_ALL (N_MARKDOWN_HEADINGS | N_MARKDOWN_TABLES | N_MARKDOWN_NESTED)
every block kind this parser knows.
Deliberately NOT the inline options below: a renderer asking for every block kind (n_gui_md) would otherwise be handed a N_MARKDOWN_STRIKE span it does not draw.
define N_MARKDOWN_ALIGN_RIGHT
#define N_MARKDOWN_ALIGN_RIGHT 2
what "|---:|" asks for.
define N_MARKDOWN_ALIGN_LEFT
#define N_MARKDOWN_ALIGN_LEFT 0
table column alignment: the default, and what "|:---|" asks for.
define N_MARKDOWN_ALIGN_CENTER
#define N_MARKDOWN_ALIGN_CENTER 1
what "|:---:|" asks for.
Source code
/*
* Nilorea Library
* Copyright (C) 2005-2026 Castagnier Mickael
*
* 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.
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef N_MARKDOWN_H
#define N_MARKDOWN_H
#include <stddef.h>
#define N_MARKDOWN_BOLD (1u << 0)
#define N_MARKDOWN_ITALIC (1u << 1)
#define N_MARKDOWN_CODE (1u << 2)
#define N_MARKDOWN_LINK (1u << 3)
#define N_MARKDOWN_STRIKE (1u << 4)
#define N_MARKDOWN_HEADINGS (1u << 0)
#define N_MARKDOWN_TABLES (1u << 1)
#define N_MARKDOWN_AUTOLINKS (1u << 2)
#define N_MARKDOWN_STRIKETHROUGH (1u << 3)
#define N_MARKDOWN_ESCAPES (1u << 4)
#define N_MARKDOWN_NESTED (1u << 5)
#define N_MARKDOWN_ALL (N_MARKDOWN_HEADINGS | N_MARKDOWN_TABLES | N_MARKDOWN_NESTED)
#define N_MARKDOWN_INLINE_ALL \
(N_MARKDOWN_AUTOLINKS | N_MARKDOWN_STRIKETHROUGH | N_MARKDOWN_ESCAPES)
#define N_MARKDOWN_ALIGN_LEFT 0
#define N_MARKDOWN_ALIGN_CENTER 1
#define N_MARKDOWN_ALIGN_RIGHT 2
enum n_markdown_block_kind {
N_MARKDOWN_PARA = 0,
N_MARKDOWN_QUOTE,
N_MARKDOWN_CODE_BLOCK,
N_MARKDOWN_UL,
N_MARKDOWN_OL,
N_MARKDOWN_HEADING,
N_MARKDOWN_TABLE
};
typedef struct n_markdown_span {
char* text;
char* url;
unsigned style;
} n_markdown_span;
typedef struct n_markdown_inline {
n_markdown_span* spans;
size_t n_spans;
} n_markdown_inline;
typedef struct n_markdown_block {
int kind;
n_markdown_inline inl;
char* code;
n_markdown_inline* items;
size_t n_items;
int level;
size_t n_cols;
unsigned char* align;
int depth;
long start;
} n_markdown_block;
typedef struct n_markdown_doc {
n_markdown_block* blocks;
size_t n_blocks;
} n_markdown_doc;
void n_markdown_parse(const char* text, size_t len, n_markdown_doc* out);
void n_markdown_parse_ex(const char* text, size_t len, unsigned flags, n_markdown_doc* out);
void n_markdown_free(n_markdown_doc* doc);
int n_markdown_has_markup(const char* text, size_t len);
int n_markdown_has_markup_ex(const char* text, size_t len, unsigned flags);
#endif /* N_MARKDOWN_H */
Modules
- AABB3D
- ACCEPT_POOL
- ALLEGRO4
- ALLEGRO5
- ANIM_LIB
- ASTAR
- ATLAS_LIB
- AVRO
- CLOCK_SYNC
- COMMONS
- CONFIGFILE
- CYPHER_BASE64
- CYPHER_VIGENERE
- DEAD_RECKONING
- ENUMS
- EXCEPTIONS
- GAME_ENV
- GIT
- GUI
- HASH_TABLE
- ISOMETRIC_ENGINE
- LIST
- LOG
- LOGNODUP
- LZ4
- NETWORKING
- NETWORK_MSG
- N_COOKIES
- N_DIFF
- N_DIGEST
- N_DISK_CACHE
- N_DNS
- N_ECC
- N_ENTROPY
- N_FILES
- N_FLUIDS
- N_GUI_COLOR
- N_GUI_COLORWHEEL
- N_GUI_CURVE
- N_GUI_GRID
- N_GUI_POPUP
- N_HEX
- N_HTTP3
- N_HTTP_COOKIE
- N_HTTP_FORM
- N_HTTP_MIME
- N_HTTP_REQ
- N_HTTP_RESP
- N_HTTP_ROUTER
- N_HTTP_SERVER
- N_ICC
- N_JSON
- N_KAFKA
- N_LINALG
- N_MODULE
- N_OPENSSL_COMPAT
- N_PRETTY
- N_PTH
- N_QUERY
- N_RANDOM
- N_SHA256
- N_STR
- N_TIFF
- N_TIME
- N_USER
- N_X509
- N_ZIP
- PARTICLES
- PCRE
- PHYSICS
- SIGNALS
- STACK
- THREADS
- TRAJECTORY
- TREE
- ZLIB