This commit is contained in:
Татьяна Фарбер 2025-12-13 01:28:21 +04:00
parent 4eef1c9252
commit d81f4ee30b
9 changed files with 314 additions and 9 deletions

18
LICENSE
View File

@ -1,9 +1,13 @@
MIT License
Copyright (c) 2012-2018 Craig Barnes
Copyright (c) 2025 Datenlabor
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
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 SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

30
Makefile Normal file
View File

@ -0,0 +1,30 @@
# This makefile is used for testing purposes and makes no attempt
# to be portable. Use "luarocks make" to build and install.
CC ?= gcc
CFLAGS = -g -O2
XCFLAGS += -std=c99 -pedantic-errors -fPIC
CWARNS = -Wall -Wextra -Wshadow -Wundef -Wconversion -Wc90-c99-compat
LUA = lua
default:
@echo 'The Makefile is for development only.' >&2
@echo 'Use "luarocks make" to install.' >&2
all: discount3.so
discount3.so: discount3.o
$(CC) $(LDFLAGS) -fPIC -shared -o $@ $^ -lmarkdown
discount3.o: discount3.c
$(CC) $(XCFLAGS) $(CPPFLAGS) $(CFLAGS) $(CWARNS) -c -o $@ $<
check: all
$(LUA) test.lua
clean:
rm -f discount3.so discount3.o
.PHONY: default all check clean
.DELETE_ON_ERROR:

View File

@ -1,3 +1,46 @@
# discount3
# lua-discount3 Markdown
Discount v3 lua binding
[Lua] bindings for the [Discount] [Markdown] library version >3.0.
Fork of Craig Barnes archive: https://github.com/craigbarnes/lua-discount
Jessica (ex- David) L. Parsons wrote (https://www.pell.portland.or.us/~orc/Code/discount/downloads.html):
```
I ran out of slots in the original bitmap flags structure, so I dumped it and replaced it with a new flag blob accessed through a flag pointer. This broke the entire published interface and required the update to version 3 (this is why it took me over 3 years to implement it; I needed to test the code to within an inch of its life and between that and transitioning it just ate up time like you wouldnt believe!)
Because I changed the mkd_flag_t structure and replaced it with a blob, the old flags are now a pointer and using the mkd_flag_isset(), mkd_set_flag(), and mkd_clr_flag() functions I introduced during the end of the run for version 2.x.x is now mandatory unless youre fond of core dumps.
The way Id originally implemented HTML5 (if thats even a thing anymore?) support was to have a global structure that I allocated once and then you were stuck with it forever (and had to deallocate the structure whenever you stopped using the library otherwise it would leak memory), so I fixed that for v3.0.0 by moving the html5 details inside the MMIOT (activated by setting the flag MKD_HTML5) so it will automatically be deallocated when you are done with a document without affecting other MMIOTs.
```
Here is the implementation of bindings for discount v3 API.
## Install
```
luarocks install discount3
```
## Troubleshooting
If you get a compilation error during installation, it's because discount isn't installed as a shared library. See the solution here: https://gitlabor.ru/Datenlabor/discount
License
-------
ISC [License][] (SPDX: [`ISC`]).
[Lua]: https://www.lua.org/
[Discount]: http://www.pell.portland.or.us/~orc/Code/discount/
[Markdown]: https://en.wikipedia.org/wiki/Markdown
[License]: LICENSE
[`ISC`]: https://spdx.org/licenses/ISC.html

31
compat.h Normal file
View File

@ -0,0 +1,31 @@
#ifndef LUA_VERSION_NUM
# error Lua >= 5.1 is required.
#endif
#ifdef __has_attribute
# define HAS_ATTRIBUTE(x) __has_attribute(x)
#else
# define HAS_ATTRIBUTE(x) 0
#endif
#ifdef __has_builtin
# define HAS_BUILTIN(x) __has_builtin(x)
#else
# define HAS_BUILTIN(x) 0
#endif
#if defined(__GNUC__) || HAS_ATTRIBUTE(visibility)
# define EXPORT __attribute__((__visibility__("default")))
#elif defined(_WIN32)
# define EXPORT __declspec(dllexport)
#else
# define EXPORT
#endif
#if (defined(__GNUC__) || HAS_BUILTIN(__builtin_expect)) && defined(__OPTIMIZE__)
# define likely(x) __builtin_expect(!!(x), 1)
# define unlikely(x) __builtin_expect(!!(x), 0)
#else
# define likely(x) (x)
# define unlikely(x) (x)
#endif

36
discount3-1.0-1.rockspec Normal file
View File

@ -0,0 +1,36 @@
package = "discount3"
version = "1.0-1"
description = {
summary = "Lua bindings for the Discount Markdown library V3",
detailed = [[
Supports all tags.
]],
homepage = "https://gitlabor.ru/Datenlabor/discount3",
license = "ISC"
}
source = {
url = "https://gitlabor.ru/Datenlabor/discount3/tags/v1.0-0.tar.gz"
}
dependencies = {
"lua >= 5.1"
}
external_dependencies = {
DISCOUNT = {
header = "mkdio.h",
library = "markdown"
}
}
build = {
type = "builtin",
modules = {
discount3 = {
sources = {"discount3.c"},
libraries = {"markdown"}
}
}
}

121
discount3.c Normal file
View File

@ -0,0 +1,121 @@
/*
Lua bindings for the Discount Markdown library.
Copyright (c) 2012-2018 Craig Barnes
Copyright (c) 2025 datenlabor.ru
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <limits.h>
#include <stddef.h>
#include <lua.h>
#include <lauxlib.h>
#include <mkdio.h>
#include "compat.h"
static const char *const options[] = {
"nolinks", "noimages", "nopants", "nohtml", "strict",
"tagtext", "noext", "cdata", "nosuperscript",
"notables", "nostrikethrough", "toc", "compat", "autolink",
"safelink", "noheader", "tabstop", "nodivquote",
"noalphalist", "extrafootnote", "nostyle",
"dlextra", "fencedcode", "idanchor",
"githubtags", "urlencodedanchor", "latex", "html5",
"normallist", "explicitlist", "dldiscount",
"altastitle", "extendedattr", NULL
};
static const unsigned int option_codes[] = {
MKD_NOLINKS, MKD_NOIMAGE, MKD_NOPANTS, MKD_NOHTML, MKD_STRICT,
MKD_TAGTEXT, MKD_NO_EXT, MKD_CDATA, MKD_NOSUPERSCRIPT,
MKD_NOTABLES, MKD_NOSTRIKETHROUGH, MKD_TOC, MKD_1_COMPAT, MKD_AUTOLINK,
MKD_SAFELINK, MKD_NOHEADER, MKD_TABSTOP, MKD_NODIVQUOTE,
MKD_NOALPHALIST, MKD_EXTRA_FOOTNOTE, MKD_NOSTYLE,
MKD_DLEXTRA, MKD_FENCEDCODE, MKD_IDANCHOR,
MKD_GITHUBTAGS, MKD_URLENCODEDANCHOR, MKD_LATEX, MKD_HTML5,
MKD_NORMAL_LISTITEM, MKD_EXPLICITLIST, MKD_DLDISCOUNT,
MKD_ALT_AS_TITLE, MKD_EXTENDED_ATTR
};
static void add_field(lua_State *L, const char *k, const char *v) {
lua_pushstring(L, v);
lua_setfield(L, -2, k);
}
static void add_lfield(lua_State *L, const char *k, const char *v, size_t n) {
lua_pushlstring(L, v, n);
lua_setfield(L, -2, k);
}
static int compile(lua_State *L) {
MMIOT *doc;
mkd_flag_t *flags = mkd_flags();
char *body = NULL, *toc = NULL, *css = NULL;
int body_size, toc_size, css_size, i, argc;
size_t input_size;
const char *input = luaL_checklstring(L, 1, &input_size);
luaL_argcheck(L, input_size < INT_MAX, 1, "string too long");
for (i = 2, argc = lua_gettop(L); i <= argc; i++) {
mkd_set_flag_num(flags, option_codes[luaL_checkoption(L, i, NULL, options)]);
}
doc = mkd_string(input, (int) input_size, flags);
if (unlikely(doc == NULL)) {
lua_pushnil(L);
lua_pushstring(L, "mkd_string() returned NULL");
return 2;
}
if (unlikely(mkd_compile(doc, flags) != 1)) {
mkd_cleanup(doc);
lua_pushnil(L);
lua_pushstring(L, "mkd_compile() failed");
return 2;
}
body_size = mkd_document(doc, &body);
if (unlikely(body == NULL || body_size < 0)) {
mkd_cleanup(doc);
lua_pushnil(L);
lua_pushstring(L, "mkd_document() failed");
return 2;
}
lua_createtable(L, 0, 6);
add_lfield(L, "body", body, (size_t) body_size);
add_field(L, "title", mkd_doc_title(doc));
add_field(L, "author", mkd_doc_author(doc));
add_field(L, "date", mkd_doc_date(doc));
if ((css_size = mkd_css(doc, &css)) > 0 && css) {
add_lfield(L, "css", css, (size_t) css_size);
}
if ((mkd_flag_isset(flags, MKD_TOC)) && (toc_size = mkd_toc(doc, &toc)) > 0 && toc) {
add_lfield(L, "index", toc, (size_t) toc_size);
}
mkd_cleanup(doc);
return 1;
}
EXPORT int luaopen_discount3(lua_State *L) {
lua_createtable(L, 0, 2);
lua_pushcfunction(L, compile);
lua_setfield(L, -2, "compile");
lua_pushstring(L, markdown_version);
lua_setfield(L, -2, "_libmarkdown_version");
return 1;
}

BIN
discount3.o Normal file

Binary file not shown.

BIN
discount3.so Executable file

Binary file not shown.

40
test.lua Normal file
View File

@ -0,0 +1,40 @@
package.path = ''
package.cpath = './?.so'
local discount = require "discount3"
local compile = assert(discount.compile)
do
local libver = assert(discount._libmarkdown_version)
assert(libver:match("^[0-9]"))
print(libver)
end
do
local doc = assert(compile "%Title ::\n%Author ::\n%Date ::\n*Text*\n")
assert(doc.title == "Title ::")
assert(doc.author == "Author ::")
assert(doc.date == "Date ::")
assert(doc.body == "<p><em>Text</em></p>")
print(doc.title)
end
do
local doc = assert(compile "<style>a {color: red}</style>Text")
--assert(doc.css == "<style>a {color: red}</style>\n")
print(doc.css)
end
do
local doc = assert(compile("", "toc"))
assert(doc.body == "")
assert(not doc.index)
end
do
local doc = assert(compile("# Heading", "toc"))
assert(doc.body)
assert(doc.index)
print(doc.index)
end
io.stderr:write("\27[1;32mAll tests passed\27[0m\n")