Add C based module + lua based API code (WIP)

This commit is contained in:
DanyLE 2023-01-18 15:12:48 +01:00
parent 8417bccefb
commit d4bb12c6b5
56 changed files with 23110 additions and 41 deletions

109
.gitignore vendored
View File

@ -1,49 +1,76 @@
# http://www.gnu.org/software/automake # Prerequisites
plugins
build
*._*
*.d
*.deb
.vscode
# Object files
*.o
*.ko
*.obj
*.elf
Makefile.in # Linker output
/ar-lib *.ilk
/mdate-sh *.map
/py-compile *.exp
/test-driver
/ylwrap
# http://www.gnu.org/software/autoconf # Precompiled Headers
*.gch
*.pch
autom4te.cache # Libraries
/autoscan.log *.lib
/autoscan-*.log *.a
/aclocal.m4 *.la
/compile *.lo
/config.guess
/config.h.in
/config.log
/config.status
/config.sub
/configure
/configure.scan
/depcomp
/install-sh
/missing
/stamp-h1
# https://www.gnu.org/software/libtool/ # Shared objects (inc. Windows DLLs)
*.dll
*.so
*.so.*
*.dylib
/ltmain.sh # Executables
*.exe
*.out
*.app
*.i*86
*.x86_64
*.hex
# http://www.gnu.org/software/texinfo # Debug files
*.dSYM/
*.su
*.idb
*.pdb
/texinfo.tex # Kernel Module Compile Results
*.mod*
# http://www.gnu.org/software/m4/ *.cmd
modules.order
m4/libtool.m4 Module.symvers
m4/ltoptions.m4 Makefile.old
m4/ltsugar.m4 dkms.con
m4/ltversion.m4 .DS_Store
m4/lt~obsolete.m4 .*
*.cache
# Generated Makefile
# (meta build system like autotools,
# can automatically generate from config.status script
# (which is called by configure script))
Makefile Makefile
antd
compile
config.guess
depcomp
install-sh
missing
libtool
config.log
config.status
config.sub
configure
aclocal.m4
ltmain.sh
Makefile.in
# others
3rd/lua-5.3.4/lua
3rd/lua-5.3.4/luac

40
Makefile.am Normal file
View File

@ -0,0 +1,40 @@
AUTOMAKE_OPTIONS = foreign
# check for system
if LINUX
AM_CPPFLAGS = -Wl,E,--no-as-needed
FL_LUA=linux
else
AM_CPPFLAGS = -Wl,-undefined,dynamic_lookup
FL_LUA=macosx
endif
AM_CPPFLAGS += -W -Wall -g -std=c99 -fPIC
silkdir = $(libdir)/lua/silk
silk_DATA = silkmvc/router.lua.tpl \
silkmvc/BaseController.lua \
silkmvc/Router.lua \
silkmvc/BaseModel.lua \
silkmvc/Template.lua \
silkmvc/Logger.lua \
silkmvc/BaseObject.lua \
silkmvc/DBHelper.lua \
silkmvc/api.lua
coredir = $(libdir)/lua/silk/core
core_DATA = silkmvc/core/OOP.lua \
silkmvc/core/std.lua \
silkmvc/core/extra_mime.lua \
silkmvc/core/utils.lua \
silkmvc/core/api.lua \
silkmvc/core/cif.lua \
silkmvc/core/sqlite.lua
# lua libraris & modules
SUBDIRS = . modules
EXTRA_DIST = README.md silkmvc

87
configure.ac Normal file
View File

@ -0,0 +1,87 @@
# initialise autoconf and set up some basic information about the program were packaging
AC_INIT([silk], [0.1.0], [xsang.le@gmail.com])
# Were going to use automake for this project
# [subdir-objects] if needed
AM_INIT_AUTOMAKE([subdir-objects])
# dependencies
# C compiler
AC_PROG_CC
# libtool for linking
AC_PROG_LIBTOOL
# check if sqlite3 header exists
has_sqlite=no
AC_CHECK_HEADER([sqlite3.h],[
AC_DEFINE([USE_DB], [1],[Use sqlite3])
has_sqlite=yes
# check if the library exists
],[])
AC_CHECK_LIB([sqlite3],[sqlite3_open],[],[
if test "$has_sqlite" = "yes"; then
AC_MSG_ERROR([Unable to find sqlite3 shared library])
fi
])
# check for lm
AC_CHECK_LIB([m],[cos],[],[
AC_MSG_ERROR([unable to find libm])
])
# check for libreadline
#AC_CHECK_HEADER([readline/readline.h],[],[
# AC_MSG_ERROR([unable to find libreadline headers])
#])
#AC_CHECK_LIB([readline],[read_history],[],[
# AC_MSG_ERROR([unable to find libreadline])
#])
AC_DEFINE([_GNU_SOURCE], [1],[Use GNU source])
# AC_CANONICAL_HOST is needed to access the 'host_os' variable
AC_CANONICAL_HOST
build_linux=no
build_windows=no
build_mac=no
# Detect the target system
case "${host_os}" in
linux*)
AC_DEFINE([LINUX], [1],[Linux system])
build_linux=yes
;;
darwin*)
build_mac=yes
AC_DEFINE([MACOS], [1],[MacOS system])
;;
*)
AC_MSG_ERROR(["OS $host_os is not supported"])
;;
esac
if test "$build_linux" = "yes"; then
AC_CHECK_LIB([crypt],[crypt],[],[
AC_MSG_ERROR([unable to find libcrypt])
])
fi
# case for window:
# cygwin*|mingw*)
# build_windows=yes
# ;;
# Pass the conditionals to automake
AM_CONDITIONAL([HAS_DB], [test "$has_sqlite" = "yes"])
AM_CONDITIONAL([LINUX], [test "$build_linux" = "yes"])
AM_CONDITIONAL([WINDOWS], [test "$build_windows" = "yes"])
AM_CONDITIONAL([OSX], [test "$build_mac" = "yes"])
# find all config files
AC_CONFIG_FILES([
Makefile
modules/Makefile
])
# AC_SUBST([my_CPPFLAGS]) pass my_CPPFLAGS to the makefile.am
# output the script:
AC_OUTPUT

20
modules/3rd/jsmn/LICENSE Executable file
View File

@ -0,0 +1,20 @@
Copyright (c) 2010 Serge A. Zaitsev
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.

167
modules/3rd/jsmn/README.md Executable file
View File

@ -0,0 +1,167 @@
JSMN
====
jsmn (pronounced like 'jasmine') is a minimalistic JSON parser in C. It can be
easily integrated into resource-limited or embedded projects.
You can find more information about JSON format at [json.org][1]
Library sources are available at https://github.com/zserge/jsmn
The web page with some information about jsmn can be found at
[http://zserge.com/jsmn.html][2]
Philosophy
----------
Most JSON parsers offer you a bunch of functions to load JSON data, parse it
and extract any value by its name. jsmn proves that checking the correctness of
every JSON packet or allocating temporary objects to store parsed JSON fields
often is an overkill.
JSON format itself is extremely simple, so why should we complicate it?
jsmn is designed to be **robust** (it should work fine even with erroneous
data), **fast** (it should parse data on the fly), **portable** (no superfluous
dependencies or non-standard C extensions). And of course, **simplicity** is a
key feature - simple code style, simple algorithm, simple integration into
other projects.
Features
--------
* compatible with C89
* no dependencies (even libc!)
* highly portable (tested on x86/amd64, ARM, AVR)
* about 200 lines of code
* extremely small code footprint
* API contains only 2 functions
* no dynamic memory allocation
* incremental single-pass parsing
* library code is covered with unit-tests
Design
------
The rudimentary jsmn object is a **token**. Let's consider a JSON string:
'{ "name" : "Jack", "age" : 27 }'
It holds the following tokens:
* Object: `{ "name" : "Jack", "age" : 27}` (the whole object)
* Strings: `"name"`, `"Jack"`, `"age"` (keys and some values)
* Number: `27`
In jsmn, tokens do not hold any data, but point to token boundaries in JSON
string instead. In the example above jsmn will create tokens like: Object
[0..31], String [3..7], String [12..16], String [20..23], Number [27..29].
Every jsmn token has a type, which indicates the type of corresponding JSON
token. jsmn supports the following token types:
* Object - a container of key-value pairs, e.g.:
`{ "foo":"bar", "x":0.3 }`
* Array - a sequence of values, e.g.:
`[ 1, 2, 3 ]`
* String - a quoted sequence of chars, e.g.: `"foo"`
* Primitive - a number, a boolean (`true`, `false`) or `null`
Besides start/end positions, jsmn tokens for complex types (like arrays
or objects) also contain a number of child items, so you can easily follow
object hierarchy.
This approach provides enough information for parsing any JSON data and makes
it possible to use zero-copy techniques.
Install
-------
To clone the repository you should have Git installed. Just run:
$ git clone https://github.com/zserge/jsmn
Repository layout is simple: jsmn.c and jsmn.h are library files, tests are in
the jsmn\_test.c, you will also find README, LICENSE and Makefile files inside.
To build the library, run `make`. It is also recommended to run `make test`.
Let me know, if some tests fail.
If build was successful, you should get a `libjsmn.a` library.
The header file you should include is called `"jsmn.h"`.
API
---
Token types are described by `jsmntype_t`:
typedef enum {
JSMN_UNDEFINED = 0,
JSMN_OBJECT = 1,
JSMN_ARRAY = 2,
JSMN_STRING = 3,
JSMN_PRIMITIVE = 4
} jsmntype_t;
**Note:** Unlike JSON data types, primitive tokens are not divided into
numbers, booleans and null, because one can easily tell the type using the
first character:
* <code>'t', 'f'</code> - boolean
* <code>'n'</code> - null
* <code>'-', '0'..'9'</code> - number
Token is an object of `jsmntok_t` type:
typedef struct {
jsmntype_t type; // Token type
int start; // Token start position
int end; // Token end position
int size; // Number of child (nested) tokens
} jsmntok_t;
**Note:** string tokens point to the first character after
the opening quote and the previous symbol before final quote. This was made
to simplify string extraction from JSON data.
All job is done by `jsmn_parser` object. You can initialize a new parser using:
jsmn_parser parser;
jsmntok_t tokens[10];
jsmn_init(&parser);
// js - pointer to JSON string
// tokens - an array of tokens available
// 10 - number of tokens available
jsmn_parse(&parser, js, strlen(js), tokens, 10);
This will create a parser, and then it tries to parse up to 10 JSON tokens from
the `js` string.
A non-negative return value of `jsmn_parse` is the number of tokens actually
used by the parser.
Passing NULL instead of the tokens array would not store parsing results, but
instead the function will return the value of tokens needed to parse the given
string. This can be useful if you don't know yet how many tokens to allocate.
If something goes wrong, you will get an error. Error will be one of these:
* `JSMN_ERROR_INVAL` - bad token, JSON string is corrupted
* `JSMN_ERROR_NOMEM` - not enough tokens, JSON string is too large
* `JSMN_ERROR_PART` - JSON string is too short, expecting more JSON data
If you get `JSON_ERROR_NOMEM`, you can re-allocate more tokens and call
`jsmn_parse` once more. If you read json data from the stream, you can
periodically call `jsmn_parse` and check if return value is `JSON_ERROR_PART`.
You will get this error until you reach the end of JSON data.
Other info
----------
This software is distributed under [MIT license](http://www.opensource.org/licenses/mit-license.php),
so feel free to integrate it in your commercial products.
[1]: http://www.json.org/
[2]: http://zserge.com/jsmn.html

View File

@ -0,0 +1,126 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include "../jsmn.h"
/* Function realloc_it() is a wrapper function for standart realloc()
* with one difference - it frees old memory pointer in case of realloc
* failure. Thus, DO NOT use old data pointer in anyway after call to
* realloc_it(). If your code has some kind of fallback algorithm if
* memory can't be re-allocated - use standart realloc() instead.
*/
static inline void *realloc_it(void *ptrmem, size_t size) {
void *p = realloc(ptrmem, size);
if (!p) {
free (ptrmem);
fprintf(stderr, "realloc(): errno=%d\n", errno);
}
return p;
}
/*
* An example of reading JSON from stdin and printing its content to stdout.
* The output looks like YAML, but I'm not sure if it's really compatible.
*/
static int dump(const char *js, jsmntok_t *t, size_t count, int indent) {
int i, j, k;
if (count == 0) {
return 0;
}
if (t->type == JSMN_PRIMITIVE) {
printf("%.*s", t->end - t->start, js+t->start);
return 1;
} else if (t->type == JSMN_STRING) {
printf("'%.*s'", t->end - t->start, js+t->start);
return 1;
} else if (t->type == JSMN_OBJECT) {
printf("\n");
j = 0;
for (i = 0; i < t->size; i++) {
for (k = 0; k < indent; k++) printf(" ");
j += dump(js, t+1+j, count-j, indent+1);
printf(": ");
j += dump(js, t+1+j, count-j, indent+1);
printf("\n");
}
return j+1;
} else if (t->type == JSMN_ARRAY) {
j = 0;
printf("\n");
for (i = 0; i < t->size; i++) {
for (k = 0; k < indent-1; k++) printf(" ");
printf(" - ");
j += dump(js, t+1+j, count-j, indent+1);
printf("\n");
}
return j+1;
}
return 0;
}
int main() {
int r;
int eof_expected = 0;
char *js = NULL;
size_t jslen = 0;
char buf[BUFSIZ];
jsmn_parser p;
jsmntok_t *tok;
size_t tokcount = 2;
/* Prepare parser */
jsmn_init(&p);
/* Allocate some tokens as a start */
tok = malloc(sizeof(*tok) * tokcount);
if (tok == NULL) {
fprintf(stderr, "malloc(): errno=%d\n", errno);
return 3;
}
for (;;) {
/* Read another chunk */
r = fread(buf, 1, sizeof(buf), stdin);
if (r < 0) {
fprintf(stderr, "fread(): %d, errno=%d\n", r, errno);
return 1;
}
if (r == 0) {
if (eof_expected != 0) {
return 0;
} else {
fprintf(stderr, "fread(): unexpected EOF\n");
return 2;
}
}
js = realloc_it(js, jslen + r + 1);
if (js == NULL) {
return 3;
}
strncpy(js + jslen, buf, r);
jslen = jslen + r;
again:
r = jsmn_parse(&p, js, jslen, tok, tokcount);
if (r < 0) {
if (r == JSMN_ERROR_NOMEM) {
tokcount = tokcount * 2;
tok = realloc_it(tok, sizeof(*tok) * tokcount);
if (tok == NULL) {
return 3;
}
goto again;
}
} else {
dump(js, tok, p.toknext, 0);
eof_expected = 1;
}
}
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,76 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "../jsmn.h"
/*
* A small example of jsmn parsing when JSON structure is known and number of
* tokens is predictable.
*/
static const char *JSON_STRING =
"{\"user\": \"johndoe\", \"admin\": false, \"uid\": 1000,\n "
"\"groups\": [\"users\", \"wheel\", \"audio\", \"video\"]}";
static int jsoneq(const char *json, jsmntok_t *tok, const char *s) {
if (tok->type == JSMN_STRING && (int) strlen(s) == tok->end - tok->start &&
strncmp(json + tok->start, s, tok->end - tok->start) == 0) {
return 0;
}
return -1;
}
int main() {
int i;
int r;
jsmn_parser p;
jsmntok_t t[128]; /* We expect no more than 128 tokens */
jsmn_init(&p);
r = jsmn_parse(&p, JSON_STRING, strlen(JSON_STRING), t, sizeof(t)/sizeof(t[0]));
if (r < 0) {
printf("Failed to parse JSON: %d\n", r);
return 1;
}
/* Assume the top-level element is an object */
if (r < 1 || t[0].type != JSMN_OBJECT) {
printf("Object expected\n");
return 1;
}
/* Loop over all keys of the root object */
for (i = 1; i < r; i++) {
if (jsoneq(JSON_STRING, &t[i], "user") == 0) {
/* We may use strndup() to fetch string value */
printf("- User: %.*s\n", t[i+1].end-t[i+1].start,
JSON_STRING + t[i+1].start);
i++;
} else if (jsoneq(JSON_STRING, &t[i], "admin") == 0) {
/* We may additionally check if the value is either "true" or "false" */
printf("- Admin: %.*s\n", t[i+1].end-t[i+1].start,
JSON_STRING + t[i+1].start);
i++;
} else if (jsoneq(JSON_STRING, &t[i], "uid") == 0) {
/* We may want to do strtol() here to get numeric value */
printf("- UID: %.*s\n", t[i+1].end-t[i+1].start,
JSON_STRING + t[i+1].start);
i++;
} else if (jsoneq(JSON_STRING, &t[i], "groups") == 0) {
int j;
printf("- Groups:\n");
if (t[i+1].type != JSMN_ARRAY) {
continue; /* We expect groups to be an array of strings */
}
for (j = 0; j < t[i+1].size; j++) {
jsmntok_t *g = &t[i+j+2];
printf(" * %.*s\n", g->end - g->start, JSON_STRING + g->start);
}
i += t[i+1].size + 1;
} else {
printf("Unexpected key: %.*s\n", t[i].end-t[i].start,
JSON_STRING + t[i].start);
}
}
return EXIT_SUCCESS;
}

314
modules/3rd/jsmn/jsmn.c Executable file
View File

@ -0,0 +1,314 @@
#include "jsmn.h"
/**
* Allocates a fresh unused token from the token pull.
*/
static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser,
jsmntok_t *tokens, size_t num_tokens) {
jsmntok_t *tok;
if (parser->toknext >= num_tokens) {
return NULL;
}
tok = &tokens[parser->toknext++];
tok->start = tok->end = -1;
tok->size = 0;
#ifdef JSMN_PARENT_LINKS
tok->parent = -1;
#endif
return tok;
}
/**
* Fills token type and boundaries.
*/
static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type,
int start, int end) {
token->type = type;
token->start = start;
token->end = end;
token->size = 0;
}
/**
* Fills next available token with JSON primitive.
*/
static int jsmn_parse_primitive(jsmn_parser *parser, const char *js,
size_t len, jsmntok_t *tokens, size_t num_tokens) {
jsmntok_t *token;
int start;
start = parser->pos;
for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
switch (js[parser->pos]) {
#ifndef JSMN_STRICT
/* In strict mode primitive must be followed by "," or "}" or "]" */
case ':':
#endif
case '\t' : case '\r' : case '\n' : case ' ' :
case ',' : case ']' : case '}' :
goto found;
}
if (js[parser->pos] < 32 || js[parser->pos] >= 127) {
parser->pos = start;
return JSMN_ERROR_INVAL;
}
}
#ifdef JSMN_STRICT
/* In strict mode primitive must be followed by a comma/object/array */
parser->pos = start;
return JSMN_ERROR_PART;
#endif
found:
if (tokens == NULL) {
parser->pos--;
return 0;
}
token = jsmn_alloc_token(parser, tokens, num_tokens);
if (token == NULL) {
parser->pos = start;
return JSMN_ERROR_NOMEM;
}
jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos);
#ifdef JSMN_PARENT_LINKS
token->parent = parser->toksuper;
#endif
parser->pos--;
return 0;
}
/**
* Fills next token with JSON string.
*/
static int jsmn_parse_string(jsmn_parser *parser, const char *js,
size_t len, jsmntok_t *tokens, size_t num_tokens) {
jsmntok_t *token;
int start = parser->pos;
parser->pos++;
/* Skip starting quote */
for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
char c = js[parser->pos];
/* Quote: end of string */
if (c == '\"') {
if (tokens == NULL) {
return 0;
}
token = jsmn_alloc_token(parser, tokens, num_tokens);
if (token == NULL) {
parser->pos = start;
return JSMN_ERROR_NOMEM;
}
jsmn_fill_token(token, JSMN_STRING, start+1, parser->pos);
#ifdef JSMN_PARENT_LINKS
token->parent = parser->toksuper;
#endif
return 0;
}
/* Backslash: Quoted symbol expected */
if (c == '\\' && parser->pos + 1 < len) {
int i;
parser->pos++;
switch (js[parser->pos]) {
/* Allowed escaped symbols */
case '\"': case '/' : case '\\' : case 'b' :
case 'f' : case 'r' : case 'n' : case 't' :
break;
/* Allows escaped symbol \uXXXX */
case 'u':
parser->pos++;
for(i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0'; i++) {
/* If it isn't a hex character we have an error */
if(!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */
(js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */
(js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */
parser->pos = start;
return JSMN_ERROR_INVAL;
}
parser->pos++;
}
parser->pos--;
break;
/* Unexpected symbol */
default:
parser->pos = start;
return JSMN_ERROR_INVAL;
}
}
}
parser->pos = start;
return JSMN_ERROR_PART;
}
/**
* Parse JSON string and fill tokens.
*/
int jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
jsmntok_t *tokens, unsigned int num_tokens) {
int r;
int i;
jsmntok_t *token;
int count = parser->toknext;
for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
char c;
jsmntype_t type;
c = js[parser->pos];
switch (c) {
case '{': case '[':
count++;
if (tokens == NULL) {
break;
}
token = jsmn_alloc_token(parser, tokens, num_tokens);
if (token == NULL)
return JSMN_ERROR_NOMEM;
if (parser->toksuper != -1) {
tokens[parser->toksuper].size++;
#ifdef JSMN_PARENT_LINKS
token->parent = parser->toksuper;
#endif
}
token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY);
token->start = parser->pos;
parser->toksuper = parser->toknext - 1;
break;
case '}': case ']':
if (tokens == NULL)
break;
type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY);
#ifdef JSMN_PARENT_LINKS
if (parser->toknext < 1) {
return JSMN_ERROR_INVAL;
}
token = &tokens[parser->toknext - 1];
for (;;) {
if (token->start != -1 && token->end == -1) {
if (token->type != type) {
return JSMN_ERROR_INVAL;
}
token->end = parser->pos + 1;
parser->toksuper = token->parent;
break;
}
if (token->parent == -1) {
if(token->type != type || parser->toksuper == -1) {
return JSMN_ERROR_INVAL;
}
break;
}
token = &tokens[token->parent];
}
#else
for (i = parser->toknext - 1; i >= 0; i--) {
token = &tokens[i];
if (token->start != -1 && token->end == -1) {
if (token->type != type) {
return JSMN_ERROR_INVAL;
}
parser->toksuper = -1;
token->end = parser->pos + 1;
break;
}
}
/* Error if unmatched closing bracket */
if (i == -1) return JSMN_ERROR_INVAL;
for (; i >= 0; i--) {
token = &tokens[i];
if (token->start != -1 && token->end == -1) {
parser->toksuper = i;
break;
}
}
#endif
break;
case '\"':
r = jsmn_parse_string(parser, js, len, tokens, num_tokens);
if (r < 0) return r;
count++;
if (parser->toksuper != -1 && tokens != NULL)
tokens[parser->toksuper].size++;
break;
case '\t' : case '\r' : case '\n' : case ' ':
break;
case ':':
parser->toksuper = parser->toknext - 1;
break;
case ',':
if (tokens != NULL && parser->toksuper != -1 &&
tokens[parser->toksuper].type != JSMN_ARRAY &&
tokens[parser->toksuper].type != JSMN_OBJECT) {
#ifdef JSMN_PARENT_LINKS
parser->toksuper = tokens[parser->toksuper].parent;
#else
for (i = parser->toknext - 1; i >= 0; i--) {
if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) {
if (tokens[i].start != -1 && tokens[i].end == -1) {
parser->toksuper = i;
break;
}
}
}
#endif
}
break;
#ifdef JSMN_STRICT
/* In strict mode primitives are: numbers and booleans */
case '-': case '0': case '1' : case '2': case '3' : case '4':
case '5': case '6': case '7' : case '8': case '9':
case 't': case 'f': case 'n' :
/* And they must not be keys of the object */
if (tokens != NULL && parser->toksuper != -1) {
jsmntok_t *t = &tokens[parser->toksuper];
if (t->type == JSMN_OBJECT ||
(t->type == JSMN_STRING && t->size != 0)) {
return JSMN_ERROR_INVAL;
}
}
#else
/* In non-strict mode every unquoted value is a primitive */
default:
#endif
r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens);
if (r < 0) return r;
count++;
if (parser->toksuper != -1 && tokens != NULL)
tokens[parser->toksuper].size++;
break;
#ifdef JSMN_STRICT
/* Unexpected char in strict mode */
default:
return JSMN_ERROR_INVAL;
#endif
}
}
if (tokens != NULL) {
for (i = parser->toknext - 1; i >= 0; i--) {
/* Unmatched opened object or array */
if (tokens[i].start != -1 && tokens[i].end == -1) {
return JSMN_ERROR_PART;
}
}
}
return count;
}
/**
* Creates a new parser based over a given buffer with an array of tokens
* available.
*/
void jsmn_init(jsmn_parser *parser) {
parser->pos = 0;
parser->toknext = 0;
parser->toksuper = -1;
}

76
modules/3rd/jsmn/jsmn.h Executable file
View File

@ -0,0 +1,76 @@
#ifndef __JSMN_H_
#define __JSMN_H_
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* JSON type identifier. Basic types are:
* o Object
* o Array
* o String
* o Other primitive: number, boolean (true/false) or null
*/
typedef enum {
JSMN_UNDEFINED = 0,
JSMN_OBJECT = 1,
JSMN_ARRAY = 2,
JSMN_STRING = 3,
JSMN_PRIMITIVE = 4
} jsmntype_t;
enum jsmnerr {
/* Not enough tokens were provided */
JSMN_ERROR_NOMEM = -1,
/* Invalid character inside JSON string */
JSMN_ERROR_INVAL = -2,
/* The string is not a full JSON packet, more bytes expected */
JSMN_ERROR_PART = -3
};
/**
* JSON token description.
* type type (object, array, string etc.)
* start start position in JSON data string
* end end position in JSON data string
*/
typedef struct {
jsmntype_t type;
int start;
int end;
int size;
#ifdef JSMN_PARENT_LINKS
int parent;
#endif
} jsmntok_t;
/**
* JSON parser. Contains an array of token blocks available. Also stores
* the string being parsed now and current position in that string
*/
typedef struct {
unsigned int pos; /* offset in the JSON string */
unsigned int toknext; /* next token to allocate */
int toksuper; /* superior token node, e.g parent object or array */
} jsmn_parser;
/**
* Create JSON parser over an array of tokens
*/
void jsmn_init(jsmn_parser *parser);
/**
* Run JSON parser. It parses a JSON data string into and array of tokens, each describing
* a single JSON object.
*/
int jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
jsmntok_t *tokens, unsigned int num_tokens);
#ifdef __cplusplus
}
#endif
#endif /* __JSMN_H_ */

16
modules/3rd/jsmn/library.json Executable file
View File

@ -0,0 +1,16 @@
{
"name": "jsmn",
"keywords": "json",
"description": "Minimalistic JSON parser/tokenizer in C. It can be easily integrated into resource-limited or embedded projects",
"repository":
{
"type": "git",
"url": "https://github.com/zserge/jsmn.git"
},
"frameworks": "*",
"platforms": "*",
"examples": [
"example/*.c"
],
"exclude": "test"
}

27
modules/3rd/jsmn/test/test.h Executable file
View File

@ -0,0 +1,27 @@
#ifndef __TEST_H__
#define __TEST_H__
static int test_passed = 0;
static int test_failed = 0;
/* Terminate current test with error */
#define fail() return __LINE__
/* Successful end of the test case */
#define done() return 0
/* Check single condition */
#define check(cond) do { if (!(cond)) fail(); } while (0)
/* Test runner */
static void test(int (*func)(void), const char *name) {
int r = func();
if (r == 0) {
test_passed++;
} else {
test_failed++;
printf("FAILED: %s (at line %d)\n", name, r);
}
}
#endif /* __TEST_H__ */

398
modules/3rd/jsmn/test/tests.c Executable file
View File

@ -0,0 +1,398 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include "test.h"
#include "testutil.h"
int test_empty(void) {
check(parse("{}", 1, 1,
JSMN_OBJECT, 0, 2, 0));
check(parse("[]", 1, 1,
JSMN_ARRAY, 0, 2, 0));
check(parse("[{},{}]", 3, 3,
JSMN_ARRAY, 0, 7, 2,
JSMN_OBJECT, 1, 3, 0,
JSMN_OBJECT, 4, 6, 0));
return 0;
}
int test_object(void) {
check(parse("{\"a\":0}", 3, 3,
JSMN_OBJECT, 0, 7, 1,
JSMN_STRING, "a", 1,
JSMN_PRIMITIVE, "0"));
check(parse("{\"a\":[]}", 3, 3,
JSMN_OBJECT, 0, 8, 1,
JSMN_STRING, "a", 1,
JSMN_ARRAY, 5, 7, 0));
check(parse("{\"a\":{},\"b\":{}}", 5, 5,
JSMN_OBJECT, -1, -1, 2,
JSMN_STRING, "a", 1,
JSMN_OBJECT, -1, -1, 0,
JSMN_STRING, "b", 1,
JSMN_OBJECT, -1, -1, 0));
check(parse("{\n \"Day\": 26,\n \"Month\": 9,\n \"Year\": 12\n }", 7, 7,
JSMN_OBJECT, -1, -1, 3,
JSMN_STRING, "Day", 1,
JSMN_PRIMITIVE, "26",
JSMN_STRING, "Month", 1,
JSMN_PRIMITIVE, "9",
JSMN_STRING, "Year", 1,
JSMN_PRIMITIVE, "12"));
check(parse("{\"a\": 0, \"b\": \"c\"}", 5, 5,
JSMN_OBJECT, -1, -1, 2,
JSMN_STRING, "a", 1,
JSMN_PRIMITIVE, "0",
JSMN_STRING, "b", 1,
JSMN_STRING, "c", 0));
#ifdef JSMN_STRICT
check(parse("{\"a\"\n0}", JSMN_ERROR_INVAL, 3));
check(parse("{\"a\", 0}", JSMN_ERROR_INVAL, 3));
check(parse("{\"a\": {2}}", JSMN_ERROR_INVAL, 3));
check(parse("{\"a\": {2: 3}}", JSMN_ERROR_INVAL, 3));
check(parse("{\"a\": {\"a\": 2 3}}", JSMN_ERROR_INVAL, 5));
/* FIXME */
/*check(parse("{\"a\"}", JSMN_ERROR_INVAL, 2));*/
/*check(parse("{\"a\": 1, \"b\"}", JSMN_ERROR_INVAL, 4));*/
/*check(parse("{\"a\",\"b\":1}", JSMN_ERROR_INVAL, 4));*/
/*check(parse("{\"a\":1,}", JSMN_ERROR_INVAL, 4));*/
/*check(parse("{\"a\":\"b\":\"c\"}", JSMN_ERROR_INVAL, 4));*/
/*check(parse("{,}", JSMN_ERROR_INVAL, 4));*/
#endif
return 0;
}
int test_array(void) {
/* FIXME */
/*check(parse("[10}", JSMN_ERROR_INVAL, 3));*/
/*check(parse("[1,,3]", JSMN_ERROR_INVAL, 3)*/
check(parse("[10]", 2, 2,
JSMN_ARRAY, -1, -1, 1,
JSMN_PRIMITIVE, "10"));
check(parse("{\"a\": 1]", JSMN_ERROR_INVAL, 3));
/* FIXME */
/*check(parse("[\"a\": 1]", JSMN_ERROR_INVAL, 3));*/
return 0;
}
int test_primitive(void) {
check(parse("{\"boolVar\" : true }", 3, 3,
JSMN_OBJECT, -1, -1, 1,
JSMN_STRING, "boolVar", 1,
JSMN_PRIMITIVE, "true"));
check(parse("{\"boolVar\" : false }", 3, 3,
JSMN_OBJECT, -1, -1, 1,
JSMN_STRING, "boolVar", 1,
JSMN_PRIMITIVE, "false"));
check(parse("{\"nullVar\" : null }", 3, 3,
JSMN_OBJECT, -1, -1, 1,
JSMN_STRING, "nullVar", 1,
JSMN_PRIMITIVE, "null"));
check(parse("{\"intVar\" : 12}", 3, 3,
JSMN_OBJECT, -1, -1, 1,
JSMN_STRING, "intVar", 1,
JSMN_PRIMITIVE, "12"));
check(parse("{\"floatVar\" : 12.345}", 3, 3,
JSMN_OBJECT, -1, -1, 1,
JSMN_STRING, "floatVar", 1,
JSMN_PRIMITIVE, "12.345"));
return 0;
}
int test_string(void) {
check(parse("{\"strVar\" : \"hello world\"}", 3, 3,
JSMN_OBJECT, -1, -1, 1,
JSMN_STRING, "strVar", 1,
JSMN_STRING, "hello world", 0));
check(parse("{\"strVar\" : \"escapes: \\/\\r\\n\\t\\b\\f\\\"\\\\\"}", 3, 3,
JSMN_OBJECT, -1, -1, 1,
JSMN_STRING, "strVar", 1,
JSMN_STRING, "escapes: \\/\\r\\n\\t\\b\\f\\\"\\\\", 0));
check(parse("{\"strVar\": \"\"}", 3, 3,
JSMN_OBJECT, -1, -1, 1,
JSMN_STRING, "strVar", 1,
JSMN_STRING, "", 0));
check(parse("{\"a\":\"\\uAbcD\"}", 3, 3,
JSMN_OBJECT, -1, -1, 1,
JSMN_STRING, "a", 1,
JSMN_STRING, "\\uAbcD", 0));
check(parse("{\"a\":\"str\\u0000\"}", 3, 3,
JSMN_OBJECT, -1, -1, 1,
JSMN_STRING, "a", 1,
JSMN_STRING, "str\\u0000", 0));
check(parse("{\"a\":\"\\uFFFFstr\"}", 3, 3,
JSMN_OBJECT, -1, -1, 1,
JSMN_STRING, "a", 1,
JSMN_STRING, "\\uFFFFstr", 0));
check(parse("{\"a\":[\"\\u0280\"]}", 4, 4,
JSMN_OBJECT, -1, -1, 1,
JSMN_STRING, "a", 1,
JSMN_ARRAY, -1, -1, 1,
JSMN_STRING, "\\u0280", 0));
check(parse("{\"a\":\"str\\uFFGFstr\"}", JSMN_ERROR_INVAL, 3));
check(parse("{\"a\":\"str\\u@FfF\"}", JSMN_ERROR_INVAL, 3));
check(parse("{{\"a\":[\"\\u028\"]}", JSMN_ERROR_INVAL, 4));
return 0;
}
int test_partial_string(void) {
int i;
int r;
jsmn_parser p;
jsmntok_t tok[5];
const char *js = "{\"x\": \"va\\\\ue\", \"y\": \"value y\"}";
jsmn_init(&p);
for (i = 1; i <= strlen(js); i++) {
r = jsmn_parse(&p, js, i, tok, sizeof(tok)/sizeof(tok[0]));
if (i == strlen(js)) {
check(r == 5);
check(tokeq(js, tok, 5,
JSMN_OBJECT, -1, -1, 2,
JSMN_STRING, "x", 1,
JSMN_STRING, "va\\\\ue", 0,
JSMN_STRING, "y", 1,
JSMN_STRING, "value y", 0));
} else {
check(r == JSMN_ERROR_PART);
}
}
return 0;
}
int test_partial_array(void) {
#ifdef JSMN_STRICT
int r;
int i;
jsmn_parser p;
jsmntok_t tok[10];
const char *js = "[ 1, true, [123, \"hello\"]]";
jsmn_init(&p);
for (i = 1; i <= strlen(js); i++) {
r = jsmn_parse(&p, js, i, tok, sizeof(tok)/sizeof(tok[0]));
if (i == strlen(js)) {
check(r == 6);
check(tokeq(js, tok, 6,
JSMN_ARRAY, -1, -1, 3,
JSMN_PRIMITIVE, "1",
JSMN_PRIMITIVE, "true",
JSMN_ARRAY, -1, -1, 2,
JSMN_PRIMITIVE, "123",
JSMN_STRING, "hello", 0));
} else {
check(r == JSMN_ERROR_PART);
}
}
#endif
return 0;
}
int test_array_nomem(void) {
int i;
int r;
jsmn_parser p;
jsmntok_t toksmall[10], toklarge[10];
const char *js;
js = " [ 1, true, [123, \"hello\"]]";
for (i = 0; i < 6; i++) {
jsmn_init(&p);
memset(toksmall, 0, sizeof(toksmall));
memset(toklarge, 0, sizeof(toklarge));
r = jsmn_parse(&p, js, strlen(js), toksmall, i);
check(r == JSMN_ERROR_NOMEM);
memcpy(toklarge, toksmall, sizeof(toksmall));
r = jsmn_parse(&p, js, strlen(js), toklarge, 10);
check(r >= 0);
check(tokeq(js, toklarge, 4,
JSMN_ARRAY, -1, -1, 3,
JSMN_PRIMITIVE, "1",
JSMN_PRIMITIVE, "true",
JSMN_ARRAY, -1, -1, 2,
JSMN_PRIMITIVE, "123",
JSMN_STRING, "hello", 0));
}
return 0;
}
int test_unquoted_keys(void) {
#ifndef JSMN_STRICT
int r;
jsmn_parser p;
jsmntok_t tok[10];
const char *js;
jsmn_init(&p);
js = "key1: \"value\"\nkey2 : 123";
r = jsmn_parse(&p, js, strlen(js), tok, 10);
check(r >= 0);
check(tokeq(js, tok, 4,
JSMN_PRIMITIVE, "key1",
JSMN_STRING, "value", 0,
JSMN_PRIMITIVE, "key2",
JSMN_PRIMITIVE, "123"));
#endif
return 0;
}
int test_issue_22(void) {
int r;
jsmn_parser p;
jsmntok_t tokens[128];
const char *js;
js = "{ \"height\":10, \"layers\":[ { \"data\":[6,6], \"height\":10, "
"\"name\":\"Calque de Tile 1\", \"opacity\":1, \"type\":\"tilelayer\", "
"\"visible\":true, \"width\":10, \"x\":0, \"y\":0 }], "
"\"orientation\":\"orthogonal\", \"properties\": { }, \"tileheight\":32, "
"\"tilesets\":[ { \"firstgid\":1, \"image\":\"..\\/images\\/tiles.png\", "
"\"imageheight\":64, \"imagewidth\":160, \"margin\":0, \"name\":\"Tiles\", "
"\"properties\":{}, \"spacing\":0, \"tileheight\":32, \"tilewidth\":32 }], "
"\"tilewidth\":32, \"version\":1, \"width\":10 }";
jsmn_init(&p);
r = jsmn_parse(&p, js, strlen(js), tokens, 128);
check(r >= 0);
return 0;
}
int test_issue_27(void) {
const char *js =
"{ \"name\" : \"Jack\", \"age\" : 27 } { \"name\" : \"Anna\", ";
check(parse(js, JSMN_ERROR_PART, 8));
return 0;
}
int test_input_length(void) {
const char *js;
int r;
jsmn_parser p;
jsmntok_t tokens[10];
js = "{\"a\": 0}garbage";
jsmn_init(&p);
r = jsmn_parse(&p, js, 8, tokens, 10);
check(r == 3);
check(tokeq(js, tokens, 3,
JSMN_OBJECT, -1, -1, 1,
JSMN_STRING, "a", 1,
JSMN_PRIMITIVE, "0"));
return 0;
}
int test_count(void) {
jsmn_parser p;
const char *js;
js = "{}";
jsmn_init(&p);
check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 1);
js = "[]";
jsmn_init(&p);
check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 1);
js = "[[]]";
jsmn_init(&p);
check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 2);
js = "[[], []]";
jsmn_init(&p);
check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 3);
js = "[[], []]";
jsmn_init(&p);
check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 3);
js = "[[], [[]], [[], []]]";
jsmn_init(&p);
check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 7);
js = "[\"a\", [[], []]]";
jsmn_init(&p);
check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 5);
js = "[[], \"[], [[]]\", [[]]]";
jsmn_init(&p);
check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 5);
js = "[1, 2, 3]";
jsmn_init(&p);
check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 4);
js = "[1, 2, [3, \"a\"], null]";
jsmn_init(&p);
check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 7);
return 0;
}
int test_nonstrict(void) {
#ifndef JSMN_STRICT
const char *js;
js = "a: 0garbage";
check(parse(js, 2, 2,
JSMN_PRIMITIVE, "a",
JSMN_PRIMITIVE, "0garbage"));
js = "Day : 26\nMonth : Sep\n\nYear: 12";
check(parse(js, 6, 6,
JSMN_PRIMITIVE, "Day",
JSMN_PRIMITIVE, "26",
JSMN_PRIMITIVE, "Month",
JSMN_PRIMITIVE, "Sep",
JSMN_PRIMITIVE, "Year",
JSMN_PRIMITIVE, "12"));
#endif
return 0;
}
int test_unmatched_brackets(void) {
const char *js;
js = "\"key 1\": 1234}";
check(parse(js, JSMN_ERROR_INVAL, 2));
js = "{\"key 1\": 1234";
check(parse(js, JSMN_ERROR_PART, 3));
js = "{\"key 1\": 1234}}";
check(parse(js, JSMN_ERROR_INVAL, 3));
js = "\"key 1\"}: 1234";
check(parse(js, JSMN_ERROR_INVAL, 3));
js = "\"key {1\": 1234";
check(parse(js, 2, 2,
JSMN_STRING, "key {1", 1,
JSMN_PRIMITIVE, "1234"));
js = "{{\"key 1\": 1234}";
check(parse(js, JSMN_ERROR_PART, 4));
return 0;
}
int main(void) {
test(test_empty, "test for a empty JSON objects/arrays");
test(test_object, "test for a JSON objects");
test(test_array, "test for a JSON arrays");
test(test_primitive, "test primitive JSON data types");
test(test_string, "test string JSON data types");
test(test_partial_string, "test partial JSON string parsing");
test(test_partial_array, "test partial array reading");
test(test_array_nomem, "test array reading with a smaller number of tokens");
test(test_unquoted_keys, "test unquoted keys (like in JavaScript)");
test(test_input_length, "test strings that are not null-terminated");
test(test_issue_22, "test issue #22");
test(test_issue_27, "test issue #27");
test(test_count, "test tokens count estimation");
test(test_nonstrict, "test for non-strict mode");
test(test_unmatched_brackets, "test for unmatched brackets");
printf("\nPASSED: %d\nFAILED: %d\n", test_passed, test_failed);
return (test_failed > 0);
}

View File

@ -0,0 +1,94 @@
#ifndef __TEST_UTIL_H__
#define __TEST_UTIL_H__
#include "../jsmn.c"
static int vtokeq(const char *s, jsmntok_t *t, int numtok, va_list ap) {
if (numtok > 0) {
int i, start, end, size;
int type;
char *value;
size = -1;
value = NULL;
for (i = 0; i < numtok; i++) {
type = va_arg(ap, int);
if (type == JSMN_STRING) {
value = va_arg(ap, char *);
size = va_arg(ap, int);
start = end = -1;
} else if (type == JSMN_PRIMITIVE) {
value = va_arg(ap, char *);
start = end = size = -1;
} else {
start = va_arg(ap, int);
end = va_arg(ap, int);
size = va_arg(ap, int);
value = NULL;
}
if (t[i].type != type) {
printf("token %d type is %d, not %d\n", i, t[i].type, type);
return 0;
}
if (start != -1 && end != -1) {
if (t[i].start != start) {
printf("token %d start is %d, not %d\n", i, t[i].start, start);
return 0;
}
if (t[i].end != end ) {
printf("token %d end is %d, not %d\n", i, t[i].end, end);
return 0;
}
}
if (size != -1 && t[i].size != size) {
printf("token %d size is %d, not %d\n", i, t[i].size, size);
return 0;
}
if (s != NULL && value != NULL) {
const char *p = s + t[i].start;
if (strlen(value) != t[i].end - t[i].start ||
strncmp(p, value, t[i].end - t[i].start) != 0) {
printf("token %d value is %.*s, not %s\n", i, t[i].end-t[i].start,
s+t[i].start, value);
return 0;
}
}
}
}
return 1;
}
static int tokeq(const char *s, jsmntok_t *tokens, int numtok, ...) {
int ok;
va_list args;
va_start(args, numtok);
ok = vtokeq(s, tokens, numtok, args);
va_end(args);
return ok;
}
static int parse(const char *s, int status, int numtok, ...) {
int r;
int ok = 1;
va_list args;
jsmn_parser p;
jsmntok_t *t = malloc(numtok * sizeof(jsmntok_t));
jsmn_init(&p);
r = jsmn_parse(&p, s, strlen(s), t, numtok);
if (r != status) {
printf("status is %d, not %d\n", r, status);
return 0;
}
if (status >= 0) {
va_start(args, numtok);
ok = vtokeq(s, t, numtok, args);
va_end(args);
}
free(t);
return ok;
}
#endif /* __TEST_UTIL_H__ */

2190
modules/3rd/md4c/entity.c Normal file

File diff suppressed because it is too large Load Diff

42
modules/3rd/md4c/entity.h Normal file
View File

@ -0,0 +1,42 @@
/*
* MD4C: Markdown parser for C
* (http://github.com/mity/md4c)
*
* Copyright (c) 2016-2019 Martin Mitas
*
* 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.
*/
#ifndef MD4C_ENTITY_H
#define MD4C_ENTITY_H
#include <stdlib.h>
/* Most entities are formed by single Unicode codepoint, few by two codepoints.
* Single-codepoint entities have codepoints[1] set to zero. */
struct entity {
const char* name;
unsigned codepoints[2];
};
const struct entity* entity_lookup(const char* name, size_t name_size);
#endif /* MD4C_ENTITY_H */

View File

@ -0,0 +1,750 @@
/*
* MD4C: Markdown parser for C
* (http://github.com/mity/md4c)
*
* Copyright (c) 2016-2019 Martin Mitas
*
* 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.
*/
#include <stdio.h>
#include <string.h>
#include "md4c-html.h"
#include "entity.h"
static int hd_cnt[6] = {0, 0, 0, 0, 0, 0};
#if !defined(__STDC_VERSION__) || __STDC_VERSION__ < 199409L
/* C89/90 or old compilers in general may not understand "inline". */
#if defined __GNUC__
#define inline __inline__
#elif defined _MSC_VER
#define inline __inline
#else
#define inline
#endif
#endif
#ifdef _WIN32
#define snprintf _snprintf
#endif
typedef struct MD_HTML_tag MD_HTML;
struct MD_HTML_tag
{
void (*process_output)(const MD_CHAR *, MD_SIZE, void *);
void *userdata;
unsigned flags;
int image_nesting_level;
char escape_map[256];
};
#define NEED_HTML_ESC_FLAG 0x1
#define NEED_URL_ESC_FLAG 0x2
/*****************************************
*** HTML rendering helper functions ***
*****************************************/
#define ISDIGIT(ch) ('0' <= (ch) && (ch) <= '9')
#define ISLOWER(ch) ('a' <= (ch) && (ch) <= 'z')
#define ISUPPER(ch) ('A' <= (ch) && (ch) <= 'Z')
#define ISALNUM(ch) (ISLOWER(ch) || ISUPPER(ch) || ISDIGIT(ch))
static inline void
render_verbatim(MD_HTML *r, const MD_CHAR *text, MD_SIZE size)
{
r->process_output(text, size, r->userdata);
}
/* Keep this as a macro. Most compiler should then be smart enough to replace
* the strlen() call with a compile-time constant if the string is a C literal. */
#define RENDER_VERBATIM(r, verbatim) \
render_verbatim((r), (verbatim), (MD_SIZE)(strlen(verbatim)))
static void
render_html_escaped(MD_HTML *r, const MD_CHAR *data, MD_SIZE size)
{
MD_OFFSET beg = 0;
MD_OFFSET off = 0;
/* Some characters need to be escaped in normal HTML text. */
#define NEED_HTML_ESC(ch) (r->escape_map[(unsigned char)(ch)] & NEED_HTML_ESC_FLAG)
while (1)
{
/* Optimization: Use some loop unrolling. */
while (off + 3 < size && !NEED_HTML_ESC(data[off + 0]) && !NEED_HTML_ESC(data[off + 1]) && !NEED_HTML_ESC(data[off + 2]) && !NEED_HTML_ESC(data[off + 3]))
off += 4;
while (off < size && !NEED_HTML_ESC(data[off]))
off++;
if (off > beg)
render_verbatim(r, data + beg, off - beg);
if (off < size)
{
switch (data[off])
{
case '&':
RENDER_VERBATIM(r, "&amp;");
break;
case '<':
RENDER_VERBATIM(r, "&lt;");
break;
case '>':
RENDER_VERBATIM(r, "&gt;");
break;
case '"':
RENDER_VERBATIM(r, "&quot;");
break;
}
off++;
}
else
{
break;
}
beg = off;
}
}
static void
render_url_escaped(MD_HTML *r, const MD_CHAR *data, MD_SIZE size)
{
static const MD_CHAR hex_chars[] = "0123456789ABCDEF";
MD_OFFSET beg = 0;
MD_OFFSET off = 0;
/* Some characters need to be escaped in URL attributes. */
#define NEED_URL_ESC(ch) (r->escape_map[(unsigned char)(ch)] & NEED_URL_ESC_FLAG)
while (1)
{
while (off < size && !NEED_URL_ESC(data[off]))
off++;
if (off > beg)
render_verbatim(r, data + beg, off - beg);
if (off < size)
{
char hex[3];
switch (data[off])
{
case '&':
RENDER_VERBATIM(r, "&amp;");
break;
default:
hex[0] = '%';
hex[1] = hex_chars[((unsigned)data[off] >> 4) & 0xf];
hex[2] = hex_chars[((unsigned)data[off] >> 0) & 0xf];
render_verbatim(r, hex, 3);
break;
}
off++;
}
else
{
break;
}
beg = off;
}
}
static unsigned
hex_val(char ch)
{
if ('0' <= ch && ch <= '9')
return ch - '0';
if ('A' <= ch && ch <= 'Z')
return ch - 'A' + 10;
else
return ch - 'a' + 10;
}
static void
render_utf8_codepoint(MD_HTML *r, unsigned codepoint,
void (*fn_append)(MD_HTML *, const MD_CHAR *, MD_SIZE))
{
static const MD_CHAR utf8_replacement_char[] = {0xef, 0xbf, 0xbd};
unsigned char utf8[4];
size_t n;
if (codepoint <= 0x7f)
{
n = 1;
utf8[0] = codepoint;
}
else if (codepoint <= 0x7ff)
{
n = 2;
utf8[0] = 0xc0 | ((codepoint >> 6) & 0x1f);
utf8[1] = 0x80 + ((codepoint >> 0) & 0x3f);
}
else if (codepoint <= 0xffff)
{
n = 3;
utf8[0] = 0xe0 | ((codepoint >> 12) & 0xf);
utf8[1] = 0x80 + ((codepoint >> 6) & 0x3f);
utf8[2] = 0x80 + ((codepoint >> 0) & 0x3f);
}
else
{
n = 4;
utf8[0] = 0xf0 | ((codepoint >> 18) & 0x7);
utf8[1] = 0x80 + ((codepoint >> 12) & 0x3f);
utf8[2] = 0x80 + ((codepoint >> 6) & 0x3f);
utf8[3] = 0x80 + ((codepoint >> 0) & 0x3f);
}
if (0 < codepoint && codepoint <= 0x10ffff)
fn_append(r, (char *)utf8, n);
else
fn_append(r, utf8_replacement_char, 3);
}
/* Translate entity to its UTF-8 equivalent, or output the verbatim one
* if such entity is unknown (or if the translation is disabled). */
static void
render_entity(MD_HTML *r, const MD_CHAR *text, MD_SIZE size,
void (*fn_append)(MD_HTML *, const MD_CHAR *, MD_SIZE))
{
if (r->flags & MD_HTML_FLAG_VERBATIM_ENTITIES)
{
render_verbatim(r, text, size);
return;
}
/* We assume UTF-8 output is what is desired. */
if (size > 3 && text[1] == '#')
{
unsigned codepoint = 0;
if (text[2] == 'x' || text[2] == 'X')
{
/* Hexadecimal entity (e.g. "&#x1234abcd;")). */
MD_SIZE i;
for (i = 3; i < size - 1; i++)
codepoint = 16 * codepoint + hex_val(text[i]);
}
else
{
/* Decimal entity (e.g. "&1234;") */
MD_SIZE i;
for (i = 2; i < size - 1; i++)
codepoint = 10 * codepoint + (text[i] - '0');
}
render_utf8_codepoint(r, codepoint, fn_append);
return;
}
else
{
/* Named entity (e.g. "&nbsp;"). */
const struct entity *ent;
ent = entity_lookup(text, size);
if (ent != NULL)
{
render_utf8_codepoint(r, ent->codepoints[0], fn_append);
if (ent->codepoints[1])
render_utf8_codepoint(r, ent->codepoints[1], fn_append);
return;
}
}
fn_append(r, text, size);
}
static void
render_attribute(MD_HTML *r, const MD_ATTRIBUTE *attr,
void (*fn_append)(MD_HTML *, const MD_CHAR *, MD_SIZE))
{
int i;
for (i = 0; attr->substr_offsets[i] < attr->size; i++)
{
MD_TEXTTYPE type = attr->substr_types[i];
MD_OFFSET off = attr->substr_offsets[i];
MD_SIZE size = attr->substr_offsets[i + 1] - off;
const MD_CHAR *text = attr->text + off;
switch (type)
{
case MD_TEXT_NULLCHAR:
render_utf8_codepoint(r, 0x0000, render_verbatim);
break;
case MD_TEXT_ENTITY:
render_entity(r, text, size, fn_append);
break;
default:
fn_append(r, text, size);
break;
}
}
}
static void
render_open_ol_block(MD_HTML *r, const MD_BLOCK_OL_DETAIL *det)
{
char buf[64];
if (det->start == 1)
{
RENDER_VERBATIM(r, "<ol>\n");
return;
}
snprintf(buf, sizeof(buf), "<ol start=\"%u\">\n", det->start);
RENDER_VERBATIM(r, buf);
}
static void
render_open_li_block(MD_HTML *r, const MD_BLOCK_LI_DETAIL *det)
{
if (det->is_task)
{
RENDER_VERBATIM(r, "<li class=\"task-list-item\">"
"<input type=\"checkbox\" class=\"task-list-item-checkbox\" disabled");
if (det->task_mark == 'x' || det->task_mark == 'X')
RENDER_VERBATIM(r, " checked");
RENDER_VERBATIM(r, ">");
}
else
{
RENDER_VERBATIM(r, "<li>");
}
}
static void
render_open_code_block(MD_HTML *r, const MD_BLOCK_CODE_DETAIL *det)
{
RENDER_VERBATIM(r, "<pre><code");
/* If known, output the HTML 5 attribute class="language-LANGNAME". */
if (det->lang.text != NULL)
{
RENDER_VERBATIM(r, " class=\"language-");
render_attribute(r, &det->lang, render_html_escaped);
RENDER_VERBATIM(r, "\"");
}
RENDER_VERBATIM(r, ">");
}
static void
render_open_td_block(MD_HTML *r, const MD_CHAR *cell_type, const MD_BLOCK_TD_DETAIL *det)
{
RENDER_VERBATIM(r, "<");
RENDER_VERBATIM(r, cell_type);
switch (det->align)
{
case MD_ALIGN_LEFT:
RENDER_VERBATIM(r, " align=\"left\">");
break;
case MD_ALIGN_CENTER:
RENDER_VERBATIM(r, " align=\"center\">");
break;
case MD_ALIGN_RIGHT:
RENDER_VERBATIM(r, " align=\"right\">");
break;
default:
RENDER_VERBATIM(r, ">");
break;
}
}
static void
render_open_a_span(MD_HTML *r, const MD_SPAN_A_DETAIL *det)
{
RENDER_VERBATIM(r, "<a href=\"");
render_attribute(r, &det->href, render_url_escaped);
if (det->title.text != NULL)
{
RENDER_VERBATIM(r, "\" title=\"");
render_attribute(r, &det->title, render_html_escaped);
}
RENDER_VERBATIM(r, "\">");
}
static void
render_open_img_span(MD_HTML *r, const MD_SPAN_IMG_DETAIL *det)
{
RENDER_VERBATIM(r, "<img src=\"");
render_attribute(r, &det->src, render_url_escaped);
RENDER_VERBATIM(r, "\" alt=\"");
r->image_nesting_level++;
}
static void
render_close_img_span(MD_HTML *r, const MD_SPAN_IMG_DETAIL *det)
{
if (det->title.text != NULL)
{
RENDER_VERBATIM(r, "\" title=\"");
render_attribute(r, &det->title, render_html_escaped);
}
RENDER_VERBATIM(r, (r->flags & MD_HTML_FLAG_XHTML) ? "\" />" : "\">");
r->image_nesting_level--;
}
static void
render_open_wikilink_span(MD_HTML *r, const MD_SPAN_WIKILINK_DETAIL *det)
{
RENDER_VERBATIM(r, "<x-wikilink data-target=\"");
render_attribute(r, &det->target, render_html_escaped);
RENDER_VERBATIM(r, "\">");
}
/**************************************
*** HTML renderer implementation ***
**************************************/
static int
enter_block_callback(MD_BLOCKTYPE type, void *detail, void *userdata)
{
static const MD_CHAR *head[6] = {"<h1>", "<h2>", "<h3>", "<h4>", "<h5>", "<h6>"};
MD_HTML *r = (MD_HTML *)userdata;
char buf[32];
switch (type)
{
case MD_BLOCK_DOC: /* noop */
break;
case MD_BLOCK_QUOTE:
RENDER_VERBATIM(r, "<blockquote>\n");
break;
case MD_BLOCK_UL:
RENDER_VERBATIM(r, "<ul>\n");
break;
case MD_BLOCK_OL:
render_open_ol_block(r, (const MD_BLOCK_OL_DETAIL *)detail);
break;
case MD_BLOCK_LI:
render_open_li_block(r, (const MD_BLOCK_LI_DETAIL *)detail);
break;
case MD_BLOCK_HR:
RENDER_VERBATIM(r, (r->flags & MD_HTML_FLAG_XHTML) ? "<hr />\n" : "<hr>\n");
break;
case MD_BLOCK_H:
hd_cnt[((MD_BLOCK_H_DETAIL *)detail)->level - 1]++;
snprintf(buf, 32, "<a id=\"h%d_%d\"></a>", ((MD_BLOCK_H_DETAIL *)detail)->level, hd_cnt[((MD_BLOCK_H_DETAIL *)detail)->level - 1]);
RENDER_VERBATIM(r, buf);
RENDER_VERBATIM(r, head[((MD_BLOCK_H_DETAIL *)detail)->level - 1]);
break;
case MD_BLOCK_CODE:
render_open_code_block(r, (const MD_BLOCK_CODE_DETAIL *)detail);
break;
case MD_BLOCK_HTML: /* noop */
break;
case MD_BLOCK_P:
RENDER_VERBATIM(r, "<p>");
break;
case MD_BLOCK_TABLE:
RENDER_VERBATIM(r, "<table>\n");
break;
case MD_BLOCK_THEAD:
RENDER_VERBATIM(r, "<thead>\n");
break;
case MD_BLOCK_TBODY:
RENDER_VERBATIM(r, "<tbody>\n");
break;
case MD_BLOCK_TR:
RENDER_VERBATIM(r, "<tr>\n");
break;
case MD_BLOCK_TH:
render_open_td_block(r, "th", (MD_BLOCK_TD_DETAIL *)detail);
break;
case MD_BLOCK_TD:
render_open_td_block(r, "td", (MD_BLOCK_TD_DETAIL *)detail);
break;
}
return 0;
}
static int
leave_block_callback(MD_BLOCKTYPE type, void *detail, void *userdata)
{
static const MD_CHAR *head[6] = {"</h1>\n", "</h2>\n", "</h3>\n", "</h4>\n", "</h5>\n", "</h6>\n"};
MD_HTML *r = (MD_HTML *)userdata;
switch (type)
{
case MD_BLOCK_DOC: /*noop*/
break;
case MD_BLOCK_QUOTE:
RENDER_VERBATIM(r, "</blockquote>\n");
break;
case MD_BLOCK_UL:
RENDER_VERBATIM(r, "</ul>\n");
break;
case MD_BLOCK_OL:
RENDER_VERBATIM(r, "</ol>\n");
break;
case MD_BLOCK_LI:
RENDER_VERBATIM(r, "</li>\n");
break;
case MD_BLOCK_HR: /*noop*/
break;
case MD_BLOCK_H:
RENDER_VERBATIM(r, head[((MD_BLOCK_H_DETAIL *)detail)->level - 1]);
break;
case MD_BLOCK_CODE:
RENDER_VERBATIM(r, "</code></pre>\n");
break;
case MD_BLOCK_HTML: /* noop */
break;
case MD_BLOCK_P:
RENDER_VERBATIM(r, "</p>\n");
break;
case MD_BLOCK_TABLE:
RENDER_VERBATIM(r, "</table>\n");
break;
case MD_BLOCK_THEAD:
RENDER_VERBATIM(r, "</thead>\n");
break;
case MD_BLOCK_TBODY:
RENDER_VERBATIM(r, "</tbody>\n");
break;
case MD_BLOCK_TR:
RENDER_VERBATIM(r, "</tr>\n");
break;
case MD_BLOCK_TH:
RENDER_VERBATIM(r, "</th>\n");
break;
case MD_BLOCK_TD:
RENDER_VERBATIM(r, "</td>\n");
break;
}
return 0;
}
static int
enter_span_callback(MD_SPANTYPE type, void *detail, void *userdata)
{
MD_HTML *r = (MD_HTML *)userdata;
if (r->image_nesting_level > 0)
{
/* We are inside a Markdown image label. Markdown allows to use any
* emphasis and other rich contents in that context similarly as in
* any link label.
*
* However, unlike in the case of links (where that contents becomes
* contents of the <a>...</a> tag), in the case of images the contents
* is supposed to fall into the attribute alt: <img alt="...">.
*
* In that context we naturally cannot output nested HTML tags. So lets
* suppress them and only output the plain text (i.e. what falls into
* text() callback).
*
* This make-it-a-plain-text approach is the recommended practice by
* CommonMark specification (for HTML output).
*/
return 0;
}
switch (type)
{
case MD_SPAN_EM:
RENDER_VERBATIM(r, "<em>");
break;
case MD_SPAN_STRONG:
RENDER_VERBATIM(r, "<strong>");
break;
case MD_SPAN_U:
RENDER_VERBATIM(r, "<u>");
break;
case MD_SPAN_A:
render_open_a_span(r, (MD_SPAN_A_DETAIL *)detail);
break;
case MD_SPAN_IMG:
render_open_img_span(r, (MD_SPAN_IMG_DETAIL *)detail);
break;
case MD_SPAN_CODE:
RENDER_VERBATIM(r, "<code>");
break;
case MD_SPAN_DEL:
RENDER_VERBATIM(r, "<del>");
break;
case MD_SPAN_LATEXMATH:
RENDER_VERBATIM(r, "<x-equation>");
break;
case MD_SPAN_LATEXMATH_DISPLAY:
RENDER_VERBATIM(r, "<x-equation type=\"display\">");
break;
case MD_SPAN_WIKILINK:
render_open_wikilink_span(r, (MD_SPAN_WIKILINK_DETAIL *)detail);
break;
}
return 0;
}
static int
leave_span_callback(MD_SPANTYPE type, void *detail, void *userdata)
{
MD_HTML *r = (MD_HTML *)userdata;
if (r->image_nesting_level > 0)
{
/* Ditto as in enter_span_callback(), except we have to allow the
* end of the <img> tag. */
if (r->image_nesting_level == 1 && type == MD_SPAN_IMG)
render_close_img_span(r, (MD_SPAN_IMG_DETAIL *)detail);
return 0;
}
switch (type)
{
case MD_SPAN_EM:
RENDER_VERBATIM(r, "</em>");
break;
case MD_SPAN_STRONG:
RENDER_VERBATIM(r, "</strong>");
break;
case MD_SPAN_U:
RENDER_VERBATIM(r, "</u>");
break;
case MD_SPAN_A:
RENDER_VERBATIM(r, "</a>");
break;
case MD_SPAN_IMG: /*noop, handled above*/
break;
case MD_SPAN_CODE:
RENDER_VERBATIM(r, "</code>");
break;
case MD_SPAN_DEL:
RENDER_VERBATIM(r, "</del>");
break;
case MD_SPAN_LATEXMATH: /*fall through*/
case MD_SPAN_LATEXMATH_DISPLAY:
RENDER_VERBATIM(r, "</x-equation>");
break;
case MD_SPAN_WIKILINK:
RENDER_VERBATIM(r, "</x-wikilink>");
break;
}
return 0;
}
static int
text_callback(MD_TEXTTYPE type, const MD_CHAR *text, MD_SIZE size, void *userdata)
{
MD_HTML *r = (MD_HTML *)userdata;
switch (type)
{
case MD_TEXT_NULLCHAR:
render_utf8_codepoint(r, 0x0000, render_verbatim);
break;
case MD_TEXT_BR:
RENDER_VERBATIM(r, (r->image_nesting_level == 0
? ((r->flags & MD_HTML_FLAG_XHTML) ? "<br />\n" : "<br>\n")
: " "));
break;
case MD_TEXT_SOFTBR:
RENDER_VERBATIM(r, (r->image_nesting_level == 0 ? "\n" : " "));
break;
case MD_TEXT_HTML:
render_verbatim(r, text, size);
break;
case MD_TEXT_ENTITY:
render_entity(r, text, size, render_html_escaped);
break;
default:
render_html_escaped(r, text, size);
break;
}
return 0;
}
static void
debug_log_callback(const char *msg, void *userdata)
{
MD_HTML *r = (MD_HTML *)userdata;
if (r->flags & MD_HTML_FLAG_DEBUG)
fprintf(stderr, "MD4C: %s\n", msg);
}
int md_html(const MD_CHAR *input, MD_SIZE input_size,
void (*process_output)(const MD_CHAR *, MD_SIZE, void *),
void *userdata, unsigned parser_flags, unsigned renderer_flags)
{
MD_HTML render = {process_output, userdata, renderer_flags, 0, {0}};
int i;
MD_PARSER parser = {
0,
parser_flags,
enter_block_callback,
leave_block_callback,
enter_span_callback,
leave_span_callback,
text_callback,
debug_log_callback,
NULL};
/* Build map of characters which need escaping. */
for (i = 0; i < 256; i++)
{
unsigned char ch = (unsigned char)i;
if (strchr("\"&<>", ch) != NULL)
render.escape_map[i] |= NEED_HTML_ESC_FLAG;
if (!ISALNUM(ch) && strchr("-_.+!*(),%#@?=;:/,+$", ch) == NULL)
render.escape_map[i] |= NEED_URL_ESC_FLAG;
}
/* Consider skipping UTF-8 byte order mark (BOM). */
if (renderer_flags & MD_HTML_FLAG_SKIP_UTF8_BOM && sizeof(MD_CHAR) == 1)
{
static const MD_CHAR bom[3] = {0xef, 0xbb, 0xbf};
if (input_size >= sizeof(bom) && memcmp(input, bom, sizeof(bom)) == 0)
{
input += sizeof(bom);
input_size -= sizeof(bom);
}
}
return md_parse(input, input_size, &parser, (void *)&render);
}
void reset_hd_cnt()
{
for (size_t i = 0; i < 6; i++)
{
hd_cnt[i] = 0;
}
}

View File

@ -0,0 +1,70 @@
/*
* MD4C: Markdown parser for C
* (http://github.com/mity/md4c)
*
* Copyright (c) 2016-2017 Martin Mitas
*
* 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.
*/
#ifndef MD4C_HTML_H
#define MD4C_HTML_H
#include "md4c.h"
#ifdef __cplusplus
extern "C" {
#endif
/* If set, debug output from md_parse() is sent to stderr. */
#define MD_HTML_FLAG_DEBUG 0x0001
#define MD_HTML_FLAG_VERBATIM_ENTITIES 0x0002
#define MD_HTML_FLAG_SKIP_UTF8_BOM 0x0004
#define MD_HTML_FLAG_XHTML 0x0008
/* Render Markdown into HTML.
*
* Note only contents of <body> tag is generated. Caller must generate
* HTML header/footer manually before/after calling md_html().
*
* Params input and input_size specify the Markdown input.
* Callback process_output() gets called with chunks of HTML output.
* (Typical implementation may just output the bytes to a file or append to
* some buffer).
* Param userdata is just propgated back to process_output() callback.
* Param parser_flags are flags from md4c.h propagated to md_parse().
* Param render_flags is bitmask of MD_HTML_FLAG_xxxx.
*
* Returns -1 on error (if md_parse() fails.)
* Returns 0 on success.
*/
int md_html(const MD_CHAR* input, MD_SIZE input_size,
void (*process_output)(const MD_CHAR*, MD_SIZE, void*),
void* userdata, unsigned parser_flags, unsigned renderer_flags);
void reset_hd_cnt();
#ifdef __cplusplus
} /* extern "C" { */
#endif
#endif /* MD4C_HTML_H */

6306
modules/3rd/md4c/md4c.c Normal file

File diff suppressed because it is too large Load Diff

397
modules/3rd/md4c/md4c.h Normal file
View File

@ -0,0 +1,397 @@
/*
* MD4C: Markdown parser for C
* (http://github.com/mity/md4c)
*
* Copyright (c) 2016-2020 Martin Mitas
*
* 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.
*/
#ifndef MD4C_H
#define MD4C_H
#ifdef __cplusplus
extern "C" {
#endif
#if defined MD4C_USE_UTF16
/* Magic to support UTF-16. Note that in order to use it, you have to define
* the macro MD4C_USE_UTF16 both when building MD4C as well as when
* including this header in your code. */
#ifdef _WIN32
#include <windows.h>
typedef WCHAR MD_CHAR;
#else
#error MD4C_USE_UTF16 is only supported on Windows.
#endif
#else
typedef char MD_CHAR;
#endif
typedef unsigned MD_SIZE;
typedef unsigned MD_OFFSET;
/* Block represents a part of document hierarchy structure like a paragraph
* or list item.
*/
typedef enum MD_BLOCKTYPE {
/* <body>...</body> */
MD_BLOCK_DOC = 0,
/* <blockquote>...</blockquote> */
MD_BLOCK_QUOTE,
/* <ul>...</ul>
* Detail: Structure MD_BLOCK_UL_DETAIL. */
MD_BLOCK_UL,
/* <ol>...</ol>
* Detail: Structure MD_BLOCK_OL_DETAIL. */
MD_BLOCK_OL,
/* <li>...</li>
* Detail: Structure MD_BLOCK_LI_DETAIL. */
MD_BLOCK_LI,
/* <hr> */
MD_BLOCK_HR,
/* <h1>...</h1> (for levels up to 6)
* Detail: Structure MD_BLOCK_H_DETAIL. */
MD_BLOCK_H,
/* <pre><code>...</code></pre>
* Note the text lines within code blocks are terminated with '\n'
* instead of explicit MD_TEXT_BR. */
MD_BLOCK_CODE,
/* Raw HTML block. This itself does not correspond to any particular HTML
* tag. The contents of it _is_ raw HTML source intended to be put
* in verbatim form to the HTML output. */
MD_BLOCK_HTML,
/* <p>...</p> */
MD_BLOCK_P,
/* <table>...</table> and its contents.
* Detail: Structure MD_BLOCK_TD_DETAIL (used with MD_BLOCK_TH and MD_BLOCK_TD)
* Note all of these are used only if extension MD_FLAG_TABLES is enabled. */
MD_BLOCK_TABLE,
MD_BLOCK_THEAD,
MD_BLOCK_TBODY,
MD_BLOCK_TR,
MD_BLOCK_TH,
MD_BLOCK_TD
} MD_BLOCKTYPE;
/* Span represents an in-line piece of a document which should be rendered with
* the same font, color and other attributes. A sequence of spans forms a block
* like paragraph or list item. */
typedef enum MD_SPANTYPE {
/* <em>...</em> */
MD_SPAN_EM,
/* <strong>...</strong> */
MD_SPAN_STRONG,
/* <a href="xxx">...</a>
* Detail: Structure MD_SPAN_A_DETAIL. */
MD_SPAN_A,
/* <img src="xxx">...</a>
* Detail: Structure MD_SPAN_IMG_DETAIL.
* Note: Image text can contain nested spans and even nested images.
* If rendered into ALT attribute of HTML <IMG> tag, it's responsibility
* of the parser to deal with it.
*/
MD_SPAN_IMG,
/* <code>...</code> */
MD_SPAN_CODE,
/* <del>...</del>
* Note: Recognized only when MD_FLAG_STRIKETHROUGH is enabled.
*/
MD_SPAN_DEL,
/* For recognizing inline ($) and display ($$) equations
* Note: Recognized only when MD_FLAG_LATEXMATHSPANS is enabled.
*/
MD_SPAN_LATEXMATH,
MD_SPAN_LATEXMATH_DISPLAY,
/* Wiki links
* Note: Recognized only when MD_FLAG_WIKILINKS is enabled.
*/
MD_SPAN_WIKILINK,
/* <u>...</u>
* Note: Recognized only when MD_FLAG_UNDERLINE is enabled. */
MD_SPAN_U
} MD_SPANTYPE;
/* Text is the actual textual contents of span. */
typedef enum MD_TEXTTYPE {
/* Normal text. */
MD_TEXT_NORMAL = 0,
/* NULL character. CommonMark requires replacing NULL character with
* the replacement char U+FFFD, so this allows caller to do that easily. */
MD_TEXT_NULLCHAR,
/* Line breaks.
* Note these are not sent from blocks with verbatim output (MD_BLOCK_CODE
* or MD_BLOCK_HTML). In such cases, '\n' is part of the text itself. */
MD_TEXT_BR, /* <br> (hard break) */
MD_TEXT_SOFTBR, /* '\n' in source text where it is not semantically meaningful (soft break) */
/* Entity.
* (a) Named entity, e.g. &nbsp;
* (Note MD4C does not have a list of known entities.
* Anything matching the regexp /&[A-Za-z][A-Za-z0-9]{1,47};/ is
* treated as a named entity.)
* (b) Numerical entity, e.g. &#1234;
* (c) Hexadecimal entity, e.g. &#x12AB;
*
* As MD4C is mostly encoding agnostic, application gets the verbatim
* entity text into the MD_PARSER::text_callback(). */
MD_TEXT_ENTITY,
/* Text in a code block (inside MD_BLOCK_CODE) or inlined code (`code`).
* If it is inside MD_BLOCK_CODE, it includes spaces for indentation and
* '\n' for new lines. MD_TEXT_BR and MD_TEXT_SOFTBR are not sent for this
* kind of text. */
MD_TEXT_CODE,
/* Text is a raw HTML. If it is contents of a raw HTML block (i.e. not
* an inline raw HTML), then MD_TEXT_BR and MD_TEXT_SOFTBR are not used.
* The text contains verbatim '\n' for the new lines. */
MD_TEXT_HTML,
/* Text is inside an equation. This is processed the same way as inlined code
* spans (`code`). */
MD_TEXT_LATEXMATH
} MD_TEXTTYPE;
/* Alignment enumeration. */
typedef enum MD_ALIGN {
MD_ALIGN_DEFAULT = 0, /* When unspecified. */
MD_ALIGN_LEFT,
MD_ALIGN_CENTER,
MD_ALIGN_RIGHT
} MD_ALIGN;
/* String attribute.
*
* This wraps strings which are outside of a normal text flow and which are
* propagated within various detailed structures, but which still may contain
* string portions of different types like e.g. entities.
*
* So, for example, lets consider this image:
*
* ![image alt text](http://example.org/image.png 'foo &quot; bar')
*
* The image alt text is propagated as a normal text via the MD_PARSER::text()
* callback. However, the image title ('foo &quot; bar') is propagated as
* MD_ATTRIBUTE in MD_SPAN_IMG_DETAIL::title.
*
* Then the attribute MD_SPAN_IMG_DETAIL::title shall provide the following:
* -- [0]: "foo " (substr_types[0] == MD_TEXT_NORMAL; substr_offsets[0] == 0)
* -- [1]: "&quot;" (substr_types[1] == MD_TEXT_ENTITY; substr_offsets[1] == 4)
* -- [2]: " bar" (substr_types[2] == MD_TEXT_NORMAL; substr_offsets[2] == 10)
* -- [3]: (n/a) (n/a ; substr_offsets[3] == 14)
*
* Note that these invariants are always guaranteed:
* -- substr_offsets[0] == 0
* -- substr_offsets[LAST+1] == size
* -- Currently, only MD_TEXT_NORMAL, MD_TEXT_ENTITY, MD_TEXT_NULLCHAR
* substrings can appear. This could change only of the specification
* changes.
*/
typedef struct MD_ATTRIBUTE {
const MD_CHAR* text;
MD_SIZE size;
const MD_TEXTTYPE* substr_types;
const MD_OFFSET* substr_offsets;
} MD_ATTRIBUTE;
/* Detailed info for MD_BLOCK_UL. */
typedef struct MD_BLOCK_UL_DETAIL {
int is_tight; /* Non-zero if tight list, zero if loose. */
MD_CHAR mark; /* Item bullet character in MarkDown source of the list, e.g. '-', '+', '*'. */
} MD_BLOCK_UL_DETAIL;
/* Detailed info for MD_BLOCK_OL. */
typedef struct MD_BLOCK_OL_DETAIL {
unsigned start; /* Start index of the ordered list. */
int is_tight; /* Non-zero if tight list, zero if loose. */
MD_CHAR mark_delimiter; /* Character delimiting the item marks in MarkDown source, e.g. '.' or ')' */
} MD_BLOCK_OL_DETAIL;
/* Detailed info for MD_BLOCK_LI. */
typedef struct MD_BLOCK_LI_DETAIL {
int is_task; /* Can be non-zero only with MD_FLAG_TASKLISTS */
MD_CHAR task_mark; /* If is_task, then one of 'x', 'X' or ' '. Undefined otherwise. */
MD_OFFSET task_mark_offset; /* If is_task, then offset in the input of the char between '[' and ']'. */
} MD_BLOCK_LI_DETAIL;
/* Detailed info for MD_BLOCK_H. */
typedef struct MD_BLOCK_H_DETAIL {
unsigned level; /* Header level (1 - 6) */
} MD_BLOCK_H_DETAIL;
/* Detailed info for MD_BLOCK_CODE. */
typedef struct MD_BLOCK_CODE_DETAIL {
MD_ATTRIBUTE info;
MD_ATTRIBUTE lang;
MD_CHAR fence_char; /* The character used for fenced code block; or zero for indented code block. */
} MD_BLOCK_CODE_DETAIL;
/* Detailed info for MD_BLOCK_TH and MD_BLOCK_TD. */
typedef struct MD_BLOCK_TD_DETAIL {
MD_ALIGN align;
} MD_BLOCK_TD_DETAIL;
/* Detailed info for MD_SPAN_A. */
typedef struct MD_SPAN_A_DETAIL {
MD_ATTRIBUTE href;
MD_ATTRIBUTE title;
} MD_SPAN_A_DETAIL;
/* Detailed info for MD_SPAN_IMG. */
typedef struct MD_SPAN_IMG_DETAIL {
MD_ATTRIBUTE src;
MD_ATTRIBUTE title;
} MD_SPAN_IMG_DETAIL;
/* Detailed info for MD_SPAN_WIKILINK. */
typedef struct MD_SPAN_WIKILINK {
MD_ATTRIBUTE target;
} MD_SPAN_WIKILINK_DETAIL;
/* Flags specifying extensions/deviations from CommonMark specification.
*
* By default (when MD_PARSER::flags == 0), we follow CommonMark specification.
* The following flags may allow some extensions or deviations from it.
*/
#define MD_FLAG_COLLAPSEWHITESPACE 0x0001 /* In MD_TEXT_NORMAL, collapse non-trivial whitespace into single ' ' */
#define MD_FLAG_PERMISSIVEATXHEADERS 0x0002 /* Do not require space in ATX headers ( ###header ) */
#define MD_FLAG_PERMISSIVEURLAUTOLINKS 0x0004 /* Recognize URLs as autolinks even without '<', '>' */
#define MD_FLAG_PERMISSIVEEMAILAUTOLINKS 0x0008 /* Recognize e-mails as autolinks even without '<', '>' and 'mailto:' */
#define MD_FLAG_NOINDENTEDCODEBLOCKS 0x0010 /* Disable indented code blocks. (Only fenced code works.) */
#define MD_FLAG_NOHTMLBLOCKS 0x0020 /* Disable raw HTML blocks. */
#define MD_FLAG_NOHTMLSPANS 0x0040 /* Disable raw HTML (inline). */
#define MD_FLAG_TABLES 0x0100 /* Enable tables extension. */
#define MD_FLAG_STRIKETHROUGH 0x0200 /* Enable strikethrough extension. */
#define MD_FLAG_PERMISSIVEWWWAUTOLINKS 0x0400 /* Enable WWW autolinks (even without any scheme prefix, if they begin with 'www.') */
#define MD_FLAG_TASKLISTS 0x0800 /* Enable task list extension. */
#define MD_FLAG_LATEXMATHSPANS 0x1000 /* Enable $ and $$ containing LaTeX equations. */
#define MD_FLAG_WIKILINKS 0x2000 /* Enable wiki links extension. */
#define MD_FLAG_UNDERLINE 0x4000 /* Enable underline extension (and disables '_' for normal emphasis). */
#define MD_FLAG_PERMISSIVEAUTOLINKS (MD_FLAG_PERMISSIVEEMAILAUTOLINKS | MD_FLAG_PERMISSIVEURLAUTOLINKS | MD_FLAG_PERMISSIVEWWWAUTOLINKS)
#define MD_FLAG_NOHTML (MD_FLAG_NOHTMLBLOCKS | MD_FLAG_NOHTMLSPANS)
/* Convenient sets of flags corresponding to well-known Markdown dialects.
*
* Note we may only support subset of features of the referred dialect.
* The constant just enables those extensions which bring us as close as
* possible given what features we implement.
*
* ABI compatibility note: Meaning of these can change in time as new
* extensions, bringing the dialect closer to the original, are implemented.
*/
#define MD_DIALECT_COMMONMARK 0
#define MD_DIALECT_GITHUB (MD_FLAG_PERMISSIVEAUTOLINKS | MD_FLAG_TABLES | MD_FLAG_STRIKETHROUGH | MD_FLAG_TASKLISTS)
/* Parser structure.
*/
typedef struct MD_PARSER {
/* Reserved. Set to zero.
*/
unsigned abi_version;
/* Dialect options. Bitmask of MD_FLAG_xxxx values.
*/
unsigned flags;
/* Caller-provided rendering callbacks.
*
* For some block/span types, more detailed information is provided in a
* type-specific structure pointed by the argument 'detail'.
*
* The last argument of all callbacks, 'userdata', is just propagated from
* md_parse() and is available for any use by the application.
*
* Note any strings provided to the callbacks as their arguments or as
* members of any detail structure are generally not zero-terminated.
* Application has to take the respective size information into account.
*
* Any rendering callback may abort further parsing of the document by
* returning non-zero.
*/
int (*enter_block)(MD_BLOCKTYPE /*type*/, void* /*detail*/, void* /*userdata*/);
int (*leave_block)(MD_BLOCKTYPE /*type*/, void* /*detail*/, void* /*userdata*/);
int (*enter_span)(MD_SPANTYPE /*type*/, void* /*detail*/, void* /*userdata*/);
int (*leave_span)(MD_SPANTYPE /*type*/, void* /*detail*/, void* /*userdata*/);
int (*text)(MD_TEXTTYPE /*type*/, const MD_CHAR* /*text*/, MD_SIZE /*size*/, void* /*userdata*/);
/* Debug callback. Optional (may be NULL).
*
* If provided and something goes wrong, this function gets called.
* This is intended for debugging and problem diagnosis for developers;
* it is not intended to provide any errors suitable for displaying to an
* end user.
*/
void (*debug_log)(const char* /*msg*/, void* /*userdata*/);
/* Reserved. Set to NULL.
*/
void (*syntax)(void);
} MD_PARSER;
/* For backward compatibility. Do not use in new code.
*/
typedef MD_PARSER MD_RENDERER;
/* Parse the Markdown document stored in the string 'text' of size 'size'.
* The parser provides callbacks to be called during the parsing so the
* caller can render the document on the screen or convert the Markdown
* to another format.
*
* Zero is returned on success. If a runtime error occurs (e.g. a memory
* fails), -1 is returned. If the processing is aborted due any callback
* returning non-zero, the return value of the callback is returned.
*/
int md_parse(const MD_CHAR* text, MD_SIZE size, const MD_PARSER* parser, void* userdata);
#ifdef __cplusplus
} /* extern "C" { */
#endif
#endif /* MD4C_H */

50
modules/3rd/zip/main.c Normal file
View File

@ -0,0 +1,50 @@
#include <stdio.h>
#include <string.h>
#include "zip.h"
// callback function
int on_extract_entry(const char *filename, void *arg) {
static int i = 0;
int n = *(int *)arg;
printf("Extracted: %s (%d of %d)\n", filename, ++i, n);
return 0;
}
int main(int argc, const char** argv) {
/*
Create a new zip archive with default compression level (6)
*/
/*
struct zip_t *zip = zip_open("foo.zip", ZIP_DEFAULT_COMPRESSION_LEVEL, 0);
// we should check if zip is NULL
{
zip_entry_open(zip, "foo-1.txt");
{
char *buf = "Some data here...";
zip_entry_write(zip, buf, strlen(buf));
}
zip_entry_close(zip);
zip_entry_open(zip, "foo-2.txt");
{
// merge 3 files into one entry and compress them on-the-fly.
zip_entry_fwrite(zip, "foo-2.1.txt");
zip_entry_fwrite(zip, "foo-2.2.txt");
zip_entry_fwrite(zip, "foo-2.3.txt");
}
zip_entry_close(zip);
}
// always remember to close and release resources
zip_close(zip);
*/
/*
Extract a zip archive into /tmp folder
*/
int arg = 5;
zip_extract(argv[1], "/Users/mrsang/Downloads/zip-master/src/tmp", on_extract_entry, &arg);
return 0;
}

4916
modules/3rd/zip/miniz.c Executable file

File diff suppressed because it is too large Load Diff

471
modules/3rd/zip/zip.c Executable file
View File

@ -0,0 +1,471 @@
/*
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 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.
*/
#include <sys/stat.h>
#include <errno.h>
#include "miniz.c"
#include "zip.h"
#if defined _WIN32 || defined __WIN32__ || defined __EMX__ || defined __DJGPP__
/* Win32, OS/2, DOS */
#define HAS_DEVICE(P) ((((P)[0] >= 'A' && (P)[0] <= 'Z') || ((P)[0] >= 'a' && (P)[0] <= 'z')) && (P)[1] == ':')
#define FILESYSTEM_PREFIX_LEN(P) (HAS_DEVICE (P) ? 2 : 0)
#define ISSLASH(C) ((C) == '/' || (C) == '\\')
#endif
#ifndef FILESYSTEM_PREFIX_LEN
#define FILESYSTEM_PREFIX_LEN(P) 0
#endif
#ifndef ISSLASH
#define ISSLASH(C) ((C) == '/')
#endif
#define cleanup(ptr) do { if (ptr) { free((void *)ptr); ptr = NULL; } } while (0)
#define strclone(ptr) ((ptr) ? strdup(ptr) : NULL)
char *basename(const char *name) {
char const *p;
char const *base = name += FILESYSTEM_PREFIX_LEN (name);
int all_slashes = 1;
for (p = name; *p; p++) {
if (ISSLASH(*p))
base = p + 1;
else
all_slashes = 0;
}
/* If NAME is all slashes, arrange to return `/'. */
if (*base == '\0' && ISSLASH(*name) && all_slashes)
--base;
return (char *)base;
}
static int mkpath(const char *path) {
const int mode = 0755;
char const *p;
char npath[MAX_PATH + 1] = { 0 };
int len = 0;
for (p = path; *p && len < MAX_PATH; p++) {
if (ISSLASH(*p) && len > 0) {
if (mkdir(npath, mode) == -1)
if (errno != EEXIST) return -1;
}
npath[len++] = *p;
}
return 0;
}
struct zip_entry_t {
const char *name;
mz_uint64 uncomp_size;
mz_uint64 comp_size;
mz_uint32 uncomp_crc32;
mz_uint64 offset;
mz_uint8 header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE];
mz_uint64 header_offset;
mz_uint16 method;
mz_zip_writer_add_state state;
tdefl_compressor comp;
};
struct zip_t {
mz_zip_archive archive;
mz_uint level;
struct zip_entry_t entry;
};
struct zip_t *zip_open(const char *zipname, int level, int append) {
struct zip_t *zip = NULL;
struct MZ_FILE_STAT_STRUCT fstat;
if (!zipname || strlen(zipname) < 1) {
// zip_t archive name is empty or NULL
return NULL;
}
if (level < 0) level = MZ_DEFAULT_LEVEL;
if ((level & 0xF) > MZ_UBER_COMPRESSION) {
// Wrong compression level
return NULL;
}
zip = (struct zip_t *)calloc((size_t)1, sizeof(struct zip_t));
if (zip) {
zip->level = level;
if (append && MZ_FILE_STAT(zipname, &fstat) == 0) {
// Append to an existing archive.
if (!mz_zip_reader_init_file(&(zip->archive), zipname, level | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY)) {
cleanup(zip);
return NULL;
}
if (!mz_zip_writer_init_from_reader(&(zip->archive), zipname)) {
mz_zip_reader_end(&(zip->archive));
cleanup(zip);
return NULL;
}
} else {
// Create a new archive.
if (!mz_zip_writer_init_file(&(zip->archive), zipname, 0)) {
// Cannot initialize zip_archive writer
cleanup(zip);
return NULL;
}
}
}
return zip;
}
void zip_close(struct zip_t *zip) {
if (zip) {
// Always finalize, even if adding failed for some reason, so we have a valid central directory.
mz_zip_writer_finalize_archive(&(zip->archive));
mz_zip_writer_end(&(zip->archive));
cleanup(zip);
}
}
int zip_entry_open(struct zip_t *zip, const char *entryname) {
size_t entrylen = 0;
mz_zip_archive *pzip = NULL;
mz_uint num_alignment_padding_bytes, level;
if (!zip || !entryname) {
return -1;
}
entrylen = strlen(entryname);
if (entrylen < 1) {
return -1;
}
zip->entry.name = strclone(entryname);
if (!zip->entry.name) {
// Cannot parse zip entry name
return -1;
}
zip->entry.comp_size = 0;
zip->entry.uncomp_size = 0;
zip->entry.uncomp_crc32 = MZ_CRC32_INIT;
zip->entry.offset = zip->archive.m_archive_size;
zip->entry.header_offset = zip->archive.m_archive_size;
memset(zip->entry.header, 0, MZ_ZIP_LOCAL_DIR_HEADER_SIZE * sizeof(mz_uint8));
zip->entry.method = 0;
pzip = &(zip->archive);
num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pzip);
if (!pzip->m_pState || (pzip->m_zip_mode != MZ_ZIP_MODE_WRITING)) {
// Wrong zip mode
return -1;
}
if (zip->level & MZ_ZIP_FLAG_COMPRESSED_DATA) {
// Wrong zip compression level
return -1;
}
// no zip64 support yet
if ((pzip->m_total_files == 0xFFFF) || ((pzip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + entrylen) > 0xFFFFFFFF)) {
// No zip64 support yet
return -1;
}
if (!mz_zip_writer_write_zeros(pzip, zip->entry.offset, num_alignment_padding_bytes + sizeof(zip->entry.header))) {
// Cannot memset zip entry header
return -1;
}
zip->entry.header_offset += num_alignment_padding_bytes;
if (pzip->m_file_offset_alignment) { MZ_ASSERT((zip->entry.header_offset & (pzip->m_file_offset_alignment - 1)) == 0); }
zip->entry.offset += num_alignment_padding_bytes + sizeof(zip->entry.header);
if (pzip->m_pWrite(pzip->m_pIO_opaque, zip->entry.offset, zip->entry.name, entrylen) != entrylen) {
// Cannot write data to zip entry
return -1;
}
zip->entry.offset += entrylen;
level = zip->level & 0xF;
if (level) {
zip->entry.state.m_pZip = pzip;
zip->entry.state.m_cur_archive_file_ofs = zip->entry.offset;
zip->entry.state.m_comp_size = 0;
if (tdefl_init(&(zip->entry.comp), mz_zip_writer_add_put_buf_callback, &(zip->entry.state), tdefl_create_comp_flags_from_zip_params(level, -15, MZ_DEFAULT_STRATEGY)) != TDEFL_STATUS_OKAY) {
// Cannot initialize the zip compressor
return -1;
}
}
return 0;
}
int zip_entry_close(struct zip_t *zip) {
mz_zip_archive *pzip = NULL;
mz_uint level;
tdefl_status done;
mz_uint16 entrylen;
time_t t;
struct tm *tm;
mz_uint16 dos_time, dos_date;
if (!zip) {
// zip_t handler is not initialized
return -1;
}
pzip = &(zip->archive);
level = zip->level & 0xF;
if (level) {
done = tdefl_compress_buffer(&(zip->entry.comp), "", 0, TDEFL_FINISH);
if (done != TDEFL_STATUS_DONE && done != TDEFL_STATUS_OKAY) {
// Cannot flush compressed buffer
cleanup(zip->entry.name);
return -1;
}
zip->entry.comp_size = zip->entry.state.m_comp_size;
zip->entry.offset = zip->entry.state.m_cur_archive_file_ofs;
zip->entry.method = MZ_DEFLATED;
}
entrylen = (mz_uint16)strlen(zip->entry.name);
t = time(NULL);
tm = localtime(&t);
dos_time = (mz_uint16)(((tm->tm_hour) << 11) + ((tm->tm_min) << 5) + ((tm->tm_sec) >> 1));
dos_date = (mz_uint16)(((tm->tm_year + 1900 - 1980) << 9) + ((tm->tm_mon + 1) << 5) + tm->tm_mday);
// no zip64 support yet
if ((zip->entry.comp_size > 0xFFFFFFFF) || (zip->entry.offset > 0xFFFFFFFF)) {
// No zip64 support, yet
cleanup(zip->entry.name);
return -1;
}
if (!mz_zip_writer_create_local_dir_header(pzip, zip->entry.header, entrylen, 0, zip->entry.uncomp_size, zip->entry.comp_size, zip->entry.uncomp_crc32, zip->entry.method, 0, dos_time, dos_date)) {
// Cannot create zip entry header
cleanup(zip->entry.name);
return -1;
}
if (pzip->m_pWrite(pzip->m_pIO_opaque, zip->entry.header_offset, zip->entry.header, sizeof(zip->entry.header)) != sizeof(zip->entry.header)) {
// Cannot write zip entry header
cleanup(zip->entry.name);
return -1;
}
if (!mz_zip_writer_add_to_central_dir(pzip, zip->entry.name, entrylen, NULL, 0, "", 0, zip->entry.uncomp_size, zip->entry.comp_size, zip->entry.uncomp_crc32, zip->entry.method, 0, dos_time, dos_date, zip->entry.header_offset, 0)) {
// Cannot write to zip central dir
cleanup(zip->entry.name);
return -1;
}
pzip->m_total_files++;
pzip->m_archive_size = zip->entry.offset;
cleanup(zip->entry.name);
return 0;
}
int zip_entry_write(struct zip_t *zip, const void *buf, size_t bufsize) {
mz_uint level;
mz_zip_archive *pzip = NULL;
tdefl_status status;
if (!zip) {
// zip_t handler is not initialized
return -1;
}
pzip = &(zip->archive);
if (buf && bufsize > 0) {
zip->entry.uncomp_size += bufsize;
zip->entry.uncomp_crc32 = (mz_uint32)mz_crc32(zip->entry.uncomp_crc32, (const mz_uint8 *)buf, bufsize);
level = zip->level & 0xF;
if (!level) {
if ((pzip->m_pWrite(pzip->m_pIO_opaque, zip->entry.offset, buf, bufsize) != bufsize)) {
// Cannot write buffer
return -1;
}
zip->entry.offset += bufsize;
zip->entry.comp_size += bufsize;
} else {
status = tdefl_compress_buffer(&(zip->entry.comp), buf, bufsize, TDEFL_NO_FLUSH);
if (status != TDEFL_STATUS_DONE && status != TDEFL_STATUS_OKAY) {
// Cannot compress buffer
return -1;
}
}
}
return 0;
}
int zip_entry_fwrite(struct zip_t *zip, const char *filename) {
int status = 0;
size_t n = 0;
FILE *stream = NULL;
mz_uint8 buf[MZ_ZIP_MAX_IO_BUF_SIZE] = { 0 };
if (!zip) {
// zip_t handler is not initialized
return -1;
}
stream = fopen(filename, "rb");
if (!stream) {
// Cannot open filename
return -1;
}
while((n = fread(buf, sizeof(mz_uint8), MZ_ZIP_MAX_IO_BUF_SIZE, stream)) > 0) {
if (zip_entry_write(zip, buf, n) < 0) {
status = -1;
break;
}
}
fclose(stream);
return status;
}
int zip_create(const char *zipname, const char *filenames[], size_t len) {
int status = 0;
size_t i;
mz_zip_archive zip_archive;
if (!zipname || strlen(zipname) < 1) {
// zip_t archive name is empty or NULL
return -1;
}
// Create a new archive.
if (!memset(&(zip_archive), 0, sizeof(zip_archive))) {
// Cannot memset zip archive
return -1;
}
if (!mz_zip_writer_init_file(&zip_archive, zipname, 0)) {
// Cannot initialize zip_archive writer
return -1;
}
for (i = 0; i < len; ++i) {
const char *name = filenames[i];
if (!name) {
status = -1;
break;
}
if (!mz_zip_writer_add_file(&zip_archive, basename(name), name, "", 0, ZIP_DEFAULT_COMPRESSION_LEVEL)) {
// Cannot add file to zip_archive
status = -1;
break;
}
}
mz_zip_writer_finalize_archive(&zip_archive);
mz_zip_writer_end(&zip_archive);
return status;
}
int zip_extract(const char *zipname, const char *dir, int (* on_extract)(const char *filename, void *arg), void *arg) {
int status = 0;
mz_uint i, n;
char path[MAX_PATH + 1] = { 0 };
mz_zip_archive zip_archive;
mz_zip_archive_file_stat info;
size_t dirlen = 0;
if (!memset(&(zip_archive), 0, sizeof(zip_archive))) {
// Cannot memset zip archive
return -1;
}
if (!zipname || !dir) {
// Cannot parse zip archive name
return -1;
}
dirlen = strlen(dir);
if (dirlen + 1 > MAX_PATH) {
return -1;
}
// Now try to open the archive.
if (!mz_zip_reader_init_file(&zip_archive, zipname, 0)) {
// Cannot initialize zip_archive reader
status = -1;
goto finally;
}
strcpy(path, dir);
if (!ISSLASH(path[dirlen -1])) {
#if defined _WIN32 || defined __WIN32__ || defined __EMX__ || defined __DJGPP__
path[dirlen] = '\\';
#else
path[dirlen] = '/';
#endif
++dirlen;
}
// Get and print information about each file in the archive.
n = mz_zip_reader_get_num_files(&zip_archive);
for (i = 0; i < n; ++i) {
if (!mz_zip_reader_file_stat(&zip_archive, i, &info)) {
// Cannot get information about zip archive;
//printf("Cant get information about fimes");
status = -1;
break;
}
strncpy(&path[dirlen], info.m_filename, MAX_PATH - dirlen);
if (mkpath(path) < 0) {
// Cannot make a path
//printf("Cant create path\n");
status = -1;
break;
}
// verify if the path is directory first
// issue fixed by xsang.le@gmail.com
struct stat path_stat;
if(stat(path, &path_stat) != 0 || !(path_stat.st_mode & S_IFDIR))
if (!mz_zip_reader_extract_to_file(&zip_archive, i, path, 0)) {
// Cannot extract zip archive to file
//printf("Cant extract %s\n", path);
status = -1;
break;
}
if (on_extract) {
if (on_extract(path, arg) < 0) {
status = -1;
break;
}
}
}
// Close the archive, freeing any resources it was using
if (!mz_zip_reader_end(&zip_archive)) {
// Cannot end zip reader
status = -1;
}
finally:
return status;
}

90
modules/3rd/zip/zip.h Executable file
View File

@ -0,0 +1,90 @@
/*
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 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.
*/
#pragma once
#ifndef ZIP_H
#define ZIP_H
#include <string.h>
#ifdef __cplusplus
extern "C" {
#endif
#ifndef MAX_PATH
#define MAX_PATH 32767 /* # chars in a path name including NULL */
#endif
#define ZIP_DEFAULT_COMPRESSION_LEVEL 6
/* This data structure is used throughout the library to represent zip archive - forward declaration. */
struct zip_t;
/*
Opens zip archive with compression level.
If append is 0 then new archive will be created, otherwise function will try to append to the specified zip archive,
instead of creating a new one.
Compression levels: 0-9 are the standard zlib-style levels.
Returns pointer to zip_t structure or NULL on error.
*/
struct zip_t *zip_open(const char *zipname, int level, int append);
/* Closes zip archive, releases resources - always finalize. */
void zip_close(struct zip_t *zip);
/*
Opens a new entry for writing in a zip archive.
Returns negative number (< 0) on error, 0 on success.
*/
int zip_entry_open(struct zip_t *zip, const char *entryname);
/*
Closes zip entry, flushes buffer and releases resources.
Returns negative number (< 0) on error, 0 on success.
*/
int zip_entry_close(struct zip_t *zip);
/*
Compresses an input buffer for the current zip entry.
Returns negative number (< 0) on error, 0 on success.
*/
int zip_entry_write(struct zip_t *zip, const void *buf, size_t bufsize);
/*
Compresses a file for the current zip entry.
Returns negative number (< 0) on error, 0 on success.
*/
int zip_entry_fwrite(struct zip_t *zip, const char *filename);
/*
Creates a new archive and puts len files into a single zip archive
Returns negative number (< 0) on error, 0 on success.
*/
int zip_create(const char *zipname, const char *filenames[], size_t len);
/*
Extracts a zip archive file into dir.
If on_extract_entry is not NULL, the callback will be called after successfully extracted each zip entry.
Returning a negative value from the callback will cause abort the extract and return an error.
The last argument (void *arg) is optional, which you can use to pass some data to the on_extract_entry callback.
Returns negative number (< 0) on error, 0 on success.
*/
int zip_extract(const char *zipname, const char *dir, int (* on_extract_entry)(const char *filename, void *arg), void *arg);
#ifdef __cplusplus
}
#endif
#endif

49
modules/Makefile.am Normal file
View File

@ -0,0 +1,49 @@
lib_LTLIBRARIES = ulib.la
ulib_la_LDFLAGS = -module -avoid-version -shared
ulib_la_SOURCES = 3rd/zip/zip.c ulib.c
lib_LTLIBRARIES += enc.la
enc_la_LDFLAGS = -module -avoid-version -shared
enc_la_SOURCES = enc.c base64.c sha1.c
lib_LTLIBRARIES += slice.la
slice_la_LDFLAGS = -module -avoid-version -shared
slice_la_SOURCES = slice.c
lib_LTLIBRARIES += stmr.la
stmr_la_LDFLAGS = -module -avoid-version -shared
stmr_la_SOURCES = stmr.c
lib_LTLIBRARIES += json.la
json_la_LDFLAGS = -module -avoid-version -shared
json_la_SOURCES = 3rd/jsmn/jsmn.c json.c
lib_LTLIBRARIES += md.la
md_la_LDFLAGS = -module -avoid-version -shared
md_la_SOURCES = md.c 3rd/md4c/entity.c 3rd/md4c/md4c.c 3rd/md4c/md4c-html.c
if HAS_DB
lib_LTLIBRARIES += sqlitedb.la
sqlitedb_la_LDFLAGS = -module -avoid-version -shared
sqlitedb_la_SOURCES = sqlitedb.c
endif
libdir=$(prefix)/lib/lua/
EXTRA_DIST = base64.h \
sha1.h \
3rd/zip/miniz.c \
3rd/zip/zip.h \
3rd/jsmn/jsmn.h \
3rd/md4c/md4c-html.h \
3rd/md4c/entity.h \
3rd/md4c/md4c.h \
lua/lualib.h \
lua/lua54/luaconf.h \
lua/lua54/lualib.h \
lua/lua54/lua.h \
lua/lua54/lauxlib.h

185
modules/base64.c Normal file
View File

@ -0,0 +1,185 @@
/* ====================================================================
* Copyright (c) 1995-1999 The Apache Group. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. 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.
*
* 3. All advertising materials mentioning features or use of this
* software must display the following acknowledgment:
* "This product includes software developed by the Apache Group
* for use in the Apache HTTP server project (http://www.apache.org/)."
*
* 4. The names "Apache Server" and "Apache Group" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For written permission, please contact
* apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* 6. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by the Apache Group
* for use in the Apache HTTP server project (http://www.apache.org/)."
*
* THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
* EXPRESSED 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 APACHE GROUP OR
* ITS 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Group and was originally based
* on public domain software written at the National Center for
* Supercomputing Applications, University of Illinois, Urbana-Champaign.
* For more information on the Apache Group and the Apache HTTP server
* project, please see <http://www.apache.org/>.
*
*/
/* Base64 encoder/decoder. Originally Apache file ap_base64.c
*/
#include <string.h>
#include "base64.h"
/* aaaack but it's fast and const should make it shared text page. */
static const unsigned char pr2six[256] =
{
/* ASCII table */
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63,
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 64, 64, 64,
64, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 64,
64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64
};
int Base64decode_len(const char *bufcoded)
{
int nbytesdecoded;
register const unsigned char *bufin;
register int nprbytes;
bufin = (const unsigned char *) bufcoded;
while (pr2six[*(bufin++)] <= 63);
nprbytes = (bufin - (const unsigned char *) bufcoded) - 1;
nbytesdecoded = ((nprbytes + 3) / 4) * 3;
return nbytesdecoded + 1;
}
int Base64decode(char *bufplain, const char *bufcoded)
{
int nbytesdecoded;
register const unsigned char *bufin;
register unsigned char *bufout;
register int nprbytes;
bufin = (const unsigned char *) bufcoded;
while (pr2six[*(bufin++)] <= 63);
nprbytes = (bufin - (const unsigned char *) bufcoded) - 1;
nbytesdecoded = ((nprbytes + 3) / 4) * 3;
bufout = (unsigned char *) bufplain;
bufin = (const unsigned char *) bufcoded;
while (nprbytes > 4) {
*(bufout++) =
(unsigned char) (pr2six[*bufin] << 2 | pr2six[bufin[1]] >> 4);
*(bufout++) =
(unsigned char) (pr2six[bufin[1]] << 4 | pr2six[bufin[2]] >> 2);
*(bufout++) =
(unsigned char) (pr2six[bufin[2]] << 6 | pr2six[bufin[3]]);
bufin += 4;
nprbytes -= 4;
}
/* Note: (nprbytes == 1) would be an error, so just ingore that case */
if (nprbytes > 1) {
*(bufout++) =
(unsigned char) (pr2six[*bufin] << 2 | pr2six[bufin[1]] >> 4);
}
if (nprbytes > 2) {
*(bufout++) =
(unsigned char) (pr2six[bufin[1]] << 4 | pr2six[bufin[2]] >> 2);
}
if (nprbytes > 3) {
*(bufout++) =
(unsigned char) (pr2six[bufin[2]] << 6 | pr2six[bufin[3]]);
}
*(bufout++) = '\0';
nbytesdecoded -= (4 - nprbytes) & 3;
return nbytesdecoded;
}
static const char basis_64[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
int Base64encode_len(int len)
{
return ((len + 2) / 3 * 4) + 1;
}
int Base64encode(char *encoded, const char *string, int len)
{
int i;
char *p;
p = encoded;
for (i = 0; i < len - 2; i += 3) {
*p++ = basis_64[(string[i] >> 2) & 0x3F];
*p++ = basis_64[((string[i] & 0x3) << 4) |
((int) (string[i + 1] & 0xF0) >> 4)];
*p++ = basis_64[((string[i + 1] & 0xF) << 2) |
((int) (string[i + 2] & 0xC0) >> 6)];
*p++ = basis_64[string[i + 2] & 0x3F];
}
if (i < len) {
*p++ = basis_64[(string[i] >> 2) & 0x3F];
if (i == (len - 1)) {
*p++ = basis_64[((string[i] & 0x3) << 4)];
*p++ = '=';
}
else {
*p++ = basis_64[((string[i] & 0x3) << 4) |
((int) (string[i + 1] & 0xF0) >> 4)];
*p++ = basis_64[((string[i + 1] & 0xF) << 2)];
}
*p++ = '=';
}
*p++ = '\0';
return p - encoded;
}

77
modules/base64.h Normal file
View File

@ -0,0 +1,77 @@
/* ====================================================================
* Copyright (c) 1995-1999 The Apache Group. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. 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.
*
* 3. All advertising materials mentioning features or use of this
* software must display the following acknowledgment:
* "This product includes software developed by the Apache Group
* for use in the Apache HTTP server project (http://www.apache.org/)."
*
* 4. The names "Apache Server" and "Apache Group" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For written permission, please contact
* apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* 6. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by the Apache Group
* for use in the Apache HTTP server project (http://www.apache.org/)."
*
* THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
* EXPRESSED 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 APACHE GROUP OR
* ITS 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Group and was originally based
* on public domain software written at the National Center for
* Supercomputing Applications, University of Illinois, Urbana-Champaign.
* For more information on the Apache Group and the Apache HTTP server
* project, please see <http://www.apache.org/>.
*
*/
#ifndef _BASE64_H_
#define _BASE64_H_
#ifdef __cplusplus
extern "C" {
#endif
int Base64encode_len(int len);
int Base64encode(char * coded_dst, const char *plain_src,int len_plain_src);
int Base64decode_len(const char * coded_src);
int Base64decode(char * plain_dst, const char *coded_src);
#ifdef __cplusplus
}
#endif
#endif //_BASE64_H_

231
modules/enc.c Normal file
View File

@ -0,0 +1,231 @@
#include <errno.h>
#include <string.h>
#include <sys/socket.h>
#include <time.h>
#include <fcntl.h>
#include "sha1.h"
#include "base64.h"
#include "lua/lualib.h"
#define LEFTROTATE(x, c) (((x) << (c)) | ((x) >> (32 - (c))))
static unsigned simple_hash(const char* key)
{
unsigned hashval;
for (hashval = 0; *key != '\0'; key++)
hashval = *key + 31 * hashval;
return hashval;
}
static void md5(uint8_t *initial_msg, size_t initial_len, char* buff) {
uint32_t h0, h1, h2, h3;
char tmp[80];
uint8_t *msg = NULL;
uint32_t r[] = {7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20,
4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21};
// Use binary integer part of the sines of integers (in radians) as constants// Initialize variables:
uint32_t k[] = {
0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,
0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed,
0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05,
0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039,
0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391};
h0 = 0x67452301;
h1 = 0xefcdab89;
h2 = 0x98badcfe;
h3 = 0x10325476;
// Pre-processing: adding a single 1 bit
//append "1" bit to message
/* Notice: the input bytes are considered as bits strings,
where the first bit is the most significant bit of the byte.[37] */
// Pre-processing: padding with zeros
//append "0" bit until message length in bit ≡ 448 (mod 512)
//append length mod (2 pow 64) to message
int new_len;
for(new_len = initial_len*8 + 1; new_len%512!=448; new_len++);
new_len /= 8;
msg = calloc(new_len + 64, 1); // also appends "0" bits
// (we alloc also 64 extra bytes...)
memcpy(msg, initial_msg, initial_len);
msg[initial_len] = 128; // write the "1" bit
uint32_t bits_len = 8*initial_len; // note, we append the len
memcpy(msg + new_len, &bits_len, 4); // in bits at the end of the buffer
// Process the message in successive 512-bit chunks:
//for each 512-bit chunk of message:
int offset;
for(offset=0; offset<new_len; offset += (512/8)) {
// break chunk into sixteen 32-bit words w[j], 0 ≤ j ≤ 15
uint32_t *w = (uint32_t *) (msg + offset);
// Initialize hash value for this chunk:
uint32_t a = h0;
uint32_t b = h1;
uint32_t c = h2;
uint32_t d = h3;
// Main loop:
uint32_t i;
for(i = 0; i<64; i++) {
uint32_t f, g;
if (i < 16) {
f = (b & c) | ((~b) & d);
g = i;
} else if (i < 32) {
f = (d & b) | ((~d) & c);
g = (5*i + 1) % 16;
} else if (i < 48) {
f = b ^ c ^ d;
g = (3*i + 5) % 16;
} else {
f = c ^ (b | (~d));
g = (7*i) % 16;
}
uint32_t temp = d;
d = c;
c = b;
//printf("rotateLeft(%x + %x + %x + %x, %d)\n", a, f, k[i], w[g], r[i]);
b = b + LEFTROTATE((a + f + k[i] + w[g]), r[i]);
a = temp;
}
// Add this chunk's hash to result so far:
h0 += a;
h1 += b;
h2 += c;
h3 += d;
}
uint8_t *p;
p=(uint8_t *)&h0;
sprintf(tmp,"%02x%02x%02x%02x", p[0], p[1], p[2], p[3]);//, h0
strcpy(buff, tmp);
p=(uint8_t *)&h1;
sprintf(tmp,"%02x%02x%02x%02x", p[0], p[1], p[2], p[3]); //, h1)
strcat(buff,tmp);
p=(uint8_t *)&h2;
sprintf(tmp,"%02x%02x%02x%02x", p[0], p[1], p[2], p[3]); // , h2
strcat(buff,tmp);
p=(uint8_t *)&h3;
sprintf(tmp,"%02x%02x%02x%02x", p[0], p[1], p[2], p[3]); // , h3
strcat(buff,tmp);
// cleanup
free(msg);
}
void sha1(const char* text, char* out)
{
uint8_t d [20];
SHA1_CTX context;
SHA1_Init(&context);
SHA1_Update(&context, text, strlen(text));
SHA1_Final(d, &context);
digest_to_hex(d,out);
}
static int l_simple_hash(lua_State *L)
{
const char *s = luaL_checkstring(L, 1);
lua_pushnumber(L, simple_hash(s));
return 1;
}
static int l_md5(lua_State *L)
{
const char *s = luaL_checkstring(L, 1);
int len = strlen(s);
char buff[256];
md5((uint8_t *)s, len, buff);
lua_pushstring(L, buff);
return 1;
}
static int l_sha1(lua_State *L)
{
const char *s = luaL_checkstring(L, 1);
char buff[80];
sha1(s, buff);
lua_pushstring(L, buff);
return 1;
}
static int l_base64_encode(lua_State *L)
{
char *s;
int len;
char *dst;
slice_t *vec = NULL;
if (lua_isstring(L, 1))
{
s = (char *)luaL_checkstring(L, 1);
len = strlen(s);
}
else
{
// this may be an bytearray
vec = lua_check_slice(L, 1);
s = (char *)vec->data;
len = vec->len;
}
if (len == 0)
{
lua_pushstring(L, "");
return 1;
}
dst = (char *)malloc(((len * 4) / 3) + (len / 96) + 6); //(((4 * len / 3) + 3) & ~3)+1
Base64encode(dst, s, len);
lua_pushstring(L, dst);
free(dst);
return 1;
}
static int l_base64_decode(lua_State *L)
{
const char *s = luaL_checkstring(L, 1);
int len = Base64decode_len(s);
// decode data to a byte array
lua_new_slice(L, len);
slice_t *vec = NULL;
vec = lua_check_slice(L, 2);
len = Base64decode((char *)vec->data, s);
vec->len = len;
// lua_pushstring(L,dst);
// free(dst);
return 1;
}
static const struct luaL_Reg enc[] = {
{"b64encode", l_base64_encode},
{"b64decode", l_base64_decode},
{"hash", l_simple_hash},
{"md5", l_md5},
{"sha1", l_sha1},
{NULL, NULL}};
int luaopen_enc(lua_State *L)
{
luaL_newlib(L, enc);
return 1;
}

264
modules/json.c Normal file
View File

@ -0,0 +1,264 @@
#include "lua/lualib.h"
#include "3rd/jsmn/jsmn.h"
#define MAXTOKEN 8192
// define unescape sequence
static int token_to_object(lua_State *L, jsmntok_t* t, const char* s, int cid);
static int l_json_parser(lua_State *L, const char* s);
//void header(int,const char*);
static int l_json_decode_s (lua_State *L) {
const char* s = luaL_checkstring(L,1);
return l_json_parser(L,s);
}
static int l_json_decode_f (lua_State *L) {
// read the entire file
char * buffer = 0;
long length;
const char* ph = luaL_checkstring(L,1);
FILE * f = fopen (ph, "rb");
if (f)
{
fseek (f, 0, SEEK_END);
length = ftell (f);
fseek (f, 0, SEEK_SET);
buffer = malloc (length+1);
if (buffer)
{
int ret = fread(buffer, 1, length, f);
}
fclose (f);
}
if (buffer)
{
buffer[length] = '\0';
l_json_parser(L,buffer);
free(buffer);
return 1;
}
else
{
lua_pushnil(L);
return 1;
}
}
static int process_token_object(lua_State* L, jsmntok_t* t, const char* s, int cid)
{
lua_newtable(L);
int id = cid+1;
//printf("%d\n", t[cid].size);
for(int i = 0; i < t[cid].size; i++)
{
char*str = strndup(s+t[id].start, t[id].end-t[id].start);
lua_pushstring(L,str);
free(str);
id = token_to_object(L,t,s,id+1);
lua_settable(L, -3);
}
return id;
}
static int process_token_array(lua_State* L, jsmntok_t* t, const char* s, int cid)
{
lua_newtable(L);
int id = cid+1;
for(int i = 1; i <= t[cid].size; i++)
{
lua_pushnumber(L,i);
id = token_to_object(L,t,s,id);
lua_settable(L, -3);
}
return id;
}
/*
static void stackDump (lua_State *L) {
int i;
int top = lua_gettop(L);
for (i = 1; i <= top; i++) {
int t = lua_type(L, i);
switch (t) {
case LUA_TSTRING:
printf("`%s' \n", lua_tostring(L, i));
break;
case LUA_TBOOLEAN:
printf(lua_toboolean(L, i) ? "true\n" : "false\n");
break;
case LUA_TNUMBER:
printf("%g\n", lua_tonumber(L, i));
break;
default:
printf("%s\n", lua_typename(L, t));
break;
}
printf(" ");
}
printf("\n");
}
*/
static int process_token_string(lua_State* L, jsmntok_t* t, const char* s, int cid)
{
// un escape the string
char * str = (char*) malloc(t[cid].end-t[cid].start + 1);
int index = 0;
char c;
uint8_t escape = 0;
for (int i = t[cid].start; i < t[cid].end; i++)
{
c = *(s+i);
if(c == '\\')
{
if(escape)
{
str[index] = c;
escape = 0;
index++;
}
else
{
escape = 1;
}
}
else
{
if(escape)
{
switch (c)
{
case 'b':
str[index] = '\b';
break;
case 'f':
str[index] = '\f';
break;
case 'n':
str[index] = '\n';
break;
case 'r':
str[index] = '\r';
break;
case 't':
str[index] = '\t';
break;
default:
str[index] = c;
}
}
else
{
str[index] = c;
}
index++;
escape = 0;
}
}
str[index] = '\0';
//strndup(s+t[cid].start, t[cid].end-t[cid].start);
// un escape the string
/*lua_getglobal(L, "utils");
lua_getfield(L, -1, "unescape");
lua_pushstring(L,str);
if (lua_pcall(L, 1, 1, 0) != 0)
printf("Error running function `unescape': %s\n",lua_tostring(L, -1));
if(str) free(str);
str = (char*)luaL_checkstring(L,-1);
lua_settop(L, -3);*/
lua_pushstring(L,str);
//stackDump(L);
//lua_pushstring(L, str);
//printf("%s\n",strndup(s+t[cid].start, t[cid].end-t[cid].start) );
if(str) free(str);
return cid+1;
}
static int process_token_primitive(lua_State* L, jsmntok_t* t, const char* s, int cid)
{
//printf("%s\n",strndup(s+t[cid].start, t[cid].end-t[cid].start) );
char c = s[t[cid].start];
char *str;
switch(c)
{
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '+':
case '-':
str = strndup(s+t[cid].start, t[cid].end-t[cid].start);
lua_pushnumber(L,atof(str));
free(str);
break;
case 't': lua_pushboolean(L,1); break;
case 'f': lua_pushboolean(L,0); break;
default: lua_pushnil(L);break;
}
return cid+1;
}
static int token_to_object(lua_State *L, jsmntok_t* t, const char* s, int cid)
{
switch(t[cid].type)
{
case JSMN_OBJECT:
return process_token_object(L,t,s,cid);
break;
case JSMN_ARRAY:
return process_token_array(L,t,s,cid);
break;
case JSMN_STRING:
return process_token_string(L,t,s,cid);
break;
case JSMN_PRIMITIVE:
return process_token_primitive(L,t,s,cid);
break;
default:
lua_pushnil(L);
return cid + t[cid].size; break;
}
}
static int l_json_parser(lua_State *L, const char* s)
{
jsmn_parser p;
jsmntok_t t[MAXTOKEN];
jsmn_init(&p);
int r = jsmn_parse(&p, s, strlen(s), t, sizeof(t)/sizeof(t[0]));
if (r < 0) {
lua_pushnil(L);
return 0;
}
token_to_object(L,t,s,0);
return 1;
}
static const struct luaL_Reg _json [] = {
{"decodeString", l_json_decode_s},
{"decodeFile", l_json_decode_f},
{NULL,NULL}
};
int luaopen_json(lua_State *L)
{
luaL_newlib(L, _json);
return 1;
}

301
modules/lua/lua54/lauxlib.h Normal file
View File

@ -0,0 +1,301 @@
/*
** $Id: lauxlib.h $
** Auxiliary functions for building Lua libraries
** See Copyright Notice in lua.h
*/
#ifndef lauxlib_h
#define lauxlib_h
#include <stddef.h>
#include <stdio.h>
#include "luaconf.h"
#include "lua.h"
/* global table */
#define LUA_GNAME "_G"
typedef struct luaL_Buffer luaL_Buffer;
/* extra error code for 'luaL_loadfilex' */
#define LUA_ERRFILE (LUA_ERRERR+1)
/* key, in the registry, for table of loaded modules */
#define LUA_LOADED_TABLE "_LOADED"
/* key, in the registry, for table of preloaded loaders */
#define LUA_PRELOAD_TABLE "_PRELOAD"
typedef struct luaL_Reg {
const char *name;
lua_CFunction func;
} luaL_Reg;
#define LUAL_NUMSIZES (sizeof(lua_Integer)*16 + sizeof(lua_Number))
LUALIB_API void (luaL_checkversion_) (lua_State *L, lua_Number ver, size_t sz);
#define luaL_checkversion(L) \
luaL_checkversion_(L, LUA_VERSION_NUM, LUAL_NUMSIZES)
LUALIB_API int (luaL_getmetafield) (lua_State *L, int obj, const char *e);
LUALIB_API int (luaL_callmeta) (lua_State *L, int obj, const char *e);
LUALIB_API const char *(luaL_tolstring) (lua_State *L, int idx, size_t *len);
LUALIB_API int (luaL_argerror) (lua_State *L, int arg, const char *extramsg);
LUALIB_API int (luaL_typeerror) (lua_State *L, int arg, const char *tname);
LUALIB_API const char *(luaL_checklstring) (lua_State *L, int arg,
size_t *l);
LUALIB_API const char *(luaL_optlstring) (lua_State *L, int arg,
const char *def, size_t *l);
LUALIB_API lua_Number (luaL_checknumber) (lua_State *L, int arg);
LUALIB_API lua_Number (luaL_optnumber) (lua_State *L, int arg, lua_Number def);
LUALIB_API lua_Integer (luaL_checkinteger) (lua_State *L, int arg);
LUALIB_API lua_Integer (luaL_optinteger) (lua_State *L, int arg,
lua_Integer def);
LUALIB_API void (luaL_checkstack) (lua_State *L, int sz, const char *msg);
LUALIB_API void (luaL_checktype) (lua_State *L, int arg, int t);
LUALIB_API void (luaL_checkany) (lua_State *L, int arg);
LUALIB_API int (luaL_newmetatable) (lua_State *L, const char *tname);
LUALIB_API void (luaL_setmetatable) (lua_State *L, const char *tname);
LUALIB_API void *(luaL_testudata) (lua_State *L, int ud, const char *tname);
LUALIB_API void *(luaL_checkudata) (lua_State *L, int ud, const char *tname);
LUALIB_API void (luaL_where) (lua_State *L, int lvl);
LUALIB_API int (luaL_error) (lua_State *L, const char *fmt, ...);
LUALIB_API int (luaL_checkoption) (lua_State *L, int arg, const char *def,
const char *const lst[]);
LUALIB_API int (luaL_fileresult) (lua_State *L, int stat, const char *fname);
LUALIB_API int (luaL_execresult) (lua_State *L, int stat);
/* predefined references */
#define LUA_NOREF (-2)
#define LUA_REFNIL (-1)
LUALIB_API int (luaL_ref) (lua_State *L, int t);
LUALIB_API void (luaL_unref) (lua_State *L, int t, int ref);
LUALIB_API int (luaL_loadfilex) (lua_State *L, const char *filename,
const char *mode);
#define luaL_loadfile(L,f) luaL_loadfilex(L,f,NULL)
LUALIB_API int (luaL_loadbufferx) (lua_State *L, const char *buff, size_t sz,
const char *name, const char *mode);
LUALIB_API int (luaL_loadstring) (lua_State *L, const char *s);
LUALIB_API lua_State *(luaL_newstate) (void);
LUALIB_API lua_Integer (luaL_len) (lua_State *L, int idx);
LUALIB_API void (luaL_addgsub) (luaL_Buffer *b, const char *s,
const char *p, const char *r);
LUALIB_API const char *(luaL_gsub) (lua_State *L, const char *s,
const char *p, const char *r);
LUALIB_API void (luaL_setfuncs) (lua_State *L, const luaL_Reg *l, int nup);
LUALIB_API int (luaL_getsubtable) (lua_State *L, int idx, const char *fname);
LUALIB_API void (luaL_traceback) (lua_State *L, lua_State *L1,
const char *msg, int level);
LUALIB_API void (luaL_requiref) (lua_State *L, const char *modname,
lua_CFunction openf, int glb);
/*
** ===============================================================
** some useful macros
** ===============================================================
*/
#define luaL_newlibtable(L,l) \
lua_createtable(L, 0, sizeof(l)/sizeof((l)[0]) - 1)
#define luaL_newlib(L,l) \
(luaL_checkversion(L), luaL_newlibtable(L,l), luaL_setfuncs(L,l,0))
#define luaL_argcheck(L, cond,arg,extramsg) \
((void)(luai_likely(cond) || luaL_argerror(L, (arg), (extramsg))))
#define luaL_argexpected(L,cond,arg,tname) \
((void)(luai_likely(cond) || luaL_typeerror(L, (arg), (tname))))
#define luaL_checkstring(L,n) (luaL_checklstring(L, (n), NULL))
#define luaL_optstring(L,n,d) (luaL_optlstring(L, (n), (d), NULL))
#define luaL_typename(L,i) lua_typename(L, lua_type(L,(i)))
#define luaL_dofile(L, fn) \
(luaL_loadfile(L, fn) || lua_pcall(L, 0, LUA_MULTRET, 0))
#define luaL_dostring(L, s) \
(luaL_loadstring(L, s) || lua_pcall(L, 0, LUA_MULTRET, 0))
#define luaL_getmetatable(L,n) (lua_getfield(L, LUA_REGISTRYINDEX, (n)))
#define luaL_opt(L,f,n,d) (lua_isnoneornil(L,(n)) ? (d) : f(L,(n)))
#define luaL_loadbuffer(L,s,sz,n) luaL_loadbufferx(L,s,sz,n,NULL)
/*
** Perform arithmetic operations on lua_Integer values with wrap-around
** semantics, as the Lua core does.
*/
#define luaL_intop(op,v1,v2) \
((lua_Integer)((lua_Unsigned)(v1) op (lua_Unsigned)(v2)))
/* push the value used to represent failure/error */
#define luaL_pushfail(L) lua_pushnil(L)
/*
** Internal assertions for in-house debugging
*/
#if !defined(lua_assert)
#if defined LUAI_ASSERT
#include <assert.h>
#define lua_assert(c) assert(c)
#else
#define lua_assert(c) ((void)0)
#endif
#endif
/*
** {======================================================
** Generic Buffer manipulation
** =======================================================
*/
struct luaL_Buffer {
char *b; /* buffer address */
size_t size; /* buffer size */
size_t n; /* number of characters in buffer */
lua_State *L;
union {
LUAI_MAXALIGN; /* ensure maximum alignment for buffer */
char b[LUAL_BUFFERSIZE]; /* initial buffer */
} init;
};
#define luaL_bufflen(bf) ((bf)->n)
#define luaL_buffaddr(bf) ((bf)->b)
#define luaL_addchar(B,c) \
((void)((B)->n < (B)->size || luaL_prepbuffsize((B), 1)), \
((B)->b[(B)->n++] = (c)))
#define luaL_addsize(B,s) ((B)->n += (s))
#define luaL_buffsub(B,s) ((B)->n -= (s))
LUALIB_API void (luaL_buffinit) (lua_State *L, luaL_Buffer *B);
LUALIB_API char *(luaL_prepbuffsize) (luaL_Buffer *B, size_t sz);
LUALIB_API void (luaL_addlstring) (luaL_Buffer *B, const char *s, size_t l);
LUALIB_API void (luaL_addstring) (luaL_Buffer *B, const char *s);
LUALIB_API void (luaL_addvalue) (luaL_Buffer *B);
LUALIB_API void (luaL_pushresult) (luaL_Buffer *B);
LUALIB_API void (luaL_pushresultsize) (luaL_Buffer *B, size_t sz);
LUALIB_API char *(luaL_buffinitsize) (lua_State *L, luaL_Buffer *B, size_t sz);
#define luaL_prepbuffer(B) luaL_prepbuffsize(B, LUAL_BUFFERSIZE)
/* }====================================================== */
/*
** {======================================================
** File handles for IO library
** =======================================================
*/
/*
** A file handle is a userdata with metatable 'LUA_FILEHANDLE' and
** initial structure 'luaL_Stream' (it may contain other fields
** after that initial structure).
*/
#define LUA_FILEHANDLE "FILE*"
typedef struct luaL_Stream {
FILE *f; /* stream (NULL for incompletely created streams) */
lua_CFunction closef; /* to close stream (NULL for closed streams) */
} luaL_Stream;
/* }====================================================== */
/*
** {==================================================================
** "Abstraction Layer" for basic report of messages and errors
** ===================================================================
*/
/* print a string */
#if !defined(lua_writestring)
#define lua_writestring(s,l) fwrite((s), sizeof(char), (l), stdout)
#endif
/* print a newline and flush the output */
#if !defined(lua_writeline)
#define lua_writeline() (lua_writestring("\n", 1), fflush(stdout))
#endif
/* print an error message */
#if !defined(lua_writestringerror)
#define lua_writestringerror(s,p) \
(fprintf(stderr, (s), (p)), fflush(stderr))
#endif
/* }================================================================== */
/*
** {============================================================
** Compatibility with deprecated conversions
** =============================================================
*/
#if defined(LUA_COMPAT_APIINTCASTS)
#define luaL_checkunsigned(L,a) ((lua_Unsigned)luaL_checkinteger(L,a))
#define luaL_optunsigned(L,a,d) \
((lua_Unsigned)luaL_optinteger(L,a,(lua_Integer)(d)))
#define luaL_checkint(L,n) ((int)luaL_checkinteger(L, (n)))
#define luaL_optint(L,n,d) ((int)luaL_optinteger(L, (n), (d)))
#define luaL_checklong(L,n) ((long)luaL_checkinteger(L, (n)))
#define luaL_optlong(L,n,d) ((long)luaL_optinteger(L, (n), (d)))
#endif
/* }============================================================ */
#endif

518
modules/lua/lua54/lua.h Normal file
View File

@ -0,0 +1,518 @@
/*
** $Id: lua.h $
** Lua - A Scripting Language
** Lua.org, PUC-Rio, Brazil (http://www.lua.org)
** See Copyright Notice at the end of this file
*/
#ifndef lua_h
#define lua_h
#include <stdarg.h>
#include <stddef.h>
#include "luaconf.h"
#define LUA_VERSION_MAJOR "5"
#define LUA_VERSION_MINOR "4"
#define LUA_VERSION_RELEASE "4"
#define LUA_VERSION_NUM 504
#define LUA_VERSION_RELEASE_NUM (LUA_VERSION_NUM * 100 + 4)
#define LUA_VERSION "Lua " LUA_VERSION_MAJOR "." LUA_VERSION_MINOR
#define LUA_RELEASE LUA_VERSION "." LUA_VERSION_RELEASE
#define LUA_COPYRIGHT LUA_RELEASE " Copyright (C) 1994-2022 Lua.org, PUC-Rio"
#define LUA_AUTHORS "R. Ierusalimschy, L. H. de Figueiredo, W. Celes"
/* mark for precompiled code ('<esc>Lua') */
#define LUA_SIGNATURE "\x1bLua"
/* option for multiple returns in 'lua_pcall' and 'lua_call' */
#define LUA_MULTRET (-1)
/*
** Pseudo-indices
** (-LUAI_MAXSTACK is the minimum valid index; we keep some free empty
** space after that to help overflow detection)
*/
#define LUA_REGISTRYINDEX (-LUAI_MAXSTACK - 1000)
#define lua_upvalueindex(i) (LUA_REGISTRYINDEX - (i))
/* thread status */
#define LUA_OK 0
#define LUA_YIELD 1
#define LUA_ERRRUN 2
#define LUA_ERRSYNTAX 3
#define LUA_ERRMEM 4
#define LUA_ERRERR 5
typedef struct lua_State lua_State;
/*
** basic types
*/
#define LUA_TNONE (-1)
#define LUA_TNIL 0
#define LUA_TBOOLEAN 1
#define LUA_TLIGHTUSERDATA 2
#define LUA_TNUMBER 3
#define LUA_TSTRING 4
#define LUA_TTABLE 5
#define LUA_TFUNCTION 6
#define LUA_TUSERDATA 7
#define LUA_TTHREAD 8
#define LUA_NUMTYPES 9
/* minimum Lua stack available to a C function */
#define LUA_MINSTACK 20
/* predefined values in the registry */
#define LUA_RIDX_MAINTHREAD 1
#define LUA_RIDX_GLOBALS 2
#define LUA_RIDX_LAST LUA_RIDX_GLOBALS
/* type of numbers in Lua */
typedef LUA_NUMBER lua_Number;
/* type for integer functions */
typedef LUA_INTEGER lua_Integer;
/* unsigned integer type */
typedef LUA_UNSIGNED lua_Unsigned;
/* type for continuation-function contexts */
typedef LUA_KCONTEXT lua_KContext;
/*
** Type for C functions registered with Lua
*/
typedef int (*lua_CFunction) (lua_State *L);
/*
** Type for continuation functions
*/
typedef int (*lua_KFunction) (lua_State *L, int status, lua_KContext ctx);
/*
** Type for functions that read/write blocks when loading/dumping Lua chunks
*/
typedef const char * (*lua_Reader) (lua_State *L, void *ud, size_t *sz);
typedef int (*lua_Writer) (lua_State *L, const void *p, size_t sz, void *ud);
/*
** Type for memory-allocation functions
*/
typedef void * (*lua_Alloc) (void *ud, void *ptr, size_t osize, size_t nsize);
/*
** Type for warning functions
*/
typedef void (*lua_WarnFunction) (void *ud, const char *msg, int tocont);
/*
** generic extra include file
*/
#if defined(LUA_USER_H)
#include LUA_USER_H
#endif
/*
** RCS ident string
*/
extern const char lua_ident[];
/*
** state manipulation
*/
LUA_API lua_State *(lua_newstate) (lua_Alloc f, void *ud);
LUA_API void (lua_close) (lua_State *L);
LUA_API lua_State *(lua_newthread) (lua_State *L);
LUA_API int (lua_resetthread) (lua_State *L);
LUA_API lua_CFunction (lua_atpanic) (lua_State *L, lua_CFunction panicf);
LUA_API lua_Number (lua_version) (lua_State *L);
/*
** basic stack manipulation
*/
LUA_API int (lua_absindex) (lua_State *L, int idx);
LUA_API int (lua_gettop) (lua_State *L);
LUA_API void (lua_settop) (lua_State *L, int idx);
LUA_API void (lua_pushvalue) (lua_State *L, int idx);
LUA_API void (lua_rotate) (lua_State *L, int idx, int n);
LUA_API void (lua_copy) (lua_State *L, int fromidx, int toidx);
LUA_API int (lua_checkstack) (lua_State *L, int n);
LUA_API void (lua_xmove) (lua_State *from, lua_State *to, int n);
/*
** access functions (stack -> C)
*/
LUA_API int (lua_isnumber) (lua_State *L, int idx);
LUA_API int (lua_isstring) (lua_State *L, int idx);
LUA_API int (lua_iscfunction) (lua_State *L, int idx);
LUA_API int (lua_isinteger) (lua_State *L, int idx);
LUA_API int (lua_isuserdata) (lua_State *L, int idx);
LUA_API int (lua_type) (lua_State *L, int idx);
LUA_API const char *(lua_typename) (lua_State *L, int tp);
LUA_API lua_Number (lua_tonumberx) (lua_State *L, int idx, int *isnum);
LUA_API lua_Integer (lua_tointegerx) (lua_State *L, int idx, int *isnum);
LUA_API int (lua_toboolean) (lua_State *L, int idx);
LUA_API const char *(lua_tolstring) (lua_State *L, int idx, size_t *len);
LUA_API lua_Unsigned (lua_rawlen) (lua_State *L, int idx);
LUA_API lua_CFunction (lua_tocfunction) (lua_State *L, int idx);
LUA_API void *(lua_touserdata) (lua_State *L, int idx);
LUA_API lua_State *(lua_tothread) (lua_State *L, int idx);
LUA_API const void *(lua_topointer) (lua_State *L, int idx);
/*
** Comparison and arithmetic functions
*/
#define LUA_OPADD 0 /* ORDER TM, ORDER OP */
#define LUA_OPSUB 1
#define LUA_OPMUL 2
#define LUA_OPMOD 3
#define LUA_OPPOW 4
#define LUA_OPDIV 5
#define LUA_OPIDIV 6
#define LUA_OPBAND 7
#define LUA_OPBOR 8
#define LUA_OPBXOR 9
#define LUA_OPSHL 10
#define LUA_OPSHR 11
#define LUA_OPUNM 12
#define LUA_OPBNOT 13
LUA_API void (lua_arith) (lua_State *L, int op);
#define LUA_OPEQ 0
#define LUA_OPLT 1
#define LUA_OPLE 2
LUA_API int (lua_rawequal) (lua_State *L, int idx1, int idx2);
LUA_API int (lua_compare) (lua_State *L, int idx1, int idx2, int op);
/*
** push functions (C -> stack)
*/
LUA_API void (lua_pushnil) (lua_State *L);
LUA_API void (lua_pushnumber) (lua_State *L, lua_Number n);
LUA_API void (lua_pushinteger) (lua_State *L, lua_Integer n);
LUA_API const char *(lua_pushlstring) (lua_State *L, const char *s, size_t len);
LUA_API const char *(lua_pushstring) (lua_State *L, const char *s);
LUA_API const char *(lua_pushvfstring) (lua_State *L, const char *fmt,
va_list argp);
LUA_API const char *(lua_pushfstring) (lua_State *L, const char *fmt, ...);
LUA_API void (lua_pushcclosure) (lua_State *L, lua_CFunction fn, int n);
LUA_API void (lua_pushboolean) (lua_State *L, int b);
LUA_API void (lua_pushlightuserdata) (lua_State *L, void *p);
LUA_API int (lua_pushthread) (lua_State *L);
/*
** get functions (Lua -> stack)
*/
LUA_API int (lua_getglobal) (lua_State *L, const char *name);
LUA_API int (lua_gettable) (lua_State *L, int idx);
LUA_API int (lua_getfield) (lua_State *L, int idx, const char *k);
LUA_API int (lua_geti) (lua_State *L, int idx, lua_Integer n);
LUA_API int (lua_rawget) (lua_State *L, int idx);
LUA_API int (lua_rawgeti) (lua_State *L, int idx, lua_Integer n);
LUA_API int (lua_rawgetp) (lua_State *L, int idx, const void *p);
LUA_API void (lua_createtable) (lua_State *L, int narr, int nrec);
LUA_API void *(lua_newuserdatauv) (lua_State *L, size_t sz, int nuvalue);
LUA_API int (lua_getmetatable) (lua_State *L, int objindex);
LUA_API int (lua_getiuservalue) (lua_State *L, int idx, int n);
/*
** set functions (stack -> Lua)
*/
LUA_API void (lua_setglobal) (lua_State *L, const char *name);
LUA_API void (lua_settable) (lua_State *L, int idx);
LUA_API void (lua_setfield) (lua_State *L, int idx, const char *k);
LUA_API void (lua_seti) (lua_State *L, int idx, lua_Integer n);
LUA_API void (lua_rawset) (lua_State *L, int idx);
LUA_API void (lua_rawseti) (lua_State *L, int idx, lua_Integer n);
LUA_API void (lua_rawsetp) (lua_State *L, int idx, const void *p);
LUA_API int (lua_setmetatable) (lua_State *L, int objindex);
LUA_API int (lua_setiuservalue) (lua_State *L, int idx, int n);
/*
** 'load' and 'call' functions (load and run Lua code)
*/
LUA_API void (lua_callk) (lua_State *L, int nargs, int nresults,
lua_KContext ctx, lua_KFunction k);
#define lua_call(L,n,r) lua_callk(L, (n), (r), 0, NULL)
LUA_API int (lua_pcallk) (lua_State *L, int nargs, int nresults, int errfunc,
lua_KContext ctx, lua_KFunction k);
#define lua_pcall(L,n,r,f) lua_pcallk(L, (n), (r), (f), 0, NULL)
LUA_API int (lua_load) (lua_State *L, lua_Reader reader, void *dt,
const char *chunkname, const char *mode);
LUA_API int (lua_dump) (lua_State *L, lua_Writer writer, void *data, int strip);
/*
** coroutine functions
*/
LUA_API int (lua_yieldk) (lua_State *L, int nresults, lua_KContext ctx,
lua_KFunction k);
LUA_API int (lua_resume) (lua_State *L, lua_State *from, int narg,
int *nres);
LUA_API int (lua_status) (lua_State *L);
LUA_API int (lua_isyieldable) (lua_State *L);
#define lua_yield(L,n) lua_yieldk(L, (n), 0, NULL)
/*
** Warning-related functions
*/
LUA_API void (lua_setwarnf) (lua_State *L, lua_WarnFunction f, void *ud);
LUA_API void (lua_warning) (lua_State *L, const char *msg, int tocont);
/*
** garbage-collection function and options
*/
#define LUA_GCSTOP 0
#define LUA_GCRESTART 1
#define LUA_GCCOLLECT 2
#define LUA_GCCOUNT 3
#define LUA_GCCOUNTB 4
#define LUA_GCSTEP 5
#define LUA_GCSETPAUSE 6
#define LUA_GCSETSTEPMUL 7
#define LUA_GCISRUNNING 9
#define LUA_GCGEN 10
#define LUA_GCINC 11
LUA_API int (lua_gc) (lua_State *L, int what, ...);
/*
** miscellaneous functions
*/
LUA_API int (lua_error) (lua_State *L);
LUA_API int (lua_next) (lua_State *L, int idx);
LUA_API void (lua_concat) (lua_State *L, int n);
LUA_API void (lua_len) (lua_State *L, int idx);
LUA_API size_t (lua_stringtonumber) (lua_State *L, const char *s);
LUA_API lua_Alloc (lua_getallocf) (lua_State *L, void **ud);
LUA_API void (lua_setallocf) (lua_State *L, lua_Alloc f, void *ud);
LUA_API void (lua_toclose) (lua_State *L, int idx);
LUA_API void (lua_closeslot) (lua_State *L, int idx);
/*
** {==============================================================
** some useful macros
** ===============================================================
*/
#define lua_getextraspace(L) ((void *)((char *)(L) - LUA_EXTRASPACE))
#define lua_tonumber(L,i) lua_tonumberx(L,(i),NULL)
#define lua_tointeger(L,i) lua_tointegerx(L,(i),NULL)
#define lua_pop(L,n) lua_settop(L, -(n)-1)
#define lua_newtable(L) lua_createtable(L, 0, 0)
#define lua_register(L,n,f) (lua_pushcfunction(L, (f)), lua_setglobal(L, (n)))
#define lua_pushcfunction(L,f) lua_pushcclosure(L, (f), 0)
#define lua_isfunction(L,n) (lua_type(L, (n)) == LUA_TFUNCTION)
#define lua_istable(L,n) (lua_type(L, (n)) == LUA_TTABLE)
#define lua_islightuserdata(L,n) (lua_type(L, (n)) == LUA_TLIGHTUSERDATA)
#define lua_isnil(L,n) (lua_type(L, (n)) == LUA_TNIL)
#define lua_isboolean(L,n) (lua_type(L, (n)) == LUA_TBOOLEAN)
#define lua_isthread(L,n) (lua_type(L, (n)) == LUA_TTHREAD)
#define lua_isnone(L,n) (lua_type(L, (n)) == LUA_TNONE)
#define lua_isnoneornil(L, n) (lua_type(L, (n)) <= 0)
#define lua_pushliteral(L, s) lua_pushstring(L, "" s)
#define lua_pushglobaltable(L) \
((void)lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS))
#define lua_tostring(L,i) lua_tolstring(L, (i), NULL)
#define lua_insert(L,idx) lua_rotate(L, (idx), 1)
#define lua_remove(L,idx) (lua_rotate(L, (idx), -1), lua_pop(L, 1))
#define lua_replace(L,idx) (lua_copy(L, -1, (idx)), lua_pop(L, 1))
/* }============================================================== */
/*
** {==============================================================
** compatibility macros
** ===============================================================
*/
#if defined(LUA_COMPAT_APIINTCASTS)
#define lua_pushunsigned(L,n) lua_pushinteger(L, (lua_Integer)(n))
#define lua_tounsignedx(L,i,is) ((lua_Unsigned)lua_tointegerx(L,i,is))
#define lua_tounsigned(L,i) lua_tounsignedx(L,(i),NULL)
#endif
#define lua_newuserdata(L,s) lua_newuserdatauv(L,s,1)
#define lua_getuservalue(L,idx) lua_getiuservalue(L,idx,1)
#define lua_setuservalue(L,idx) lua_setiuservalue(L,idx,1)
#define LUA_NUMTAGS LUA_NUMTYPES
/* }============================================================== */
/*
** {======================================================================
** Debug API
** =======================================================================
*/
/*
** Event codes
*/
#define LUA_HOOKCALL 0
#define LUA_HOOKRET 1
#define LUA_HOOKLINE 2
#define LUA_HOOKCOUNT 3
#define LUA_HOOKTAILCALL 4
/*
** Event masks
*/
#define LUA_MASKCALL (1 << LUA_HOOKCALL)
#define LUA_MASKRET (1 << LUA_HOOKRET)
#define LUA_MASKLINE (1 << LUA_HOOKLINE)
#define LUA_MASKCOUNT (1 << LUA_HOOKCOUNT)
typedef struct lua_Debug lua_Debug; /* activation record */
/* Functions to be called by the debugger in specific events */
typedef void (*lua_Hook) (lua_State *L, lua_Debug *ar);
LUA_API int (lua_getstack) (lua_State *L, int level, lua_Debug *ar);
LUA_API int (lua_getinfo) (lua_State *L, const char *what, lua_Debug *ar);
LUA_API const char *(lua_getlocal) (lua_State *L, const lua_Debug *ar, int n);
LUA_API const char *(lua_setlocal) (lua_State *L, const lua_Debug *ar, int n);
LUA_API const char *(lua_getupvalue) (lua_State *L, int funcindex, int n);
LUA_API const char *(lua_setupvalue) (lua_State *L, int funcindex, int n);
LUA_API void *(lua_upvalueid) (lua_State *L, int fidx, int n);
LUA_API void (lua_upvaluejoin) (lua_State *L, int fidx1, int n1,
int fidx2, int n2);
LUA_API void (lua_sethook) (lua_State *L, lua_Hook func, int mask, int count);
LUA_API lua_Hook (lua_gethook) (lua_State *L);
LUA_API int (lua_gethookmask) (lua_State *L);
LUA_API int (lua_gethookcount) (lua_State *L);
LUA_API int (lua_setcstacklimit) (lua_State *L, unsigned int limit);
struct lua_Debug {
int event;
const char *name; /* (n) */
const char *namewhat; /* (n) 'global', 'local', 'field', 'method' */
const char *what; /* (S) 'Lua', 'C', 'main', 'tail' */
const char *source; /* (S) */
size_t srclen; /* (S) */
int currentline; /* (l) */
int linedefined; /* (S) */
int lastlinedefined; /* (S) */
unsigned char nups; /* (u) number of upvalues */
unsigned char nparams;/* (u) number of parameters */
char isvararg; /* (u) */
char istailcall; /* (t) */
unsigned short ftransfer; /* (r) index of first value transferred */
unsigned short ntransfer; /* (r) number of transferred values */
char short_src[LUA_IDSIZE]; /* (S) */
/* private part */
struct CallInfo *i_ci; /* active function */
};
/* }====================================================================== */
/******************************************************************************
* Copyright (C) 1994-2022 Lua.org, PUC-Rio.
*
* 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.
******************************************************************************/
#endif

786
modules/lua/lua54/luaconf.h Normal file
View File

@ -0,0 +1,786 @@
/*
** $Id: luaconf.h $
** Configuration file for Lua
** See Copyright Notice in lua.h
*/
#ifndef luaconf_h
#define luaconf_h
#include <limits.h>
#include <stddef.h>
/*
** ===================================================================
** General Configuration File for Lua
**
** Some definitions here can be changed externally, through the compiler
** (e.g., with '-D' options): They are commented out or protected
** by '#if !defined' guards. However, several other definitions
** should be changed directly here, either because they affect the
** Lua ABI (by making the changes here, you ensure that all software
** connected to Lua, such as C libraries, will be compiled with the same
** configuration); or because they are seldom changed.
**
** Search for "@@" to find all configurable definitions.
** ===================================================================
*/
/*
** {====================================================================
** System Configuration: macros to adapt (if needed) Lua to some
** particular platform, for instance restricting it to C89.
** =====================================================================
*/
/*
@@ LUA_USE_C89 controls the use of non-ISO-C89 features.
** Define it if you want Lua to avoid the use of a few C99 features
** or Windows-specific features on Windows.
*/
/* #define LUA_USE_C89 */
/*
** By default, Lua on Windows use (some) specific Windows features
*/
#if !defined(LUA_USE_C89) && defined(_WIN32) && !defined(_WIN32_WCE)
#define LUA_USE_WINDOWS /* enable goodies for regular Windows */
#endif
#if defined(LUA_USE_WINDOWS)
#define LUA_DL_DLL /* enable support for DLL */
#define LUA_USE_C89 /* broadly, Windows is C89 */
#endif
#if defined(LUA_USE_LINUX)
#define LUA_USE_POSIX
#define LUA_USE_DLOPEN /* needs an extra library: -ldl */
#endif
#if defined(LUA_USE_MACOSX)
#define LUA_USE_POSIX
#define LUA_USE_DLOPEN /* MacOS does not need -ldl */
#endif
/*
@@ LUAI_IS32INT is true iff 'int' has (at least) 32 bits.
*/
#define LUAI_IS32INT ((UINT_MAX >> 30) >= 3)
/* }================================================================== */
/*
** {==================================================================
** Configuration for Number types. These options should not be
** set externally, because any other code connected to Lua must
** use the same configuration.
** ===================================================================
*/
/*
@@ LUA_INT_TYPE defines the type for Lua integers.
@@ LUA_FLOAT_TYPE defines the type for Lua floats.
** Lua should work fine with any mix of these options supported
** by your C compiler. The usual configurations are 64-bit integers
** and 'double' (the default), 32-bit integers and 'float' (for
** restricted platforms), and 'long'/'double' (for C compilers not
** compliant with C99, which may not have support for 'long long').
*/
/* predefined options for LUA_INT_TYPE */
#define LUA_INT_INT 1
#define LUA_INT_LONG 2
#define LUA_INT_LONGLONG 3
/* predefined options for LUA_FLOAT_TYPE */
#define LUA_FLOAT_FLOAT 1
#define LUA_FLOAT_DOUBLE 2
#define LUA_FLOAT_LONGDOUBLE 3
/* Default configuration ('long long' and 'double', for 64-bit Lua) */
#define LUA_INT_DEFAULT LUA_INT_LONGLONG
#define LUA_FLOAT_DEFAULT LUA_FLOAT_DOUBLE
/*
@@ LUA_32BITS enables Lua with 32-bit integers and 32-bit floats.
*/
#define LUA_32BITS 0
/*
@@ LUA_C89_NUMBERS ensures that Lua uses the largest types available for
** C89 ('long' and 'double'); Windows always has '__int64', so it does
** not need to use this case.
*/
#if defined(LUA_USE_C89) && !defined(LUA_USE_WINDOWS)
#define LUA_C89_NUMBERS 1
#else
#define LUA_C89_NUMBERS 0
#endif
#if LUA_32BITS /* { */
/*
** 32-bit integers and 'float'
*/
#if LUAI_IS32INT /* use 'int' if big enough */
#define LUA_INT_TYPE LUA_INT_INT
#else /* otherwise use 'long' */
#define LUA_INT_TYPE LUA_INT_LONG
#endif
#define LUA_FLOAT_TYPE LUA_FLOAT_FLOAT
#elif LUA_C89_NUMBERS /* }{ */
/*
** largest types available for C89 ('long' and 'double')
*/
#define LUA_INT_TYPE LUA_INT_LONG
#define LUA_FLOAT_TYPE LUA_FLOAT_DOUBLE
#else /* }{ */
/* use defaults */
#define LUA_INT_TYPE LUA_INT_DEFAULT
#define LUA_FLOAT_TYPE LUA_FLOAT_DEFAULT
#endif /* } */
/* }================================================================== */
/*
** {==================================================================
** Configuration for Paths.
** ===================================================================
*/
/*
** LUA_PATH_SEP is the character that separates templates in a path.
** LUA_PATH_MARK is the string that marks the substitution points in a
** template.
** LUA_EXEC_DIR in a Windows path is replaced by the executable's
** directory.
*/
#define LUA_PATH_SEP ";"
#define LUA_PATH_MARK "?"
#define LUA_EXEC_DIR "!"
/*
@@ LUA_PATH_DEFAULT is the default path that Lua uses to look for
** Lua libraries.
@@ LUA_CPATH_DEFAULT is the default path that Lua uses to look for
** C libraries.
** CHANGE them if your machine has a non-conventional directory
** hierarchy or if you want to install your libraries in
** non-conventional directories.
*/
#define LUA_VDIR LUA_VERSION_MAJOR "." LUA_VERSION_MINOR
#if defined(_WIN32) /* { */
/*
** In Windows, any exclamation mark ('!') in the path is replaced by the
** path of the directory of the executable file of the current process.
*/
#define LUA_LDIR "!\\lua\\"
#define LUA_CDIR "!\\"
#define LUA_SHRDIR "!\\..\\share\\lua\\" LUA_VDIR "\\"
#if !defined(LUA_PATH_DEFAULT)
#define LUA_PATH_DEFAULT \
LUA_LDIR"?.lua;" LUA_LDIR"?\\init.lua;" \
LUA_CDIR"?.lua;" LUA_CDIR"?\\init.lua;" \
LUA_SHRDIR"?.lua;" LUA_SHRDIR"?\\init.lua;" \
".\\?.lua;" ".\\?\\init.lua"
#endif
#if !defined(LUA_CPATH_DEFAULT)
#define LUA_CPATH_DEFAULT \
LUA_CDIR"?.dll;" \
LUA_CDIR"..\\lib\\lua\\" LUA_VDIR "\\?.dll;" \
LUA_CDIR"loadall.dll;" ".\\?.dll"
#endif
#else /* }{ */
#define LUA_ROOT "/usr/local/"
#define LUA_LDIR LUA_ROOT "share/lua/" LUA_VDIR "/"
#define LUA_CDIR LUA_ROOT "lib/lua/" LUA_VDIR "/"
#if !defined(LUA_PATH_DEFAULT)
#define LUA_PATH_DEFAULT \
LUA_LDIR"?.lua;" LUA_LDIR"?/init.lua;" \
LUA_CDIR"?.lua;" LUA_CDIR"?/init.lua;" \
"./?.lua;" "./?/init.lua"
#endif
#if !defined(LUA_CPATH_DEFAULT)
#define LUA_CPATH_DEFAULT \
LUA_CDIR"?.so;" LUA_CDIR"loadall.so;" "./?.so"
#endif
#endif /* } */
/*
@@ LUA_DIRSEP is the directory separator (for submodules).
** CHANGE it if your machine does not use "/" as the directory separator
** and is not Windows. (On Windows Lua automatically uses "\".)
*/
#if !defined(LUA_DIRSEP)
#if defined(_WIN32)
#define LUA_DIRSEP "\\"
#else
#define LUA_DIRSEP "/"
#endif
#endif
/* }================================================================== */
/*
** {==================================================================
** Marks for exported symbols in the C code
** ===================================================================
*/
/*
@@ LUA_API is a mark for all core API functions.
@@ LUALIB_API is a mark for all auxiliary library functions.
@@ LUAMOD_API is a mark for all standard library opening functions.
** CHANGE them if you need to define those functions in some special way.
** For instance, if you want to create one Windows DLL with the core and
** the libraries, you may want to use the following definition (define
** LUA_BUILD_AS_DLL to get it).
*/
#if defined(LUA_BUILD_AS_DLL) /* { */
#if defined(LUA_CORE) || defined(LUA_LIB) /* { */
#define LUA_API __declspec(dllexport)
#else /* }{ */
#define LUA_API __declspec(dllimport)
#endif /* } */
#else /* }{ */
#define LUA_API extern
#endif /* } */
/*
** More often than not the libs go together with the core.
*/
#define LUALIB_API LUA_API
#define LUAMOD_API LUA_API
/*
@@ LUAI_FUNC is a mark for all extern functions that are not to be
** exported to outside modules.
@@ LUAI_DDEF and LUAI_DDEC are marks for all extern (const) variables,
** none of which to be exported to outside modules (LUAI_DDEF for
** definitions and LUAI_DDEC for declarations).
** CHANGE them if you need to mark them in some special way. Elf/gcc
** (versions 3.2 and later) mark them as "hidden" to optimize access
** when Lua is compiled as a shared library. Not all elf targets support
** this attribute. Unfortunately, gcc does not offer a way to check
** whether the target offers that support, and those without support
** give a warning about it. To avoid these warnings, change to the
** default definition.
*/
#if defined(__GNUC__) && ((__GNUC__*100 + __GNUC_MINOR__) >= 302) && \
defined(__ELF__) /* { */
#define LUAI_FUNC __attribute__((visibility("internal"))) extern
#else /* }{ */
#define LUAI_FUNC extern
#endif /* } */
#define LUAI_DDEC(dec) LUAI_FUNC dec
#define LUAI_DDEF /* empty */
/* }================================================================== */
/*
** {==================================================================
** Compatibility with previous versions
** ===================================================================
*/
/*
@@ LUA_COMPAT_5_3 controls other macros for compatibility with Lua 5.3.
** You can define it to get all options, or change specific options
** to fit your specific needs.
*/
#if defined(LUA_COMPAT_5_3) /* { */
/*
@@ LUA_COMPAT_MATHLIB controls the presence of several deprecated
** functions in the mathematical library.
** (These functions were already officially removed in 5.3;
** nevertheless they are still available here.)
*/
#define LUA_COMPAT_MATHLIB
/*
@@ LUA_COMPAT_APIINTCASTS controls the presence of macros for
** manipulating other integer types (lua_pushunsigned, lua_tounsigned,
** luaL_checkint, luaL_checklong, etc.)
** (These macros were also officially removed in 5.3, but they are still
** available here.)
*/
#define LUA_COMPAT_APIINTCASTS
/*
@@ LUA_COMPAT_LT_LE controls the emulation of the '__le' metamethod
** using '__lt'.
*/
#define LUA_COMPAT_LT_LE
/*
@@ The following macros supply trivial compatibility for some
** changes in the API. The macros themselves document how to
** change your code to avoid using them.
** (Once more, these macros were officially removed in 5.3, but they are
** still available here.)
*/
#define lua_strlen(L,i) lua_rawlen(L, (i))
#define lua_objlen(L,i) lua_rawlen(L, (i))
#define lua_equal(L,idx1,idx2) lua_compare(L,(idx1),(idx2),LUA_OPEQ)
#define lua_lessthan(L,idx1,idx2) lua_compare(L,(idx1),(idx2),LUA_OPLT)
#endif /* } */
/* }================================================================== */
/*
** {==================================================================
** Configuration for Numbers (low-level part).
** Change these definitions if no predefined LUA_FLOAT_* / LUA_INT_*
** satisfy your needs.
** ===================================================================
*/
/*
@@ LUAI_UACNUMBER is the result of a 'default argument promotion'
@@ over a floating number.
@@ l_floatatt(x) corrects float attribute 'x' to the proper float type
** by prefixing it with one of FLT/DBL/LDBL.
@@ LUA_NUMBER_FRMLEN is the length modifier for writing floats.
@@ LUA_NUMBER_FMT is the format for writing floats.
@@ lua_number2str converts a float to a string.
@@ l_mathop allows the addition of an 'l' or 'f' to all math operations.
@@ l_floor takes the floor of a float.
@@ lua_str2number converts a decimal numeral to a number.
*/
/* The following definitions are good for most cases here */
#define l_floor(x) (l_mathop(floor)(x))
#define lua_number2str(s,sz,n) \
l_sprintf((s), sz, LUA_NUMBER_FMT, (LUAI_UACNUMBER)(n))
/*
@@ lua_numbertointeger converts a float number with an integral value
** to an integer, or returns 0 if float is not within the range of
** a lua_Integer. (The range comparisons are tricky because of
** rounding. The tests here assume a two-complement representation,
** where MININTEGER always has an exact representation as a float;
** MAXINTEGER may not have one, and therefore its conversion to float
** may have an ill-defined value.)
*/
#define lua_numbertointeger(n,p) \
((n) >= (LUA_NUMBER)(LUA_MININTEGER) && \
(n) < -(LUA_NUMBER)(LUA_MININTEGER) && \
(*(p) = (LUA_INTEGER)(n), 1))
/* now the variable definitions */
#if LUA_FLOAT_TYPE == LUA_FLOAT_FLOAT /* { single float */
#define LUA_NUMBER float
#define l_floatatt(n) (FLT_##n)
#define LUAI_UACNUMBER double
#define LUA_NUMBER_FRMLEN ""
#define LUA_NUMBER_FMT "%.7g"
#define l_mathop(op) op##f
#define lua_str2number(s,p) strtof((s), (p))
#elif LUA_FLOAT_TYPE == LUA_FLOAT_LONGDOUBLE /* }{ long double */
#define LUA_NUMBER long double
#define l_floatatt(n) (LDBL_##n)
#define LUAI_UACNUMBER long double
#define LUA_NUMBER_FRMLEN "L"
#define LUA_NUMBER_FMT "%.19Lg"
#define l_mathop(op) op##l
#define lua_str2number(s,p) strtold((s), (p))
#elif LUA_FLOAT_TYPE == LUA_FLOAT_DOUBLE /* }{ double */
#define LUA_NUMBER double
#define l_floatatt(n) (DBL_##n)
#define LUAI_UACNUMBER double
#define LUA_NUMBER_FRMLEN ""
#define LUA_NUMBER_FMT "%.14g"
#define l_mathop(op) op
#define lua_str2number(s,p) strtod((s), (p))
#else /* }{ */
#error "numeric float type not defined"
#endif /* } */
/*
@@ LUA_UNSIGNED is the unsigned version of LUA_INTEGER.
@@ LUAI_UACINT is the result of a 'default argument promotion'
@@ over a LUA_INTEGER.
@@ LUA_INTEGER_FRMLEN is the length modifier for reading/writing integers.
@@ LUA_INTEGER_FMT is the format for writing integers.
@@ LUA_MAXINTEGER is the maximum value for a LUA_INTEGER.
@@ LUA_MININTEGER is the minimum value for a LUA_INTEGER.
@@ LUA_MAXUNSIGNED is the maximum value for a LUA_UNSIGNED.
@@ lua_integer2str converts an integer to a string.
*/
/* The following definitions are good for most cases here */
#define LUA_INTEGER_FMT "%" LUA_INTEGER_FRMLEN "d"
#define LUAI_UACINT LUA_INTEGER
#define lua_integer2str(s,sz,n) \
l_sprintf((s), sz, LUA_INTEGER_FMT, (LUAI_UACINT)(n))
/*
** use LUAI_UACINT here to avoid problems with promotions (which
** can turn a comparison between unsigneds into a signed comparison)
*/
#define LUA_UNSIGNED unsigned LUAI_UACINT
/* now the variable definitions */
#if LUA_INT_TYPE == LUA_INT_INT /* { int */
#define LUA_INTEGER int
#define LUA_INTEGER_FRMLEN ""
#define LUA_MAXINTEGER INT_MAX
#define LUA_MININTEGER INT_MIN
#define LUA_MAXUNSIGNED UINT_MAX
#elif LUA_INT_TYPE == LUA_INT_LONG /* }{ long */
#define LUA_INTEGER long
#define LUA_INTEGER_FRMLEN "l"
#define LUA_MAXINTEGER LONG_MAX
#define LUA_MININTEGER LONG_MIN
#define LUA_MAXUNSIGNED ULONG_MAX
#elif LUA_INT_TYPE == LUA_INT_LONGLONG /* }{ long long */
/* use presence of macro LLONG_MAX as proxy for C99 compliance */
#if defined(LLONG_MAX) /* { */
/* use ISO C99 stuff */
#define LUA_INTEGER long long
#define LUA_INTEGER_FRMLEN "ll"
#define LUA_MAXINTEGER LLONG_MAX
#define LUA_MININTEGER LLONG_MIN
#define LUA_MAXUNSIGNED ULLONG_MAX
#elif defined(LUA_USE_WINDOWS) /* }{ */
/* in Windows, can use specific Windows types */
#define LUA_INTEGER __int64
#define LUA_INTEGER_FRMLEN "I64"
#define LUA_MAXINTEGER _I64_MAX
#define LUA_MININTEGER _I64_MIN
#define LUA_MAXUNSIGNED _UI64_MAX
#else /* }{ */
#error "Compiler does not support 'long long'. Use option '-DLUA_32BITS' \
or '-DLUA_C89_NUMBERS' (see file 'luaconf.h' for details)"
#endif /* } */
#else /* }{ */
#error "numeric integer type not defined"
#endif /* } */
/* }================================================================== */
/*
** {==================================================================
** Dependencies with C99 and other C details
** ===================================================================
*/
/*
@@ l_sprintf is equivalent to 'snprintf' or 'sprintf' in C89.
** (All uses in Lua have only one format item.)
*/
#if !defined(LUA_USE_C89)
#define l_sprintf(s,sz,f,i) snprintf(s,sz,f,i)
#else
#define l_sprintf(s,sz,f,i) ((void)(sz), sprintf(s,f,i))
#endif
/*
@@ lua_strx2number converts a hexadecimal numeral to a number.
** In C99, 'strtod' does that conversion. Otherwise, you can
** leave 'lua_strx2number' undefined and Lua will provide its own
** implementation.
*/
#if !defined(LUA_USE_C89)
#define lua_strx2number(s,p) lua_str2number(s,p)
#endif
/*
@@ lua_pointer2str converts a pointer to a readable string in a
** non-specified way.
*/
#define lua_pointer2str(buff,sz,p) l_sprintf(buff,sz,"%p",p)
/*
@@ lua_number2strx converts a float to a hexadecimal numeral.
** In C99, 'sprintf' (with format specifiers '%a'/'%A') does that.
** Otherwise, you can leave 'lua_number2strx' undefined and Lua will
** provide its own implementation.
*/
#if !defined(LUA_USE_C89)
#define lua_number2strx(L,b,sz,f,n) \
((void)L, l_sprintf(b,sz,f,(LUAI_UACNUMBER)(n)))
#endif
/*
** 'strtof' and 'opf' variants for math functions are not valid in
** C89. Otherwise, the macro 'HUGE_VALF' is a good proxy for testing the
** availability of these variants. ('math.h' is already included in
** all files that use these macros.)
*/
#if defined(LUA_USE_C89) || (defined(HUGE_VAL) && !defined(HUGE_VALF))
#undef l_mathop /* variants not available */
#undef lua_str2number
#define l_mathop(op) (lua_Number)op /* no variant */
#define lua_str2number(s,p) ((lua_Number)strtod((s), (p)))
#endif
/*
@@ LUA_KCONTEXT is the type of the context ('ctx') for continuation
** functions. It must be a numerical type; Lua will use 'intptr_t' if
** available, otherwise it will use 'ptrdiff_t' (the nearest thing to
** 'intptr_t' in C89)
*/
#define LUA_KCONTEXT ptrdiff_t
#if !defined(LUA_USE_C89) && defined(__STDC_VERSION__) && \
__STDC_VERSION__ >= 199901L
#include <stdint.h>
#if defined(INTPTR_MAX) /* even in C99 this type is optional */
#undef LUA_KCONTEXT
#define LUA_KCONTEXT intptr_t
#endif
#endif
/*
@@ lua_getlocaledecpoint gets the locale "radix character" (decimal point).
** Change that if you do not want to use C locales. (Code using this
** macro must include the header 'locale.h'.)
*/
#if !defined(lua_getlocaledecpoint)
#define lua_getlocaledecpoint() (localeconv()->decimal_point[0])
#endif
/*
** macros to improve jump prediction, used mostly for error handling
** and debug facilities. (Some macros in the Lua API use these macros.
** Define LUA_NOBUILTIN if you do not want '__builtin_expect' in your
** code.)
*/
#if !defined(luai_likely)
#if defined(__GNUC__) && !defined(LUA_NOBUILTIN)
#define luai_likely(x) (__builtin_expect(((x) != 0), 1))
#define luai_unlikely(x) (__builtin_expect(((x) != 0), 0))
#else
#define luai_likely(x) (x)
#define luai_unlikely(x) (x)
#endif
#endif
#if defined(LUA_CORE) || defined(LUA_LIB)
/* shorter names for Lua's own use */
#define l_likely(x) luai_likely(x)
#define l_unlikely(x) luai_unlikely(x)
#endif
/* }================================================================== */
/*
** {==================================================================
** Language Variations
** =====================================================================
*/
/*
@@ LUA_NOCVTN2S/LUA_NOCVTS2N control how Lua performs some
** coercions. Define LUA_NOCVTN2S to turn off automatic coercion from
** numbers to strings. Define LUA_NOCVTS2N to turn off automatic
** coercion from strings to numbers.
*/
/* #define LUA_NOCVTN2S */
/* #define LUA_NOCVTS2N */
/*
@@ LUA_USE_APICHECK turns on several consistency checks on the C API.
** Define it as a help when debugging C code.
*/
#if defined(LUA_USE_APICHECK)
#include <assert.h>
#define luai_apicheck(l,e) assert(e)
#endif
/* }================================================================== */
/*
** {==================================================================
** Macros that affect the API and must be stable (that is, must be the
** same when you compile Lua and when you compile code that links to
** Lua).
** =====================================================================
*/
/*
@@ LUAI_MAXSTACK limits the size of the Lua stack.
** CHANGE it if you need a different limit. This limit is arbitrary;
** its only purpose is to stop Lua from consuming unlimited stack
** space (and to reserve some numbers for pseudo-indices).
** (It must fit into max(size_t)/32.)
*/
#if LUAI_IS32INT
#define LUAI_MAXSTACK 1000000
#else
#define LUAI_MAXSTACK 15000
#endif
/*
@@ LUA_EXTRASPACE defines the size of a raw memory area associated with
** a Lua state with very fast access.
** CHANGE it if you need a different size.
*/
#define LUA_EXTRASPACE (sizeof(void *))
/*
@@ LUA_IDSIZE gives the maximum size for the description of the source
@@ of a function in debug information.
** CHANGE it if you want a different size.
*/
#define LUA_IDSIZE 60
/*
@@ LUAL_BUFFERSIZE is the buffer size used by the lauxlib buffer system.
*/
#define LUAL_BUFFERSIZE ((int)(16 * sizeof(void*) * sizeof(lua_Number)))
/*
@@ LUAI_MAXALIGN defines fields that, when used in a union, ensure
** maximum alignment for the other items in that union.
*/
#define LUAI_MAXALIGN lua_Number n; double u; void *s; lua_Integer i; long l
/* }================================================================== */
/* =================================================================== */
/*
** Local configuration. You can use this space to add your redefinitions
** without modifying the main part of the file.
*/
#endif

View File

@ -0,0 +1,52 @@
/*
** $Id: lualib.h $
** Lua standard libraries
** See Copyright Notice in lua.h
*/
#ifndef lualib_h
#define lualib_h
#include "lua.h"
/* version suffix for environment variable names */
#define LUA_VERSUFFIX "_" LUA_VERSION_MAJOR "_" LUA_VERSION_MINOR
LUAMOD_API int (luaopen_base) (lua_State *L);
#define LUA_COLIBNAME "coroutine"
LUAMOD_API int (luaopen_coroutine) (lua_State *L);
#define LUA_TABLIBNAME "table"
LUAMOD_API int (luaopen_table) (lua_State *L);
#define LUA_IOLIBNAME "io"
LUAMOD_API int (luaopen_io) (lua_State *L);
#define LUA_OSLIBNAME "os"
LUAMOD_API int (luaopen_os) (lua_State *L);
#define LUA_STRLIBNAME "string"
LUAMOD_API int (luaopen_string) (lua_State *L);
#define LUA_UTF8LIBNAME "utf8"
LUAMOD_API int (luaopen_utf8) (lua_State *L);
#define LUA_MATHLIBNAME "math"
LUAMOD_API int (luaopen_math) (lua_State *L);
#define LUA_DBLIBNAME "debug"
LUAMOD_API int (luaopen_debug) (lua_State *L);
#define LUA_LOADLIBNAME "package"
LUAMOD_API int (luaopen_package) (lua_State *L);
/* open all previous libraries */
LUALIB_API void (luaL_openlibs) (lua_State *L);
#endif

38
modules/lua/lualib.h Normal file
View File

@ -0,0 +1,38 @@
#ifndef LUALIB_H
#define LUALIB_H
#include <stdio.h>
#include <stdarg.h>
#include <unistd.h>
#include <strings.h>
#include <string.h>
#include <stdlib.h>
#include "lua54/lua.h"
#include "lua54/lauxlib.h"
#include "lua54/lualib.h"
#define SLICE "slice"
typedef struct {
size_t len;
uint8_t* data;
} slice_t;
void lua_new_slice(lua_State*L, int n)
{
size_t nbytes = sizeof(slice_t) + n * 1U;
slice_t *a = (slice_t *)lua_newuserdata(L, nbytes);
a->data = &((char *)a)[sizeof(slice_t)];
luaL_getmetatable(L, SLICE);
lua_setmetatable(L, -2);
a->len = n;
}
slice_t * lua_check_slice(lua_State *L, int idx)
{
void *ud = luaL_checkudata(L, idx, SLICE);
luaL_argcheck(L, ud != NULL, idx, "`slice' expected");
return (slice_t *)ud;
}
#endif

63
modules/md.c Normal file
View File

@ -0,0 +1,63 @@
#include "lua/lualib.h"
#include "3rd/md4c/md4c-html.h"
static void md_process_output(const MD_CHAR *buf, MD_SIZE len, void *udata)
{
lua_State *L = (lua_State *)udata;
lua_pushlstring(L, buf, len);
lua_call(L, 1, 0);
lua_pushvalue(L, -1);
}
static int l_md_to_html(lua_State *L)
{
const char *input = luaL_checkstring(L, 1);
if (input == NULL)
{
//lua_pushstring(L,"NULL markdown input string");
return 0;
}
if (!lua_isfunction(L, -1))
{
//lua_pushstring(L,"Invalid callback function");
return 0;
}
// duplicate top of the stack
lua_pushvalue(L, -1);
reset_hd_cnt();
if (md_html(input,
strlen(input),
md_process_output,
L,
MD_DIALECT_GITHUB |
MD_HTML_FLAG_VERBATIM_ENTITIES |
MD_FLAG_PERMISSIVEATXHEADERS |
MD_FLAG_NOINDENTEDCODEBLOCKS |
MD_FLAG_NOHTMLBLOCKS |
MD_FLAG_NOHTMLSPANS |
MD_FLAG_NOHTML |
MD_FLAG_COLLAPSEWHITESPACE |
MD_FLAG_PERMISSIVEURLAUTOLINKS |
MD_FLAG_PERMISSIVEWWWAUTOLINKS |
MD_FLAG_PERMISSIVEEMAILAUTOLINKS |
MD_FLAG_PERMISSIVEAUTOLINKS |
MD_FLAG_UNDERLINE,
MD_HTML_FLAG_XHTML) == -1)
{
//lua_pushstring(L,"Unable to parse markdown: md_parse() fails");
lua_pop(L,1);
return 0;
}
lua_pop(L,1);
return 1;
}
static const struct luaL_Reg _lib[] = {
{"to_html", l_md_to_html},
{NULL, NULL}};
int luaopen_md(lua_State *L)
{
luaL_newlib(L, _lib);
return 1;
}

265
modules/sha1.c Normal file
View File

@ -0,0 +1,265 @@
/*
SHA-1 in C
By Steve Reid <sreid@sea-to-sky.net>
100% Public Domain
-----------------
Modified 7/98
By James H. Brown <jbrown@burgoyne.com>
Still 100% Public Domain
Corrected a problem which generated improper hash values on 16 bit machines
Routine SHA1Update changed from
void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned int
len)
to
void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned
long len)
The 'len' parameter was declared an int which works fine on 32 bit machines.
However, on 16 bit machines an int is too small for the shifts being done
against
it. This caused the hash function to generate incorrect values if len was
greater than 8191 (8K - 1) due to the 'len << 3' on line 3 of SHA1Update().
Since the file IO in main() reads 16K at a time, any file 8K or larger would
be guaranteed to generate the wrong hash (e.g. Test Vector #3, a million
"a"s).
I also changed the declaration of variables i & j in SHA1Update to
unsigned long from unsigned int for the same reason.
These changes should make no difference to any 32 bit implementations since
an
int and a long are the same size in those environments.
--
I also corrected a few compiler warnings generated by Borland C.
1. Added #include <process.h> for exit() prototype
2. Removed unused variable 'j' in SHA1Final
3. Changed exit(0) to return(0) at end of main.
ALL changes I made can be located by searching for comments containing 'JHB'
-----------------
Modified 8/98
By Steve Reid <sreid@sea-to-sky.net>
Still 100% public domain
1- Removed #include <process.h> and used return() instead of exit()
2- Fixed overwriting of finalcount in SHA1Final() (discovered by Chris Hall)
3- Changed email address from steve@edmweb.com to sreid@sea-to-sky.net
-----------------
Modified 4/01
By Saul Kravitz <Saul.Kravitz@celera.com>
Still 100% PD
Modified to run on Compaq Alpha hardware.
-----------------
Modified 07/2002
By Ralph Giles <giles@ghostscript.com>
Still 100% public domain
modified for use with stdint types, autoconf
code cleanup, removed attribution comments
switched SHA1Final() argument order for consistency
use SHA1_ prefix for public api
move public api to sha1.h
*/
/*
Test Vectors (from FIPS PUB 180-1)
"abc"
A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
A million repetitions of "a"
34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
*/
/* #define SHA1HANDSOFF */
#include "sha1.h"
void SHA1_Transform(uint32_t state[5], const uint8_t buffer[64]);
#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
/* blk0() and blk() perform the initial expand. */
/* I got the idea of expanding during the round function from SSLeay */
/* FIXME: can we do this in an endian-proof way? */
#ifdef WORDS_BIGENDIAN
#define blk0(i) block->l[i]
#else
#define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \
|(rol(block->l[i],8)&0x00FF00FF))
#endif
#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
^block->l[(i+2)&15]^block->l[i&15],1))
/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);
#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
#ifdef VERBOSE /* SAK */
void SHAPrintContext(SHA1_CTX *context, char *msg){
printf("%s (%d,%d) %x %x %x %x %x\n",
msg,
context->count[0], context->count[1],
context->state[0],
context->state[1],
context->state[2],
context->state[3],
context->state[4]);
}
#endif /* VERBOSE */
/* Hash a single 512-bit block. This is the core of the algorithm. */
void SHA1_Transform(uint32_t state[5], const uint8_t buffer[64])
{
uint32_t a, b, c, d, e;
typedef union {
uint8_t c[64];
uint32_t l[16];
} CHAR64LONG16;
CHAR64LONG16* block;
#ifdef SHA1HANDSOFF
static uint8_t workspace[64];
block = (CHAR64LONG16*)workspace;
memcpy(block, buffer, 64);
#else
block = (CHAR64LONG16*)buffer;
#endif
/* Copy context->state[] to working vars */
a = state[0];
b = state[1];
c = state[2];
d = state[3];
e = state[4];
/* 4 rounds of 20 operations each. Loop unrolled. */
R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
/* Add the working vars back into context.state[] */
state[0] += a;
state[1] += b;
state[2] += c;
state[3] += d;
state[4] += e;
/* Wipe variables */
a = b = c = d = e = 0;
}
/* SHA1Init - Initialize new context */
void SHA1_Init(SHA1_CTX* context)
{
/* SHA1 initialization constants */
context->state[0] = 0x67452301;
context->state[1] = 0xEFCDAB89;
context->state[2] = 0x98BADCFE;
context->state[3] = 0x10325476;
context->state[4] = 0xC3D2E1F0;
context->count[0] = context->count[1] = 0;
}
/* Run your data through this. */
void SHA1_Update(SHA1_CTX* context, const uint8_t* data, const size_t len)
{
size_t i, j;
#ifdef VERBOSE
SHAPrintContext(context, "before");
#endif
j = (context->count[0] >> 3) & 63;
if ((context->count[0] += len << 3) < (len << 3)) context->count[1]++;
context->count[1] += (len >> 29);
if ((j + len) > 63) {
memcpy(&context->buffer[j], data, (i = 64-j));
SHA1_Transform(context->state, context->buffer);
for ( ; i + 63 < len; i += 64) {
SHA1_Transform(context->state, data + i);
}
j = 0;
}
else i = 0;
memcpy(&context->buffer[j], &data[i], len - i);
#ifdef VERBOSE
SHAPrintContext(context, "after ");
#endif
}
/* Add padding and return the message digest. */
void SHA1_Final(uint8_t digest[SHA1_DIGEST_SIZE], SHA1_CTX* context)
{
uint32_t i;
uint8_t finalcount[8];
for (i = 0; i < 8; i++) {
finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)]
>> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */
}
SHA1_Update(context, (uint8_t *)"\200", 1);
while ((context->count[0] & 504) != 448) {
SHA1_Update(context, (uint8_t *)"\0", 1);
}
SHA1_Update(context, finalcount, 8); /* Should cause a SHA1_Transform() */
for (i = 0; i < SHA1_DIGEST_SIZE; i++) {
digest[i] = (uint8_t)
((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
}
/* Wipe variables */
i = 0;
memset(context->buffer, 0, 64);
memset(context->state, 0, 20);
memset(context->count, 0, 8);
memset(finalcount, 0, 8); /* SWR */
#ifdef SHA1HANDSOFF /* make SHA1Transform overwrite its own static vars */
SHA1_Transform(context->state, context->buffer);
#endif
}
void digest_to_hex(const uint8_t digest[SHA1_DIGEST_SIZE], char *output)
{
int i,j;
char *c = output;
for (i = 0; i < SHA1_DIGEST_SIZE/4; i++) {
for (j = 0; j < 4; j++) {
sprintf(c,"%02x", digest[i*4+j]);
c += 2;
}
//sprintf(c, " ");
//c += 1;
}
*c = '\0';
}

23
modules/sha1.h Normal file
View File

@ -0,0 +1,23 @@
/* public api for steve reid's public domain SHA-1 implementation */
/* this file is in the public domain */
#ifndef __SHA1_H
#define __SHA1_H
#include <stdio.h>
#include <string.h>
#include <stdint.h>
typedef struct {
uint32_t state[5];
uint32_t count[2];
uint8_t buffer[64];
} SHA1_CTX;
#define SHA1_DIGEST_SIZE 20
void SHA1_Init(SHA1_CTX* context);
void SHA1_Update(SHA1_CTX* context, const uint8_t* data, const size_t len);
void SHA1_Final(uint8_t digest[SHA1_DIGEST_SIZE], SHA1_CTX* context);
void digest_to_hex(const uint8_t digest[SHA1_DIGEST_SIZE], char *output);
#endif /* __SHA1_H */

141
modules/slice.c Normal file
View File

@ -0,0 +1,141 @@
#include "lua/lualib.h"
void lua_new_light_slice(lua_State *L, int n, char *ptr)
{
size_t nbytes = sizeof(slice_t);
slice_t *a = (slice_t *)lua_newuserdata(L, nbytes);
a->len = n;
a->data = ptr;
luaL_getmetatable(L, SLICE);
lua_setmetatable(L, -2);
}
static int l_new_slice(lua_State *L)
{
int n = luaL_checknumber(L, 1);
lua_new_slice(L, n);
return 1; /* new userdatum is already on the stack */
}
static int l_new_lightslice(lua_State *L)
{
uint8_t* ptr = NULL;
if(lua_isnumber(L,1))
{
size_t addr = luaL_checknumber(L, 1);
ptr = (uint8_t*) addr;
}
else
{
ptr = lua_touserdata(L, 1);
}
int n = luaL_checknumber(L, 2);
lua_new_light_slice(L, n, ptr);
return 1; /* new userdatum is already on the stack */
}
static unsigned char *get_sel(lua_State *L)
{
slice_t *a = lua_check_slice(L, 1);
int index = luaL_checknumber(L, 2);
luaL_argcheck(L, 1 <= index && index <= a->len, 2,
"index out of range");
/* return element address */
return &a->data[index - 1];
}
static int l_set_slice(lua_State *L)
{
unsigned char value = luaL_checknumber(L, 3);
*get_sel(L) = value;
return 0;
}
static int l_get_slice_size(lua_State *L)
{
slice_t *a = lua_check_slice(L, 1);
lua_pushnumber(L, a->len);
return 1;
}
static int l_slice_write(lua_State *L)
{
slice_t *a = lua_check_slice(L, 1);
const char *f = luaL_checkstring(L, 2);
FILE *fp;
fp = fopen(f, "wb");
if (!fp)
lua_pushboolean(L, 0);
else
{
fwrite(a->data, 1, a->len, fp);
lua_pushboolean(L, 1);
fclose(fp);
}
return 1;
}
static int l_slice_index(lua_State *L)
{
if(lua_isnumber(L,2))
{
lua_pushnumber(L, *get_sel(L));
}
else if(lua_isstring(L,2))
{
const char* string = luaL_checkstring(L,2);
if(strcmp(string,"size") == 0)
{
lua_pushcfunction(L, l_get_slice_size);
}
else if(strcmp(string, "write") == 0)
{
lua_pushcfunction(L, l_slice_write);
}
else
{
lua_pushnil(L);
}
return 1;
}
else
{
lua_pushnil(L);
}
return 1;
}
static int l_slice_to_string(lua_State *L)
{
slice_t *a = lua_check_slice(L, 1);
char *d = (char *)malloc(a->len + 1);
memcpy(d, a->data, a->len);
d[a->len] = '\0';
lua_pushstring(L, d);
if (d)
free(d);
return 1;
}
static const struct luaL_Reg slicemetalib[] = {
{"unew", l_new_lightslice},
{"new", l_new_slice},
{NULL, NULL}};
static const struct luaL_Reg slicelib[] = {
{"__index", l_slice_index},
{"__newindex", l_set_slice},
{"__tostring", l_slice_to_string},
{NULL, NULL}};
int luaopen_slice(lua_State *L)
{
luaL_newmetatable(L, SLICE);
luaL_setfuncs(L, slicelib, 0);
luaL_newlib(L, slicemetalib);
return 1;
}

125
modules/sqlitedb.c Normal file
View File

@ -0,0 +1,125 @@
#include <sqlite3.h>
#include "lua/lualib.h"
typedef sqlite3 *sqldb;
static int l_getdb(lua_State *L)
{
const char *file = luaL_checkstring(L, 1);
sqlite3 *db;
int rc = sqlite3_open(file, &db);
if (rc != SQLITE_OK)
{
lua_pushnil(L);
lua_pushstring(L,sqlite3_errmsg(db));
sqlite3_close(db);
return 2;
}
lua_pushlightuserdata(L, db);
return 1;
}
static int l_db_close(lua_State *L)
{
sqldb db = (sqldb)lua_touserdata(L, 1);
if (db)
{
sqlite3_close(db);
}
db = NULL;
return 0;
}
static int l_db_exec(lua_State *L)
{
sqldb db = (sqldb)lua_touserdata(L, 1);
const char *sql = luaL_checkstring(L, 2);
int r = 0;
if(!db)
{
lua_pushboolean(L, 0);
lua_pushstring(L,"Invalid database handle");
return 2;
}
char *err_msg = 0;
sqlite3_mutex_enter(sqlite3_db_mutex(db));
int rc = sqlite3_exec(db, sql, NULL, 0, &err_msg);
sqlite3_mutex_leave(sqlite3_db_mutex(db));
if (rc != SQLITE_OK)
{
lua_pushboolean(L, 0);
lua_pushstring(L,err_msg);
sqlite3_free(err_msg);
return 2;
}
lua_pushboolean(L, 1);
return 1;
}
static int l_db_lastid(lua_State *L)
{
sqldb db = (sqldb)lua_touserdata(L, 1);
int idx = -1;
if (db)
idx = sqlite3_last_insert_rowid(db);
lua_pushnumber(L, idx);
return 1;
}
static int l_db_query(lua_State *L)
{
sqldb db = (sqldb)lua_touserdata(L, 1);
const char *query = luaL_checkstring(L, 2);
if (!db)
{
lua_pushnil(L);
return 1;
}
sqlite3_stmt *statement;
if (sqlite3_prepare_v2(db, query, -1, &statement, 0) == SQLITE_OK)
{
int cols = sqlite3_column_count(statement);
int result = 0;
int cnt = 1;
// new table for data
lua_newtable(L);
while ((result = sqlite3_step(statement)) == SQLITE_ROW)
{
lua_pushnumber(L, cnt);
lua_newtable(L);
for (int col = 0; col < cols; col++)
{
const char *value = (const char *)sqlite3_column_text(statement, col);
const char *name = sqlite3_column_name(statement, col);
lua_pushstring(L, name);
lua_pushstring(L, value);
lua_settable(L, -3);
}
lua_settable(L, -3);
cnt++;
}
sqlite3_finalize(statement);
}
else
{
lua_pushnil(L);
lua_pushstring(L, sqlite3_errmsg(db));
return 2;
}
return 1;
}
static const struct luaL_Reg sqlite[] = {
{"db", l_getdb},
{"dbclose", l_db_close},
{"query", l_db_query},
{"last_insert_id", l_db_lastid},
{"exec", l_db_exec},
{NULL, NULL}};
int luaopen_sqlitedb(lua_State *L)
{
luaL_newlib(L, sqlite);
return 1;
}

704
modules/stmr.c Normal file
View File

@ -0,0 +1,704 @@
/* This is the Porter stemming algorithm, coded up in ANSI C by the
* author. It may be be regarded as canonical, in that it follows the
* algorithm presented in
*
* Porter, 1980, An algorithm for suffix stripping, Program, Vol. 14,
* no. 3, pp 130-137,
*
* only differing from it at the points marked --DEPARTURE-- below.
*
* See also http://www.tartarus.org/~martin/PorterStemmer
*
* The algorithm as described in the paper could be exactly replicated
* by adjusting the points of DEPARTURE, but this is barely necessary,
* because (a) the points of DEPARTURE are definitely improvements, and
* (b) no encoding of the Porter stemmer I have seen is anything like
* as exact as this version, even with the points of DEPARTURE!
*
* You can compile it on Unix with 'gcc -O3 -o stem stem.c' after which
* 'stem' takes a list of inputs and sends the stemmed equivalent to
* stdout.
*
* The algorithm as encoded here is particularly fast.
*
* Release 1: was many years ago
* Release 2: 11 Apr 2013
* fixes a bug noted by Matt Patenaude <matt@mattpatenaude.com>,
*
* case 'o': if (ends("\03" "ion") && (b[j] == 's' || b[j] == 't')) break;
* ==>
* case 'o': if (ends("\03" "ion") && j >= k0 && (b[j] == 's' || b[j] == 't')) break;
*
* to avoid accessing b[k0-1] when the word in b is "ion".
* Release 3: 25 Mar 2014
* fixes a similar bug noted by Klemens Baum <klemensbaum@gmail.com>,
* that if step1ab leaves a one letter result (ied -> i, aing -> a etc),
* step2 and step4 access the byte before the first letter. So we skip
* steps after step1ab unless k > k0. */
#include <string.h>
#include "lua/lualib.h"
#define TRUE 1
#define FALSE 0
/* The main part of the stemming algorithm starts here. b is a buffer
* holding a word to be stemmed. The letters are in b[k0], b[k0+1] ...
* ending at b[k]. In fact k0 = 0 in this demo program. k is readjusted
* downwards as the stemming progresses. Zero termination is not in fact
* used in the algorithm.
*
* Note that only lower case sequences are stemmed. Forcing to lower case
* should be done before stem(...) is called. */
/* buffer for word to be stemmed */
static char *b;
static int k;
static int k0;
/* j is a general offset into the string */
static int j;
/**
* TRUE when `b[i]` is a consonant.
*/
static int
isConsonant(int index) {
switch (b[index]) {
case 'a':
case 'e':
case 'i':
case 'o':
case 'u':
return FALSE;
case 'y':
return (index == k0) ? TRUE : !isConsonant(index - 1);
default:
return TRUE;
}
}
/* Measure the number of consonant sequences between
* `k0` and `j`. If C is a consonant sequence and V
* a vowel sequence, and <..> indicates arbitrary
* presence:
*
* <C><V> gives 0
* <C>VC<V> gives 1
* <C>VCVC<V> gives 2
* <C>VCVCVC<V> gives 3
* ....
*/
static int
getMeasure() {
int position;
int index;
position = 0;
index = k0;
while (TRUE) {
if (index > j) {
return position;
}
if (!isConsonant(index)) {
break;
}
index++;
}
index++;
while (TRUE) {
while (TRUE) {
if (index > j) {
return position;
}
if (isConsonant(index)) {
break;
}
index++;
}
index++;
position++;
while (TRUE) {
if (index > j) {
return position;
}
if (!isConsonant(index)) {
break;
}
index++;
}
index++;
}
}
/* `TRUE` when `k0, ... j` contains a vowel. */
static int
vowelInStem() {
int index;
index = k0 - 1;
while (++index <= j) {
if (!isConsonant(index)) {
return TRUE;
}
}
return FALSE;
}
/* `TRUE` when `j` and `(j-1)` are the same consonant. */
static int
isDoubleConsonant(int index) {
if (b[index] != b[index - 1]) {
return FALSE;
}
return isConsonant(index);
}
/* `TRUE` when `i - 2, i - 1, i` has the form
* `consonant - vowel - consonant` and also if the second
* C is not `"w"`, `"x"`, or `"y"`. this is used when
* trying to restore an `e` at the end of a short word.
*
* Such as:
*
* `cav(e)`, `lov(e)`, `hop(e)`, `crim(e)`, but `snow`,
* `box`, `tray`.
*/
static int
cvc(int index) {
int character;
if (index < k0 + 2 || !isConsonant(index) || isConsonant(index - 1) || !isConsonant(index - 2)) {
return FALSE;
}
character = b[index];
if (character == 'w' || character == 'x' || character == 'y') {
return FALSE;
}
return TRUE;
}
/* `ends(s)` is `TRUE` when `k0, ...k` ends with `value`. */
static int
ends(const char *value) {
int length = value[0];
/* Tiny speed-up. */
if (value[length] != b[k]) {
return FALSE;
}
if (length > k - k0 + 1) {
return FALSE;
}
if (memcmp(b + k - length + 1, value + 1, length) != 0) {
return FALSE;
}
j = k - length;
return TRUE;
}
/* `setTo(value)` sets `(j + 1), ...k` to the characters in
* `value`, readjusting `k`. */
static void
setTo(const char *value) {
int length = value[0];
memmove(b + j + 1, value + 1, length);
k = j + length;
}
/* Set string. */
static void
replace(const char *value) {
if (getMeasure() > 0) {
setTo(value);
}
}
/* `step1ab()` gets rid of plurals, `-ed`, `-ing`.
*
* Such as:
*
* caresses -> caress
* ponies -> poni
* ties -> ti
* caress -> caress
* cats -> cat
*
* feed -> feed
* agreed -> agree
* disabled -> disable
*
* matting -> mat
* mating -> mate
* meeting -> meet
* milling -> mill
* messing -> mess
*
* meetings -> meet
*/
static void
step1ab() {
int character;
if (b[k] == 's') {
if (ends("\04" "sses")) {
k -= 2;
} else if (ends("\03" "ies")) {
setTo("\01" "i");
} else if (b[k - 1] != 's') {
k--;
}
}
if (ends("\03" "eed")) {
if (getMeasure() > 0) {
k--;
}
} else if ((ends("\02" "ed") || ends("\03" "ing")) && vowelInStem()) {
k = j;
if (ends("\02" "at")) {
setTo("\03" "ate");
} else if (ends("\02" "bl")) {
setTo("\03" "ble");
} else if (ends("\02" "iz")) {
setTo("\03" "ize");
} else if (isDoubleConsonant(k)) {
k--;
character = b[k];
if (character == 'l' || character == 's' || character == 'z') {
k++;
}
} else if (getMeasure() == 1 && cvc(k)) {
setTo("\01" "e");
}
}
}
/* `step1c()` turns terminal `"y"` to `"i"` when there
* is another vowel in the stem. */
static void
step1c() {
if (ends("\01" "y") && vowelInStem()) {
b[k] = 'i';
}
}
/* `step2()` maps double suffices to single ones.
* so -ization ( = -ize plus -ation) maps to -ize etc.
* note that the string before the suffix must give
* getMeasure() > 0. */
static void
step2() {
switch (b[k - 1]) {
case 'a':
if (ends("\07" "ational")) {
replace("\03" "ate");
break;
}
if (ends("\06" "tional")) {
replace("\04" "tion");
break;
}
break;
case 'c':
if (ends("\04" "enci")) {
replace("\04" "ence");
break;
}
if (ends("\04" "anci")) {
replace("\04" "ance");
break;
}
break;
case 'e':
if (ends("\04" "izer")) {
replace("\03" "ize");
break;
}
break;
case 'l':
/* --DEPARTURE--: To match the published algorithm,
* replace this line with:
*
* ```
* if (ends("\04" "abli")) {
* replace("\04" "able");
*
* break;
* }
* ```
*/
if (ends("\03" "bli")) {
replace("\03" "ble");
break;
}
if (ends("\04" "alli")) {
replace("\02" "al");
break;
}
if (ends("\05" "entli")) {
replace("\03" "ent");
break;
}
if (ends("\03" "eli")) {
replace("\01" "e");
break;
}
if (ends("\05" "ousli")) {
replace("\03" "ous");
break;
}
break;
case 'o':
if (ends("\07" "ization")) {
replace("\03" "ize");
break;
}
if (ends("\05" "ation")) {
replace("\03" "ate");
break;
}
if (ends("\04" "ator")) {
replace("\03" "ate");
break;
}
break;
case 's':
if (ends("\05" "alism")) {
replace("\02" "al");
break;
}
if (ends("\07" "iveness")) {
replace("\03" "ive");
break;
}
if (ends("\07" "fulness")) {
replace("\03" "ful");
break;
}
if (ends("\07" "ousness")) {
replace("\03" "ous");
break;
}
break;
case 't':
if (ends("\05" "aliti")) {
replace("\02" "al");
break;
}
if (ends("\05" "iviti")) {
replace("\03" "ive");
break;
}
if (ends("\06" "biliti")) {
replace("\03" "ble");
break;
}
break;
/* --DEPARTURE--: To match the published algorithm, delete this line. */
case 'g':
if (ends("\04" "logi")) {
replace("\03" "log");
break;
}
}
}
/* `step3()` deals with -ic-, -full, -ness etc.
* similar strategy to step2. */
static void
step3() {
switch (b[k]) {
case 'e':
if (ends("\05" "icate")) {
replace("\02" "ic");
break;
}
if (ends("\05" "ative")) {
replace("\00" "");
break;
}
if (ends("\05" "alize")) {
replace("\02" "al");
break;
}
break;
case 'i':
if (ends("\05" "iciti")) {
replace("\02" "ic");
break;
}
break;
case 'l':
if (ends("\04" "ical")) {
replace("\02" "ic");
break;
}
if (ends("\03" "ful")) {
replace("\00" "");
break;
}
break;
case 's':
if (ends("\04" "ness")) {
replace("\00" "");
break;
}
break;
}
}
/* `step4()` takes off -ant, -ence etc., in
* context <c>vcvc<v>. */
static void
step4() {
switch (b[k - 1]) {
case 'a':
if (ends("\02" "al")) {
break;
}
return;
case 'c':
if (ends("\04" "ance")) {
break;
}
if (ends("\04" "ence")) {
break;
}
return;
case 'e':
if (ends("\02" "er")) {
break;
}
return;
case 'i':
if (ends("\02" "ic")) {
break;
}
return;
case 'l':
if (ends("\04" "able")) {
break;
}
if (ends("\04" "ible")) {
break;
}
return;
case 'n':
if (ends("\03" "ant")) {
break;
}
if (ends("\05" "ement")) {
break;
}
if (ends("\04" "ment")) {
break;
}
if (ends("\03" "ent")) {
break;
}
return;
case 'o':
if (ends("\03" "ion") && j >= k0 && (b[j] == 's' || b[j] == 't')) {
break;
}
/* takes care of -ous */
if (ends("\02" "ou")) {
break;
}
return;
case 's':
if (ends("\03" "ism")) {
break;
}
return;
case 't':
if (ends("\03" "ate")) {
break;
}
if (ends("\03" "iti")) {
break;
}
return;
case 'u':
if (ends("\03" "ous")) {
break;
}
return;
case 'v':
if (ends("\03" "ive")) {
break;
}
return;
case 'z':
if (ends("\03" "ize")) {
break;
}
return;
default:
return;
}
if (getMeasure() > 1) {
k = j;
}
}
/* `step5()` removes a final `-e` if `getMeasure()` is
* greater than `1`, and changes `-ll` to `-l` if
* `getMeasure()` is greater than `1`. */
static void
step5() {
int a;
j = k;
if (b[k] == 'e') {
a = getMeasure();
if (a > 1 || (a == 1 && !cvc(k - 1))) {
k--;
}
}
if (b[k] == 'l' && isDoubleConsonant(k) && getMeasure() > 1) {
k--;
}
}
/* In `stem(p, i, j)`, `p` is a `char` pointer, and the
* string to be stemmed is from `p[i]` to
* `p[j]` (inclusive).
*
* Typically, `i` is zero and `j` is the offset to the
* last character of a string, `(p[j + 1] == '\0')`.
* The stemmer adjusts the characters `p[i]` ... `p[j]`
* and returns the new end-point of the string, `k`.
*
* Stemming never increases word length, so `i <= k <= j`.
*
* To turn the stemmer into a module, declare 'stem' as
* extern, and delete the remainder of this file. */
int
stem(char *p, int index, int position) {
/* Copy the parameters into statics. */
b = p;
k = position;
k0 = index;
if (k <= k0 + 1) {
return k; /* --DEPARTURE-- */
}
/* With this line, strings of length 1 or 2 don't
* go through the stemming process, although no
* mention is made of this in the published
* algorithm. Remove the line to match the published
* algorithm. */
step1ab();
if (k > k0) {
step1c();
step2();
step3();
step4();
step5();
}
return k;
}
static int l_stmr(lua_State* L)
{
char* word = strdup(luaL_checkstring(L,1));
int end = stem(word, 0, strlen(word) - 1);
word[end + 1] = 0;
lua_pushstring(L, word);
free(word);
return 1;
}
static const struct luaL_Reg _lib [] = {
{"stmr", l_stmr},
{NULL,NULL}
};
int luaopen_stmr(lua_State *L)
{
luaL_newlib(L, _lib);
return 1;
}

866
modules/ulib.c Normal file
View File

@ -0,0 +1,866 @@
#ifdef LINUX
#include <unistd.h>
#include <limits.h>
#include <pwd.h>
#include <shadow.h>
#include <fcntl.h>
#include <sys/sendfile.h>
#endif
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <dirent.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <errno.h>
#include <pwd.h>
#include <grp.h>
#include <ctype.h>
#include "lua/lualib.h"
// zip library
#include "3rd/zip/zip.h"
#define MAX_PATH_LEN 1024
/**
* Trim a string by a character on both ends
* @param str The target string
* @param delim the delim
*/
static void trim(char* str, const char delim)
{
if(!str || strlen(str) == 0) return;
char * p = str;
int l = strlen(p);
while(l > 0 && p[l - 1] == delim)
p[--l] = 0;
while(* p && (* p) == delim ) ++p, --l;
memmove(str, p, l + 1);
}
static int l_check_login (lua_State *L) {
#ifdef LINUX
const char* username = luaL_checkstring(L,1);
const char* password = luaL_checkstring(L,2);
char *encrypted;
struct passwd *pwd;
struct spwd *spwd;
/* Look up password and shadow password records for username */
pwd = getpwnam(username);
if (pwd == NULL)
{
lua_pushboolean(L,0);
lua_pushstring(L, strerror(errno));
return 2;
}
spwd = getspnam(username);
/*if (spwd == NULL)
{
lua_pushboolean(L,0);
printf("no permission to read shadow password file\n");
return 1;
}*/
if (spwd != NULL) /* If there is a shadow password record */
{
pwd->pw_passwd = spwd->sp_pwdp; /* Use the shadow password */
}
/*else
{
lua_pushboolean(L,0);
printf("shadow record is null \n" );
return 1;
}*/
/* Encrypt password and erase cleartext version immediately */
encrypted = crypt(password, pwd->pw_passwd);
if (encrypted == NULL)
{
lua_pushboolean(L,0);
lua_pushstring(L, strerror(errno));
return 2;
}
if(strcmp(encrypted, pwd->pw_passwd) == 0)
{
lua_pushboolean(L,1);
return 1;
} else
{
lua_pushboolean(L,0);
return 1;
}
#else
// macos
// just pass the check, for test only
lua_pushboolean(L,0);
lua_pushstring(L, "Login by shadow passwd is not supported on this system");
return 2;
#endif
}
static void get_userId(const char* name, uid_t* uid,gid_t* gid )
{
struct passwd *pwd;
uid_t u;
char *endptr;
if (name == NULL || *name == '\0')
{
*uid = -1; *gid=-1;
return;
}
u = strtol(name, &endptr, 10);
if (*endptr == '\0')
{
*uid = u;
*gid = -1;
return;
}
pwd = getpwnam(name);
if (pwd == NULL)
{
*uid = -1; *gid=-1;
return;
}
*uid = pwd->pw_uid;
*gid = pwd->pw_gid;
}
static int l_fork(lua_State* L)
{
int pid = fork();
lua_pushnumber(L, pid);
return 1;
}
static int l_waitpid(lua_State* L)
{
int pid = luaL_checknumber(L,1);
int nohang = luaL_checknumber(L,2);
pid_t st;
int status;
if(nohang)
{
st = waitpid(pid, &status, WNOHANG);
}
else
{
st = waitpid(pid, &status, 0);
}
lua_pushnumber(L, st);
return 1;
}
static int l_kill(lua_State* L)
{
int pid = luaL_checknumber(L,1);
if(pid == -1) pid = getpid();
int status = kill(pid, SIGHUP);
lua_pushnumber(L, status);
return 1;
}
static int l_setuid(lua_State* L)
{
uid_t uid = (uid_t) luaL_checknumber(L,1);
if((int)uid != -1)
{
if(setuid(uid) < 0)
{
lua_pushboolean(L,0);
lua_pushstring(L, strerror(errno));
return 2;
}
else
{
//printf("UID set\n");
lua_pushboolean(L,1);
return 1;
}
}
else
lua_pushboolean(L,0);
return 1;
}
static int l_setgid(lua_State* L)
{
uid_t gid = (uid_t) luaL_checknumber(L,1);
if((int)gid != -1)
{
if(setgid(gid) < 0)
{
lua_pushboolean(L,0);
lua_pushstring(L, strerror(errno));
return 2;
}
else
{
//printf("GID set\n");
lua_pushboolean(L,1);
return 1;
}
}
else
lua_pushboolean(L,0);
return 1;
}
static int l_getuid(lua_State* L)
{
const char* name = luaL_checkstring(L,1);
uid_t uid = -1;
uid_t gid = -1;
get_userId(name,&uid,&gid);
lua_newtable(L);
lua_pushstring(L,"id");
lua_pushnumber(L,uid);
lua_settable(L,-3);
lua_pushstring(L,"gid");
lua_pushnumber(L,gid);
lua_settable(L,-3);
int j, ngroups = 30; //only first 10 group
gid_t *groups;
struct group *gr;
// find all the groups
if((int)uid != -1 && (int)gid != -1)
{
/* Retrieve group list */
groups = malloc(ngroups * sizeof (gid_t));
if (groups == NULL) {
return 1;
}
if (getgrouplist(name, gid, groups, &ngroups) == -1) {
free(groups);
return 1;
}
/* retrieved groups, along with group names */
lua_pushstring(L,"groups");
lua_newtable(L);
for (j = 0; j < ngroups; j++) {
gr = getgrgid(groups[j]);
if (gr != NULL)
{
//printf("%d: (%s)\n", groups[j],gr->gr_name);
lua_pushnumber(L, groups[j]);
lua_pushstring(L, gr->gr_name);
lua_settable(L,-3);
}
}
lua_settable(L, -3);
free(groups);
}
return 1;
}
static void timestr(time_t time, char* buf,int len,char* format, int gmt)
{
struct tm t;
if(gmt)
{
gmtime_r(&time, &t);
}
else
{
localtime_r(&time, &t);
}
strftime(buf, len, format, &t);
}
static int l_file_stat(lua_State* L, const char* path)
{
//const char* path = luaL_checkstring(L,-1);
//printf("PATH %s\n", path);
//lua_pop(L,1);
char date[64];
struct stat st;
if( stat(path, &st) == 0 )
{
// recore for file
lua_newtable(L);
//type
lua_pushstring(L,"type");
if(S_ISDIR(st.st_mode))
lua_pushstring(L,"dir");
else
lua_pushstring(L,"file");
lua_settable(L,-3);
//ctime
lua_pushstring(L,"ctime");
timestr(st.st_ctime,date,sizeof(date),"%a, %d %b %Y %H:%M:%S GMT",1);
lua_pushstring(L,date);
lua_settable(L,-3);
//mtime
lua_pushstring(L,"mtime");
timestr(st.st_mtime,date,sizeof(date),"%a, %d %b %Y %H:%M:%S GMT",1);
lua_pushstring(L,date);
lua_settable(L,-3);
//size
lua_pushstring(L,"size");
lua_pushnumber(L,st.st_size);
lua_settable(L,-3);
// uid
lua_pushstring(L,"uid");
lua_pushnumber(L,st.st_uid);
lua_settable(L,-3);
// gid
lua_pushstring(L,"gid");
lua_pushnumber(L,st.st_gid);
lua_settable(L,-3);
lua_pushstring(L,"permissions");
sprintf(date," (%3o)", st.st_mode&0777);
lua_pushstring(L,date);
lua_settable(L,-3);
// permission
lua_pushstring(L,"perm");
lua_newtable(L);
lua_pushstring(L,"owner");
lua_newtable(L);
lua_pushstring(L,"read");
lua_pushboolean(L, (st.st_mode & S_IRUSR)==0?0:1);
lua_settable(L,-3);
lua_pushstring(L,"write");
lua_pushboolean(L, (st.st_mode & S_IWUSR)==0?0:1);
lua_settable(L,-3);
lua_pushstring(L,"exec");
lua_pushboolean(L, (st.st_mode & S_IXUSR)==0?0:1);
lua_settable(L,-3);
//end owner
lua_settable(L,-3);
lua_pushstring(L,"group");
lua_newtable(L);
lua_pushstring(L,"read");
lua_pushboolean(L, (st.st_mode & S_IRGRP)==0?0:1);
lua_settable(L,-3);
lua_pushstring(L,"write");
lua_pushboolean(L, (st.st_mode & S_IWGRP)==0?0:1);
lua_settable(L,-3);
lua_pushstring(L,"exec");
lua_pushboolean(L, (st.st_mode & S_IXGRP)==0?0:1);
lua_settable(L,-3);
//end owner
lua_settable(L,-3);
lua_pushstring(L,"other");
lua_newtable(L);
lua_pushstring(L,"read");
lua_pushboolean(L, (st.st_mode & S_IROTH)==0?0:1);
lua_settable(L,-3);
lua_pushstring(L,"write");
lua_pushboolean(L, (st.st_mode & S_IWOTH)==0?0:1);
lua_settable(L,-3);
lua_pushstring(L,"exec");
lua_pushboolean(L, (st.st_mode & S_IXOTH)==0?0:1);
lua_settable(L,-3);
//end owner
lua_settable(L,-3);
// end permission
lua_settable(L,-3);
}
else
{
lua_newtable(L);
}
return 1;
}
static int l_file_info(lua_State* L)
{
const char* path = luaL_checkstring(L,1);
l_file_stat(L,path);
return 1;
}
static int l_file_move(lua_State* L)
{
const char* old = luaL_checkstring(L,1);
const char* new = luaL_checkstring(L,2);
//printf("MOVE %s to %s\n",old,new);
lua_pushboolean(L, rename(old,new)==0);
return 1;
}
static int l_send_file(lua_State* L)
{
int fromfd, tofd, ret;
size_t sz = 0;
char* old = NULL;
char* new = NULL;
if(lua_isnumber(L,1))
{
fromfd = (int)luaL_checknumber(L,1);
}
else
{
old = (char*)luaL_checkstring(L,1);
if((fromfd = open(old, O_RDONLY)) < 0)
{
lua_pushboolean(L,0);
goto end_send_file;
}
struct stat st;
if(stat(old, &st)!=0)
{
lua_pushboolean(L,0);
goto end_send_file;
}
sz = st.st_size;
}
if(lua_isnumber(L,2))
{
tofd = (int) luaL_checknumber(L,2);
}
else
{
new = (char*)luaL_checkstring(L,2);
if (unlink(new) < 0 && errno != ENOENT) {
lua_pushboolean(L,0);
goto end_send_file;
}
if((tofd = open(new, O_WRONLY | O_CREAT, 0600)) < 0)
{
lua_pushboolean(L,0);
goto end_send_file;
}
}
if(lua_isnumber(L,3))
{
sz = (size_t) luaL_checknumber(L,3);
}
off_t off = 0;
int read = 0;
while (
sz > 0 &&
read != sz &&
(
((ret = sendfile(tofd, fromfd, &off, sz - read)) >= 0) ||
(errno == EAGAIN)
)
)
{
if(ret < 0) ret = 0;
read += ret;
}
if(read != sz)
{
lua_pushboolean(L,0);
goto end_send_file;
}
lua_pushboolean(L,1);
end_send_file:
if(fromfd >= 0 && old)
{
(void) close(fromfd);
}
if(tofd >= 0 && new)
{
(void) close(tofd);
}
return 1;
}
static int l_read_dir(lua_State* L)
{
const char* path = luaL_checkstring(L,1);
const char* prefix = luaL_checkstring(L,2);
DIR *d;
struct dirent *dir;
d = opendir(path);
char buff[1024];
lua_newtable(L);
if (d)
{
int id=0;
while ((dir = readdir(d)) != NULL)
{
//ignore curent directory, parent directory
if(strcmp(dir->d_name,".") == 0 ||
strcmp(dir->d_name,"..")==0/*|| *(dir->d_name)=='.'*/) continue;
sprintf(buff,"%s/%s",path,dir->d_name);
lua_pushnumber(L,id);
//lua_pushstring(L,buff);
l_file_stat(L,buff);
lua_pushstring(L,"filename");
lua_pushstring(L,dir->d_name);
lua_settable(L,-3);
sprintf(buff,"%s/%s",prefix,dir->d_name);
//printf("FILE %s\n",buff );
lua_pushstring(L,"path");
lua_pushstring(L,buff);
lua_settable(L,-3);
lua_settable(L,-3);
id++;
}
closedir(d);
}
else
{
lua_pushstring(L,"error");
lua_pushstring(L,"Resource not found");
lua_settable(L,-3);
}
return 1;
}
static int l_chown(lua_State* L)
{
const char* path = luaL_checkstring(L,1);
int id = luaL_checknumber(L,2);
int gid = luaL_checknumber(L,3);
lua_pushboolean(L, chown(path,id,gid) == 0);
return 1;
}
static int l_mkdir(lua_State* L)
{
const char* path = luaL_checkstring(L,1);
lua_pushboolean(L, mkdir(path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) == 0);
return 1;
}
static int l_exist(lua_State* L)
{
const char* path = luaL_checkstring(L,1);
lua_pushboolean(L,access( path, F_OK ) != -1 );
return 1;
}
static int is_dir(const char* f)
{
struct stat st;
if(stat(f, &st) == -1)
return -1; // unknow
else if(st.st_mode & S_IFDIR)
return 1;
else
return 0;
}
static int l_is_dir(lua_State *L)
{
const char *file = (char *)luaL_checkstring(L, 1);
lua_pushboolean(L, is_dir(file) == 1);
return 1;
}
static int _recursive_delete(const char* path)
{
if(is_dir(path) == 1)
{
DIR *d;
struct dirent *dir;
d = opendir(path);
char buff[1024];
if (d)
{
while ((dir = readdir(d)) != NULL)
{
//ignore current & parent dir
if(strcmp(dir->d_name,".") == 0 || strcmp(dir->d_name,"..")==0) continue;
// get the file
sprintf(buff,"%s/%s",path,dir->d_name);
if(_recursive_delete(buff) == -1) return -1;
}
closedir(d);
// remove dir
if(rmdir(path) != 0) return -1;
} else return -1;
}
else
{
// file remove
if(remove(path) != 0) return -1;
}
return 0;
}
static int l_delete(lua_State* L)
{
const char* f = luaL_checkstring(L,1);
lua_pushboolean(L,_recursive_delete(f) == 0);
return 1;
}
/*
* Only use this for simple command
* Be careful to expose this function
* to user. This can cause a serious
* security problem
*/
static int l_cmd_open(lua_State* L)
{
FILE *fp;
const char* cmd = luaL_checkstring(L,1);
/* Open the command for reading. */
fp = popen(cmd, "r");
if (fp == NULL) {
lua_pushnil(L);
return 1;
}
/*return the fd to lua
* user can use this to
read the output data*/
intptr_t pt = (intptr_t)fp;
lua_pushnumber(L, pt);
return 1;
}
static int l_cmd_read(lua_State* L)
{
/* Read the output a line at a time - output it.
read user defined data of std
*/
FILE * fd;
intptr_t pt = (intptr_t)luaL_checknumber(L, 1);
fd = (FILE*)pt;
if(fd == NULL)
{
lua_pushnil(L);
return 1;
}
char buff[1024];
if(fgets(buff, sizeof(buff)-1, fd) != NULL) {
lua_pushstring(L,buff);
} else
{
lua_pushnil(L);
}
/* close */
//pclose(fp);
return 1;
}
static int l_cmd_close(lua_State* L)
{
/* Read the output a line at a time - output it.
read user defined data of std
*/
FILE * fd;
intptr_t pt = (intptr_t)luaL_checknumber(L, 1);
fd = (FILE*)pt;
if(fd == NULL)
{
pclose(fd);
}
return 0;
}
/*zip file handler
* using miniz to compress and uncompress zip file
*/
static int l_unzip(lua_State* L)
{
const char* src = luaL_checkstring(L,1);
const char* dest = luaL_checkstring(L,2);
if(zip_extract(src, dest, NULL, NULL) == -1)
{
lua_pushboolean(L,0);
return 1;
}
lua_pushboolean(L,1);
return 1;
}
static int _add_to_zip(struct zip_t * zip, const char* path, const char* root)
{
int st = is_dir(path);
if(st == -1) return -1;
char p1[MAX_PATH_LEN];
char p2[MAX_PATH_LEN];
if(st)
{
// read directory
DIR *d;
struct dirent *dir;
//struct stat st;
d = opendir(path);
if (d)
{
while ((dir = readdir(d)) != NULL)
{
//ignore curent directory, parent directory
if(strcmp(dir->d_name,".") == 0 || strcmp(dir->d_name,"..")==0) continue;
// add file to zip
snprintf(p1,MAX_PATH_LEN,"%s/%s", path, dir->d_name);
snprintf(p2,MAX_PATH_LEN,"%s/%s",root,dir->d_name);
if(_add_to_zip(zip,p1,p2) == -1)
{
return -1;
}
}
closedir(d);
}
}
else
{
// if it is a file
// add it to zip
if(zip_entry_open(zip, root) == -1) return -1;
if(zip_entry_fwrite(zip, path) == -1) return -1;
zip_entry_close(zip);
}
return 0;
}
static int l_zip(lua_State* L)
{
const char* src = luaL_checkstring(L,1);
const char* dest = luaL_checkstring(L,2);
// create a zip handler
struct zip_t *zip = zip_open(dest, ZIP_DEFAULT_COMPRESSION_LEVEL, 0);
if(zip == NULL) goto pfalse;
if(_add_to_zip(zip,src,"") == -1) goto pfalse;
zip_close(zip);
lua_pushboolean(L,1);
return 1;
// return false
pfalse:
// TODO remove if filed is created
if(zip) zip_close(zip);
lua_pushboolean(L,0);
return 1;
}
static int l_getenv(lua_State* L)
{
const char* name = luaL_checkstring(L,1);
if(!name)
{
lua_pushnil(L);
return 1;
}
char * value = getenv(name);
lua_pushstring(L, value);
return 1;
}
static int l_setenv(lua_State* L)
{
const char* name = luaL_checkstring(L,1);
const char* value = luaL_checkstring(L,2);
const int force = luaL_checknumber(L,3);
if(!name)
{
lua_pushboolean(L, 0);
return 1;
}
if(!value)
{
lua_pushboolean(L, 0);
return 1;
}
int ret = setenv(name, value, force);
if(ret != 0)
{
lua_pushboolean(L, 0);
return 1;
}
lua_pushboolean(L,1);
return 1;
}
static int l_unsetenv(lua_State* L)
{
const char* name = luaL_checkstring(L,1);
if(!name)
{
lua_pushboolean(L, 0);
return 1;
}
int ret = unsetenv(name);
if(ret != 0)
{
lua_pushboolean(L, 0);
return 1;
}
lua_pushboolean(L,1);
return 1;
}
static int l_gethomedir(lua_State* L)
{
uid_t uid = (uid_t) luaL_checknumber(L,1);
if(uid < 0)
{
lua_pushnil(L);
return 1;
}
struct passwd *pw = getpwuid(uid);
if (pw == NULL) {
lua_pushnil(L);
return 1;
}
lua_pushstring(L, pw->pw_dir);
return 1;
}
static int l_trim(lua_State *L)
{
char *str = strdup((char *)luaL_checkstring(L, 1));
const char *tok = luaL_checkstring(L, 2);
trim(str, tok[0]);
lua_pushstring(L, (const char *)str);
free(str);
return 1;
}
static const struct luaL_Reg _lib [] = {
{"auth", l_check_login},
{"read_dir",l_read_dir},
{"file_stat",l_file_info},
{"uid",l_getuid},
{"setuid", l_setuid},
{"setgid", l_setgid},
{"fork", l_fork},
{"kill", l_kill},
{"waitpid", l_waitpid},
{"trim", l_trim},
{"chown",l_chown},
{"delete", l_delete},
{"exists",l_exist},
{"mkdir",l_mkdir},
{"cmdopen",l_cmd_open},
{"cmdread",l_cmd_read},
{"cmdclose",l_cmd_close},
{"move",l_file_move},
{"unzip",l_unzip},
{"zip",l_zip},
{"getenv",l_getenv},
{"setenv",l_setenv},
{"unsetenv",l_unsetenv},
{"home_dir",l_gethomedir},
{"send_file", l_send_file},
{"is_dir", l_is_dir},
{NULL,NULL}
};
int luaopen_ulib(lua_State *L)
{
luaL_newlib(L, _lib);
return 1;
}

108
silkmvc/BaseController.lua Normal file
View File

@ -0,0 +1,108 @@
-- create class
BaseObject:subclass("BaseController",
{
registry = {},
models = {},
main = false
})
-- set the name here in each subclasses
function BaseController:initialize()
for k, v in pairs(self.models) do
--- infer the class here
local modelname = firstToUpper(v).."Model"
local path = MODEL_ROOT.."."..modelname
-- require it
pcall(require, path)
--require(controller_path)
if not _G[modelname] then
self:modelnotfound(v)
else
-- create new model object
self[v] = _G[modelname]:new{registry = self.registry}
end
end
-- create template
self.template = Template:new{registry = self.registry}
end
function BaseController:print(...)
return self:actionnotfound("print")
end
function BaseController:redirect(url)
std.status(301, "Moved Permanently")
std.custom_header("Content-Type","text/html")
std.custom_header("Location", url)
std.header_flush()
end
function BaseController:switchLayout(name)
if self.main then
self.registry.layout = name
else
self:log("Cannot switch layout since the controller "..self.class.." is not the main controller")
end
end
function BaseController:setSession(key, value)
SESSION[key] = value
end
function BaseController:getSession(key)
return SESSION[key]
end
function BaseController:removeSession(key)
self:setSession(key, nil)
end
function BaseController:index(...)
self:error("#index: subclasses responsibility")
end
-- not found action
function BaseController:actionnotfound(...)
local args = {...}
self:error("#action "..args[1].." is not found in controller "..self.class)
end
-- not found model
function BaseController:modelnotfound(...)
local args = {...}
self:error("Model "..firstToUpper(args[1]).."Model is not found in controller "..self.class)
end
-- The not found controller
BaseController:subclass("NotfoundController",{ registry = {}, models = {} })
function NotfoundController:index(...)
local args = {...}
local error = args[2] or ""
if self.template:path() then
self.template:set("error", error)
self.template:set("title", "404 not found")
return true
end
self:error("404: Controller "..args[1].." not found : "..error)
return false
end
-- The asset controller for the static file
BaseController:subclass("AssetController", {registry={}, models={}})
function AssetController:index(...)
local args = {...}
return self:get(table.unpack(args))
end
function AssetController:get(...)
local path = WWW_ROOT..DIR_SEP..implode({...}, DIR_SEP)
if self.registry.fileaccess and ulib.exists(path) then
local mime = std.mimeOf(path)
if POLICY.mimes[mime] then
std.sendFile(path)
else
self:error("Access forbidden: "..path)
end
else
self:error("Asset file not found or access forbidden: "..path)
end
return false
end

51
silkmvc/BaseModel.lua Normal file
View File

@ -0,0 +1,51 @@
-- create class
BaseObject:subclass("BaseModel", {registry = {}})
function BaseModel:initialize()
self.db = self.registry.db
if self.db and self.name and self.name ~= "" and self.fields and
not self.db:available(self.name) then
self.db:createTable(self.name, self.fields)
end
end
function BaseModel:create(m)
if self.db and m then return self.db:insert(self.name, m) end
return false
end
function BaseModel:update(m)
if self.db and m then return self.db:update(self.name, m) end
return false
end
function BaseModel:delete(cond)
if self.db and cond then return self.db:delete(self.name, cond) end
return false
end
function BaseModel:find(cond)
if self.db and cond then return self.db:find(self.name, cond) end
return false
end
function BaseModel:get(id)
local data, order = self:find({exp = {["="] = {id = id}}})
if not data or #order == 0 then return false end
return data[1]
end
function BaseModel:findAll()
if self.db then return self.db:getAll(self.name) end
return false
end
function BaseModel:query(sql)
if self.db then return self.db:query(sql) end
return false
end
function BaseModel:select(sel, sql_cnd)
if self.db then return self.db:select(self.name, sel, sql_cnd) end
return nil
end

34
silkmvc/BaseObject.lua Normal file
View File

@ -0,0 +1,34 @@
BaseObject = Object:extends{registry = {}, class="BaseObject"}
function BaseObject:subclass(name, args)
_G[name] = self:extends(args)
_G[name].class = name
end
function BaseObject:log(msg, level)
level = level or "INFO"
if self.registry.logger then
self.registry.logger:log(msg,level)
end
end
function BaseObject:debug(msg)
self:log(msg, "DEBUG")
end
function BaseObject:print()
print(self.class)
end
function BaseObject:error(msg, trace)
html()
--local line = debug.getinfo(1).currentline
echo(msg)
self:log(msg,"ERROR")
if trace then
debug.traceback=nil
error(msg)
else
error(msg)
end
return false
end

144
silkmvc/DBHelper.lua Normal file
View File

@ -0,0 +1,144 @@
sqlite = modules.sqlite()
if sqlite == nil then return 0 end
-- create class
BaseObject:subclass("DBHelper", {db = {}})
function DBHelper:createTable(tbl, m)
if self:available(tbl) then return true end
local sql = "CREATE TABLE " .. tbl .. "(id INTEGER PRIMARY KEY"
for k, v in pairs(m) do
if k ~= "id" then sql = sql .. "," .. k .. " " .. v end
end
sql = sql .. ");"
return sqlite.query(self.db, sql) == 1
end
function DBHelper:insert(tbl, m)
local keys = {}
local values = {}
for k, v in pairs(m) do
if k ~= "id" then
table.insert(keys, k)
if type(v) == "number" then
table.insert(values, v)
else
local t = "\"" .. v:gsub('"', '""') .. "\""
table.insert(values, t)
end
end
end
local sql = "INSERT INTO " .. tbl .. " (" .. table.concat(keys, ',') ..
') VALUES ('
sql = sql .. table.concat(values, ',') .. ');'
return sqlite.query(self.db, sql) == 1
end
function DBHelper:get(tbl, id)
return sqlite.select(self.db, tbl, "*", "id=" .. id)[1]
end
function DBHelper:getAll(tbl)
local data = sqlite.select(self.db, tbl, "*", "1=1")
if data == nil then return nil end
local a = {}
for n in pairs(data) do table.insert(a, n) end
table.sort(a)
return data, a
end
function DBHelper:find(tbl, cond)
local cnd = "1=1"
local sel = "*"
if cond.exp then cnd = self:gencond(cond.exp) end
if cond.order then
cnd = cnd .. " ORDER BY "
local l = {}
local i = 1
for k, v in pairs(cond.order) do
l[i] = k .. " " .. v
i = i + 1
end
cnd = cnd .. table.concat(l, ",")
end
if cond.limit then cnd = cnd .. " LIMIT " .. cond.limit end
if cond.fields then
sel = table.concat(cond.fields, ",")
-- print(sel)
end
local data = sqlite.select(self.db, tbl, sel, cnd)
if data == nil then return nil end
local a = {}
for n in pairs(data) do table.insert(a, n) end
table.sort(a)
return data, a
end
function DBHelper:select(tbl, sel, cnd)
local data = sqlite.select(self.db, tbl, sel, cnd)
if data == nil then return nil end
local a = {}
for n in pairs(data) do table.insert(a, n) end
table.sort(a)
return data, a
end
function DBHelper:query(sql) return sqlite.query(self.db, sql) == 1 end
function DBHelper:update(tbl, m)
local id = m['id']
if id ~= nil then
local lst = {}
for k, v in pairs(m) do
if (type(v) == "number") then
table.insert(lst, k .. "=" .. v)
else
table.insert(lst, k .. "=\"" .. v:gsub('"', '""') .. "\"")
end
end
local sql = "UPDATE " .. tbl .. " SET " .. table.concat(lst, ",") ..
" WHERE id=" .. id .. ";"
return sqlite.query(self.db, sql) == 1
end
return false
end
function DBHelper:available(tbl) return sqlite.hasTable(self.db, tbl) == 1 end
function DBHelper:deleteByID(tbl, id)
local sql = "DELETE FROM " .. tbl .. " WHERE id=" .. id .. ";"
return sqlite.query(self.db, sql) == 1
end
function DBHelper:gencond(o)
for k, v in pairs(o) do
if k == "and" or k == "or" then
local cnd = {}
local i = 1
for k1, v1 in pairs(v) do
cnd[i] = self:gencond(v1)
i = i + 1
end
return " (" .. table.concat(cnd, " " .. k .. " ") .. ") "
else
for k1, v1 in pairs(v) do
local t = type(v1)
if (t == "string") then
return
" (" .. k1 .. " " .. k .. ' "' .. v1:gsub('"', '""') ..
'") '
end
return " (" .. k1 .. " " .. k .. " " .. v1 .. ") "
end
end
end
end
function DBHelper:delete(tbl, cond)
local sql = "DELETE FROM " .. tbl .. " WHERE " .. self:gencond(cond) .. ";"
return sqlite.query(self.db, sql) == 1
end
function DBHelper:lastInsertID() return sqlite.lastInsertID(self.db) end
function DBHelper:close() if self.db then sqlite.dbclose(self.db) end end
function DBHelper:open()
if self.db ~= nil then self.db = sqlite.getdb(self.db) end
end

30
silkmvc/Logger.lua Normal file
View File

@ -0,0 +1,30 @@
Logger = Object:extends{levels = {}}
function Logger:initialize()
end
function Logger:log(msg,level)
if self.levels[level] and ulib.exists(LOG_ROOT) then
local path = LOG_ROOT..DIR_SEP..level..'.txt'
local f = io.open(path, 'a')
local text = '['..level.."]: "..msg
if f then
f:write(text..'\n')
f:close()
end
print(text)
end
end
function Logger:info(msg)
self:log(msg, "INFO")
end
function Logger:debug(msg)
self:log(msg, "DEBUG")
end
function Logger:error(msg)
self:log(msg, "ERROR")
end

164
silkmvc/Router.lua Normal file
View File

@ -0,0 +1,164 @@
--define the class
BaseObject:subclass("Router", {registry = {}})
function Router:setPath(path)
self.path = path
end
function Router:initialize()
self.routes = {}
self.remaps = {}
end
--function Router:setArgs(args)
-- self.args = args
--end
--function Router:arg(name)
-- return self.args[name]
--end
function Router:infer(url)
-- a controller is like this /a/b/c/d/e
-- a is controller name
-- b is action
-- c,d,e is parameters
-- if user dont provide the url, try to infer it
-- from the REQUEST
url = url or REQUEST.r or ""
url = std.trim(url, "/")
local args = explode(url, "/")
local data = {
name = "index",
action = "index",
args = {}
}
if args and #args > 0 and args[1] ~= "" then
data.name = args[1]:gsub("%.", "")
if args[2] then
data.action = args[2]:gsub("%.", "")
end
for i = 3, #args do
table.insert(data.args, args[i])
end
end
-- remap if needed
if self.remaps[data.name] ~= nil then
data.name = self.remaps[data.name]
end
-- find the controller class and init it
local controller_name = firstToUpper(data.name) .. "Controller"
local controller_path = self.path .. "." .. controller_name
-- require the controller module
-- ignore the error
local r, e = pcall(require, controller_path)
--require(controller_path)
if not _G[controller_name] then
-- verify if it is an asset
url = url:gsub("/", DIR_SEP)
local filepath = WWW_ROOT..DIR_SEP..url
if ulib.exists(filepath) then -- and not std.is_dir(filepath)
data.controller = AssetController:new {registry = self.registry}
data.action = "get"
data.name = "asset"
data.args ={url}
else
-- let the notfound controller handle the error
data.controller = NotfoundController:new {registry = self.registry}
data.args = {controller_name, e}
data.action = "index"
data.name = "notfound"
end
else
-- create the coresponding controller
data.controller = _G[controller_name]:new {registry = self.registry}
if not data.controller[data.action] then
--data.args = {data.action}
table.insert(data.args, 1, data.action)
data.action = "actionnotfound"
end
end
self:log("Controller: " .. data.controller.class .. ", action: "..data.action..", args: ".. JSON.encode(data.args))
return data
end
function Router:delegate()
local views = {}
local data = self:infer()
-- set the controller to the main controller
data.controller.main = true
views.__main__ = self:call(data)
if not views.__main__ then
--self:error("No view available for this action")
return
end
-- get all visible routes
local routes = self:dependencies(data.name .. "/" .. data.action)
for k, v in pairs(routes) do
data = self:infer(v)
views[k] = self:call(data)
end
-- now require the main page to put the view
local view_args = {}
local view_argv = {}
for k,v in pairs(views) do
table.insert( view_args, k )
table.insert( view_argv, v )
end
local fn, e = loadscript(VIEW_ROOT .. DIR_SEP .. self.registry.layout .. DIR_SEP .. "layout.ls", view_args)
html()
if fn then
local r, o = pcall(fn, table.unpack(view_argv))
if not r then
self:error(o)
end
else
e = e or ""
self:error("The index page is not found for layout: " .. self.registry.layout..": "..e)
end
end
function Router:dependencies(url)
if not self.routes[self.registry.layout] then
return {}
end
local list = {}
--self:log("comparing "..url)
for k, v in pairs(self.routes[self.registry.layout]) do
v.url = std.trim(v.url, "/")
if v.visibility == "ALL" then
list[k] = v.url
elseif v.visibility.routes then
if v.visibility.shown == true or v.visibility.shown == nil then
if v.visibility.routes[url] then
list[k] = v.url
end
else
if not v.visibility.routes[url] then
list[k] = v.url
end
end
end
end
return list
end
function Router:call(data)
data.controller.template:setView(data.action, data.name)
local obj = data.controller[data.action](data.controller, table.unpack(data.args))
if obj then
return data.controller.template
else
return false
end
end
function Router:remap(from, to)
self.remaps[from] = to
end
function Router:route(layout, dependencies)
self.routes[layout] = dependencies
end

58
silkmvc/Template.lua Normal file
View File

@ -0,0 +1,58 @@
-- create class
BaseObject:subclass("Template",{registry = {}})
function Template:initialize()
self.vars = {}
end
function Template:set(k, v, ow)
if not self.vars[k] or (self.vars[k] and ow) then
self.vars[k] = v
end
end
function Template:get(k)
return self.vars[k]
end
function Template:remove(k)
self.vars[k] = nil
end
-- infer view path
function Template:setView(name, controller)
self.name = name
if controller then
self.controller = controller
end
end
function Template:path()
local path = VIEW_ROOT..DIR_SEP..self.registry.layout..DIR_SEP..self.controller..DIR_SEP..self.name..".ls"
if ulib.exists(path) then
return path
else
return false, path
end
end
-- render the page
function Template:render()
local path, err = self:path()
if not path then
return self:error("View not found: "..err)
end
local args = {}
local argv = {}
for k, v in pairs(self.vars) do
table.insert( args, k )
table.insert( argv,v )
end
local fn, e = loadscript(self:path(), args)
if fn then
local r,o = pcall(fn, table.unpack(argv))
if not r then
self:error(o)
end
else
self:error(e)
end
end

57
silkmvc/api.lua Normal file
View File

@ -0,0 +1,57 @@
require("OOP")
ulib = require("ulib")
require(BASE_FRW.."silk.BaseObject")
require(BASE_FRW.."silk.DBHelper")
require(BASE_FRW.."silk.Router")
require(BASE_FRW.."silk.BaseController")
require(BASE_FRW.."silk.BaseModel")
require(BASE_FRW.."silk.Logger")
require(BASE_FRW.."silk.Template")
-- mime type allows
-- this will bypass the default server security
-- the default list is from the server setting
POLICY = {}
POLICY.mimes = {
["application/javascript"] = true,
["image/bmp"] = true,
["image/jpeg"] = true,
["image/png"] = true,
["text/css"] = true,
["text/markdown"] = true,
["text/csv"] = true,
["application/pdf"] = true,
["image/gif"] = true,
["text/html"] = true,
["application/json"] = true,
["application/javascript"] = true,
["image/x-portable-pixmap"] = true,
["application/x-rar-compressed"] = true,
["image/tiff"] = true,
["application/x-tar"] = true,
["text/plain"] = true,
["application/x-font-ttf"] = true,
["application/xhtml+xml"] = true,
["application/xml"] = true,
["application/zip"] = true,
["image/svg+xml"] = true,
["application/vnd.ms-fontobject"] = true,
["application/x-font-woff"] = true,
["application/x-font-otf"] = true,
["audio/mpeg"] = true,
}
HEADER_FLAG = false
function html()
if not HEADER_FLAG then
std.chtml(SESSION)
HEADER_FLAG = true
end
end
function import(module)
return require(BASE_FRW.."silk.api."..module)
end

40
silkmvc/core/OOP.lua Normal file
View File

@ -0,0 +1,40 @@
Object = {}
function Object:prototype(o)
o = o or {} -- create table if user does not provide one
setmetatable(o, self)
self.__index = self
return o
end
function Object:new(o)
local obj = self:prototype(o)
obj:initialize()
return obj
end
function Object:print()
print('an Object')
end
function Object:initialize()
end
function Object:asJSON()
return '{}'
end
function Object:inherit(o)
return self:prototype(o)
end
function Object:extends(o)
return self:inherit(o)
end
Test = Object:inherit{dummy = 0}
function Test:toWeb()
wio.t(self.dummy)
end

260
silkmvc/core/api.lua Normal file
View File

@ -0,0 +1,260 @@
math.randomseed(os.clock())
package.cpath = __api__.apiroot..'/?.so'
require("antd")
std = modules.std()
local read_header =function()
local l
repeat
l = std.antd_recv(HTTP_REQUEST.id)
if l and l ~= '\r' then
if l == "HTTP_REQUEST" or l == "request" or l == "COOKIE" or l == "REQUEST_HEADER" or l == "REQUEST_DATA" then
coroutine.yield(l, "LUA_TABLE")
else
local l1 = std.antd_recv(HTTP_REQUEST.id)
if l1 ~= '\r' then
coroutine.yield(l, l1)
end
l = l1
end
end
until not l or l == '\r'
end
local read_headers = function()
local co = coroutine.create(function () read_header() end)
return function () -- iterator
local code, k, v = coroutine.resume(co)
return k,v
end
end
local parse_headers =function()
local lut = {
HTTP_REQUEST = HTTP_REQUEST
}
local curr_tbl = "HTTP_REQUEST"
for k,v in read_headers() do
if v == "LUA_TABLE" then
if not lut[k] then
lut[k] = {}
end
curr_tbl = k
else
lut[curr_tbl][k] = v
end
end
HTTP_REQUEST.request = lut.request
HTTP_REQUEST.request.COOKIE = lut.COOKIE
HTTP_REQUEST.request.REQUEST_HEADER = lut.REQUEST_HEADER
HTTP_REQUEST.request.REQUEST_DATA = lut.REQUEST_DATA
end
-- parsing the header
parse_headers()
-- root dir
__ROOT__ = HTTP_REQUEST.request.SERVER_WWW_ROOT
-- set require path
package.path = __ROOT__ .. '/?.lua;'..__api__.apiroot..'/?.lua'
require("std")
require("utils")
require("extra_mime")
ulib = require("ulib")
-- set session
SESSION = {}
REQUEST = HTTP_REQUEST.request.REQUEST_DATA
REQUEST.method = HTTP_REQUEST.request.METHOD
if HTTP_REQUEST.request.COOKIE then
SESSION = HTTP_REQUEST.request.COOKIE
end
HEADER = HTTP_REQUEST.request.REQUEST_HEADER
HEADER.mobile = false
if HEADER["User-Agent"] and HEADER["User-Agent"]:match("Mobi") then
HEADER.mobile = true
end
function LOG_INFO(fmt,...)
ulib.syslog(5,string.format(fmt or "LOG",...))
end
function LOG_ERROR(fmt,...)
ulib.syslog(3,string.format(fmt or "ERROR",...))
end
function has_module(m)
if utils.file_exists(__ROOT__..'/'..m) then
if m:find("%.ls$") then
return true, true, __ROOT__..'/'..m
else
return true, false, m:gsub(".lua$","")
end
elseif utils.file_exists(__ROOT__..'/'..string.gsub(m,'%.','/')..'.lua') then
return true, false, m
elseif utils.file_exists(__ROOT__..'/'..string.gsub(m,'%.','/')..'.ls') then
return true, true, __ROOT__..'/'..string.gsub(m,'%.','/')..'.ls'
end
return false, false, nil
end
function echo(m)
if m then std.t(m) else std.t("Undefined value") end
end
function loadscript(file, args)
local f = io.open(file, "rb")
local content = ""
if f then
local html = ""
local pro = "local fn = function(...)"
local s,e, mt
local mtbegin = true -- find begin of scrit, 0 end of scrit
local i = 1
if args then
pro = "local fn = function("..table.concat( args, ",")..")"
end
for line in io.lines(file) do
line = std.trim(line, " ")
if(line ~= "") then
if(mtbegin) then
mt = "^%s*<%?lua"
else
mt = "%?>%s*$"
end
s,e = line:find(mt)
if(s) then
if mtbegin then
if html ~= "" then
pro= pro.."echo(\""..utils.escape(html).."\")\n"
html = ""
end
local b,f = line:find("%?>%s*$")
if b then
pro = pro..line:sub(e+1,b-1).."\n"
else
pro = pro..line:sub(e+1).."\n"
mtbegin = not mtbegin
end
else
pro = pro..line:sub(0,s-1).."\n"
mtbegin = not mtbegin
end
else -- no match
if mtbegin then
-- detect if we have inline lua with format <?=..?>
local b,f = line:find("<%?=")
if b then
local tmp = line
pro= pro.."echo("
while(b) do
-- find the close
local x,y = tmp:find("%?>")
if x then
pro = pro.."\""..utils.escape(html..tmp:sub(0,b-1):gsub("%%","%%%%")).."\".."
pro = pro..tmp:sub(f+1,x-1)..".."
html = ""
tmp = tmp:sub(y+1)
b,f = tmp:find("<%?=")
else
error("Syntax error near line "..i)
end
end
pro = pro.."\""..utils.escape(tmp:gsub("%%","%%%%")).."\")\n"
else
html = html..std.trim(line," "):gsub("%%","%%%%").."\n"
end
else
if line ~= "" then pro = pro..line.."\n" end
end
end
end
i = i+ 1
end
f:close()
if(html ~= "") then
pro = pro.."echo(\""..utils.escape(html).."\")\n"
end
pro = pro.."\nend \n return fn"
local r,e = load(pro)
if r then return r(), e else return nil,e end
end
end
-- decode post data if any
local decode_request_data = function()
if (not REQUEST.method)
or (REQUEST.method ~= "POST"
and REQUEST.method ~= "PUT"
and REQUEST.method ~= "PATCH")
or (not REQUEST.HAS_RAW_BODY) then
return 0
end
local ctype = HEADER['Content-Type']
local clen = HEADER['Content-Length'] or -1
if clen then
clen = tonumber(clen)
end
if not ctype or clen == -1 then
LOG_ERROR("Invalid content type %s or content length %d", ctype, clen)
return 400, "Bad Request, missing content description"
end
local raw_data, len = std.antd_recv(HTTP_REQUEST.id, clen)
if len ~= clen then
LOG_ERROR("Unable to read all data: read %d expected %d", len, clen)
return 400, "Bad Request, missing content data"
end
if ctype:find("application/json") then
REQUEST.json = bytes.__tostring(raw_data)
else
REQUEST[ctype] = raw_data
end
REQUEST.HAS_RAW_BODY = nil
return 0
end
-- set compression level
local accept_encoding = HEADER["Accept-Encoding"]
if accept_encoding then
if accept_encoding:find("gzip") then
std.antd_set_zlevel(HTTP_REQUEST.id, "gzip")
elseif accept_encoding:find("deflate") then
std.antd_set_zlevel(HTTP_REQUEST.id, "deflate")
end
end
local code, error = decode_request_data()
if code ~= 0 then
LOG_ERROR(error)
std.error(code, error)
return
end
-- LOG_INFO(JSON.encode(REQUEST))
-- OOP support
--require("OOP")
-- load sqlite helper
--require("sqlite")
-- enable extra mime
-- run the file
local m, s, p = has_module(HTTP_REQUEST.request.RESOURCE_PATH)
if m then
-- run the correct module
if s then
local r,e = loadscript(p)
if r then r() else unknow(e) end
else
LOG_INFO("RUNNING MODULE %s", p)
require(p)
end
else
unknow("Resource not found for request "..HTTP_REQUEST.request.RESOURCE_PATH)
end
--require('router')

63
silkmvc/core/cif.lua Normal file
View File

@ -0,0 +1,63 @@
FFI = require("ffi")
FFI.type = {}
FFI.type.VOID = 0
FFI.type.UINT8 = 1
FFI.type.SINT8 = 2
FFI.type.UINT16 = 3
FFI.type.SINT16 = 4
FFI.type.UINT32 = 5
FFI.type.SINT32 = 6
FFI.type.UINT64 = 7
FFI.type.SINT64 = 8
FFI.type.FLOAT = 9
FFI.type.DOUBLE = 10
FFI.type.UCHAR = 11
FFI.type.SCHAR = 12
FFI.type.USHORT = 13
FFI.type.SSHORT = 14
FFI.type.UINT = 15
FFI.type.SINT = 16
FFI.type.ULONG = 17
FFI.type.SLONG = 18
FFI.type.LONGDOUBLE = 19
FFI.type.POINTER = 20
FFI.cache = {}
FFI.load = function(path)
if FFI.cache[path] then
return FFI.cache[path]
else
print("Loading: "..path)
local lib = FFI.dlopen(path)
if lib then
FFI.cache[path] = {ref = lib, fn= {}}
end
return FFI.cache[path]
end
end
FFI.unload = function(path)
local lib = FFI.cache[path]
if lib then
FFI.dlclose(lib.ref)
FFI.cache[path] = false
end
end
FFI.unloadAll = function()
for k,v in pairs(FFI.cache) do
FFI.dlclose(v.ref)
end
FFI.cache = {}
end
FFI.lookup = function(lib, name)
local fn = lib.fn[name]
if fn then return fn end
fn = FFI.dlsym(lib.ref, name)
if fn then
lib.fn[name] = fn
return fn
end
return nil
end

View File

@ -0,0 +1,79 @@
function std.extra_mime(name)
local ext = std.ext(name)
local mpath = __ROOT__.."/".."mimes.json"
local xmimes = {}
if utils.file_exists(mpath) then
local f = io.open(mpath, "r")
if f then
xmimes = JSON.decodeString(f:read("*all"))
f:close()
end
end
if(name:find("Makefile$")) then return "text/makefile",false
elseif ext == "php" then return "text/php",false
elseif ext == "c" or ext == "h" then return "text/c",false
elseif ext == "cpp" or ext == "hpp" then return "text/cpp",false
elseif ext == "md" then return "text/markdown",false
elseif ext == "lua" then return "text/lua",false
elseif ext == "yml" then return "application/x-yaml", false
elseif xmimes[ext] then return xmimes[ext].mime, xmimes[ext].binary
--elseif ext == "pgm" then return "image/x-portable-graymap", true
else
return "application/octet-stream",true
end
end
function std.mimeOf(name)
local mime = std.mime(name)
if mime ~= "application/octet-stream" then
return mime
else
return std.extra_mime(name)
end
end
--[[ function std.isBinary(name)
local mime = std.mime(name)
if mime ~= "application/octet-stream" then
return std.is_bin(name)
else
local xmime,bin = std.extra_mime(name)
return bin
end
end ]]
function std.sendFile(m)
local mime = std.mimeOf(m)
local finfo = ulib.file_stat(m)
local len = tostring(math.floor(finfo.size))
local len1 = tostring(math.floor(finfo.size - 1))
if mime == "audio/mpeg" then
std.status(200)
std.header("Pragma", "public")
std.header("Expires", "0")
std.header("Content-Type", mime)
std.header("Content-Length", len)
std.header("Content-Disposition", "inline; filename=" .. std.basename(m))
std.header("Content-Range:", "bytes 0-" .. len1 .. "/" .. len)
std.header("Accept-Ranges", "bytes")
std.header("X-Pad", "avoid browser bug")
std.header("Content-Transfer-Encoding", "binary")
std.header("Cache-Control", "no-cache, no-store")
std.header("Connection", "Keep-Alive")
std.header_flush()
std.f(m)
else
if HEADER['If-Modified-Since'] and HEADER['If-Modified-Since'] == finfo.ctime then
std.status(304)
std.header_flush()
else
std.status(200)
std.header("Content-Type", mime)
--std.header("Content-Length", len)
std.header("Cache-Control", "no-cache")
std.header("Last-Modified", finfo.ctime)
std.header_flush()
std.f(m)
end
end
end

157
silkmvc/core/sqlite.lua Normal file
View File

@ -0,0 +1,157 @@
sqlite = modules.sqlite()
if sqlite == nil then return 0 end
require("OOP")
-- create class
DBModel = Object:inherit{db=nil, name=''}
function DBModel:createTable(m)
if self:available() then return true end
local sql = "CREATE TABLE "..self.name.."(id INTEGER PRIMARY KEY"
for k, v in pairs(m) do
if k ~= "id" then
sql = sql..","..k.." "..v
end
end
sql = sql..");"
return sqlite.query(self.db,sql) == 1
end
function DBModel:insert(m)
local keys = {}
local values = {}
for k,v in pairs(m) do
if k ~= "id" then
table.insert(keys,k)
if type(v) == "number" then
table.insert(values, v)
elseif type(v) == "boolean" then
table.insert( values, v and 1 or 0 )
else
local t = "\""..v:gsub('"', '""').."\""
table.insert(values,t)
end
end
end
local sql = "INSERT INTO "..self.name.." ("..table.concat(keys,',')..') VALUES ('
sql = sql..table.concat(values,',')..');'
return sqlite.query(self.db, sql) == 1
end
function DBModel:get(id)
return sqlite.select(self.db, self.name, "*","id="..id)[1]
end
function DBModel:getAll()
--local sql = "SELECT * FROM "..self.name
--return sqlite.select(self.db, self.name, "1=1")
local data = sqlite.select(self.db, self.name, "*", "1=1")
if data == nil then return nil end
local a = {}
for n in pairs(data) do table.insert(a, n) end
table.sort(a)
return data, a
end
function DBModel:find(cond)
local cnd = "1=1"
local sel = "*"
if cond.exp then
cnd = self:gencond(cond.exp)
end
if cond.order then
cnd = cnd.." ORDER BY "
local l = {}
local i = 1
for k,v in pairs(cond.order) do
l[i] = k.." "..v
i = i+1
end
cnd = cnd..table.concat(l, ",")
end
if cond.limit then
cnd = cnd.." LIMIT "..cond.limit
end
if cond.fields then
sel = table.concat(cond.fields, ",")
--print(sel)
end
--print(cnd)
local data = sqlite.select(self.db, self.name, sel, cnd)
if data == nil then return nil end
local a = {}
for n in pairs(data) do table.insert(a, n) end
table.sort(a)
return data, a
end
function DBModel:query(sql)
return sqlite.query(self.db, sql) == 1
end
function DBModel:update(m)
local id = m['id']
if id ~= nil then
local lst = {}
for k,v in pairs(m) do
if(type(v)== "number") then
table.insert(lst,k.."="..v)
elseif type(v) == "boolean" then
table.insert( lst, k.."="..(v and 1 or 0) )
else
table.insert(lst,k.."=\""..v:gsub('"', '""').."\"")
end
end
local sql = "UPDATE "..self.name.." SET "..table.concat(lst,",").." WHERE id="..id..";"
return sqlite.query(self.db, sql) == 1
end
return false
end
function DBModel:available()
return sqlite.hasTable(self.db, self.name) == 1
end
function DBModel:deleteByID(id)
local sql = "DELETE FROM "..self.name.." WHERE id="..id..";"
return sqlite.query(self.db, sql) == 1
end
function DBModel:gencond(o)
for k,v in pairs(o) do
if k == "and" or k == "or" then
local cnd = {}
local i = 1
for k1,v1 in pairs(v) do
cnd[i] = self:gencond(v1)
i = i + 1
end
return " ("..table.concat(cnd, " "..k.." ")..") "
else
for k1,v1 in pairs(v) do
local t = type(v1)
if(t == "string") then
return " ("..k1.." "..k..' "'..v1:gsub('"','""')..'") '
end
return " ("..k1.." "..k.." "..v1..") "
end
end
end
end
function DBModel:delete(cond)
local sql = "DELETE FROM "..self.name.." WHERE "..self:gencond(cond)..";"
return sqlite.query(self.db, sql) == 1
end
function DBModel:lastInsertID()
return sqlite.lastInsertID(self.db)
end
function DBModel:close()
if self.db then
sqlite.dbclose(self.db)
end
end
function DBModel:open()
if self.db ~= nil then
self.db = sqlite.getdb(self.db)
end
end

171
silkmvc/core/std.lua Normal file
View File

@ -0,0 +1,171 @@
bytes = modules.bytes()
array = modules.array()
modules.sqlite = function()
if not sqlite then
sqlite = require("sqlitedb")
sqlite.getdb = function(name)
if name:find("%.db$") then
return sqlite._getdb(name)
elseif name:find("/") then
LOG_ERROR("Invalid database name %s", name)
return nil
else
return sqlite._getdb(__api__.dbpath.."/"..name..".db")
end
end
end
return sqlite
end
RESPONSE_HEADER = {
status = 200,
header = {},
cookie = {},
sent = false
}
function std.status(code)
RESPONSE_HEADER.status=code
end
function std.custom_header(k,v)
std.header(k,v)
end
function std.header_flush()
std._send_header(HTTP_REQUEST.id,RESPONSE_HEADER.status, RESPONSE_HEADER.header, RESPONSE_HEADER.cookie)
RESPONSE_HEADER.sent = true
end
function std.header(k,v)
RESPONSE_HEADER.header[k] = v
end
function std.cjson(ck)
for k,v in pairs(ck) do
std.setCookie(k.."="..v.."; Path=/")
end
std.header("Content-Type","application/json; charset=utf-8")
std.header_flush()
end
function std.chtml(ck)
for k,v in pairs(ck) do
std.setCookie(k.."="..v.."; Path=/")
end
std.header("Content-Type","text/html; charset=utf-8")
std.header_flush()
end
function std.t(s)
if RESPONSE_HEADER.sent == false then
std.header_flush()
end
std._t(HTTP_REQUEST.id,s)
end
function std.b(s)
if RESPONSE_HEADER.sent == false then
std.header_flush()
end
std._b(HTTP_REQUEST.id,s)
end
function std.f(v)
std._f(HTTP_REQUEST.id,v)
--ulib.send_file(v, HTTP_REQUEST.socket)
end
function std.setCookie(v)
RESPONSE_HEADER.cookie[#RESPONSE_HEADER.cookie] = v
end
function std.error(status, msg)
std._error(HTTP_REQUEST.id, status, msg)
end
--_upload
--_route
function std.unknow(s)
std.error(404, "Unknown request")
end
--_redirect
--[[ function std.redirect(s)
std._redirect(HTTP_REQUEST.id,s)
end ]]
function std.html()
std.header("Content-Type","text/html; charset=utf-8")
std.header_flush()
end
function std.text()
std.header("Content-Type","text/plain; charset=utf-8")
std.header_flush()
end
function std.json()
std.header("Content-Type","application/json; charset=utf-8")
std.header_flush()
end
function std.jpeg()
std.header("Content-Type","image/jpeg")
std.header_flush()
end
function std.octstream(s)
std.header("Content-Type","application/octet-stream")
std.header("Content-Disposition",'attachment; filename="'..s..'"')
std.header_flush()
end
--[[ function std.textstream()
std._textstream(HTTP_REQUEST.id)
end ]]
function std.readOnly(t) -- bugging
local proxy = {}
local mt = { -- create metatable
__index = t,
__newindex = function (t,k,v)
error("attempt to update a read-only table", 2)
end
}
setmetatable(proxy, mt)
return proxy
end
-- web socket
std.ws = {}
function std.ws.header()
local h = std.ws_header(HTTP_REQUEST.id)
if(h) then
return h --std.readOnly(h)
else
return nil
end
end
function std.ws.read(h)
return std.ws_read(HTTP_REQUEST.id,h)
end
function std.ws.swrite(s)
std.ws_t(HTTP_REQUEST.id,s)
end
function std.ws.fwrite(s)
std.ws_f(HTTP_REQUEST.id,s)
end
function std.ws.write_bytes(arr)
std.ws_b(HTTP_REQUEST.id,arr)
end
function std.ws.enable()
return HTTP_REQUEST ~= nil and HTTP_REQUEST.request["__web_socket__"] == "1"
end
function std.ws.close(code)
std.ws_close(HTTP_REQUEST.id,code)
end
function std.basename(str)
local name = string.gsub(std.trim(str,"/"), "(.*/)(.*)", "%2")
return name
end
function std.is_file(f)
return std.is_dir(f) == false
end
std.ws.TEXT = 1
std.ws.BIN = 2
std.ws.CLOSE = 8

161
silkmvc/core/utils.lua Normal file
View File

@ -0,0 +1,161 @@
utils = {}
function utils.is_array(table)
local max = 0
local count = 0
for k, v in pairs(table) do
if type(k) == "number" then
if k > max then max = k end
count = count + 1
else
return false
end
end
if max > count * 2 then
return false
end
return true
end
function utils.escape(s)
local replacements = {
["\\"] = "\\\\" ,
['"'] = '\\"',
["\n"] = "\\n",
["\t"] = "\\t",
["\b"] = "\\b",
["\f"] = "\\f",
["\r"] = "\\r",
["%"] = "%%"
}
return (s:gsub( "[\\'\"\n\t\b\f\r%%]", replacements ))
end
function utils.escape_pattern(s)
return s:gsub("[%(%)%.%+%-%*%?%[%]%^%$%%]", "%%%1")
end
function utils.unescape_pattern(s)
return s:gsub( "[%%]", "%%%%")
end
function utils.hex_to_char(x)
return string.char(tonumber(x, 16))
end
function utils.decodeURI(url)
return url:gsub("%%(%x%x)", utils.hex_to_char)
end
function utils.unescape(s)
local str = ""
local escape = false
local esc_map = {b = '\b', f = '\f', n = '\n', r = '\r', t = '\t'}
for c in s:gmatch"." do
if c ~= '\\' then
if escape then
if esc_map[c] then
str = str..esc_map[c]
else
str = str..c
end
else
str = str..c
end
escape = false
else
if escape then
str = str..c
escape = false
else
escape = true
end
end
end
return str
end
function utils.file_exists(name)
local f=io.open(name,"r")
if f~=nil then io.close(f) return true else return false end
end
function utils.url_parser(uri)
local pattern = "^(https?)://([%.%w]+):?(%d*)(/?[^#]*)#?.*$"
local obj = {}
obj.protocol = uri:gsub(pattern, "%1")
obj.hostname = uri:gsub(pattern, "%2")
obj.port = uri:gsub(pattern, "%3")
obj.query = uri:gsub(pattern, "%4")
if obj.port == "" then obj.port = 80 else obj.port = tonumber(obj.port) end
if obj.query == "" then obj.query="/" end
return obj
end
JSON = require("json")
function JSON.encode(obj)
local t = type(obj)
if t == 'table' then
-- encode object
if utils.is_array(obj) == false then
local lst = {}
for k,v in pairs(obj) do
table.insert(lst,'"'..k..'":'..JSON.encode(v))
end
return "{"..table.concat(lst,",").."}"
else
local lst = {}
local a = {}
for n in pairs(obj) do table.insert(a, n) end
table.sort(a)
for i,v in pairs(a) do
table.insert(lst,JSON.encode(obj[v]))
end
return "["..table.concat(lst,",").."]"
end
elseif t == 'string' then
--print('"'..utils.escape(obj)..'"')
return '"'..utils.escape(obj)..'"'
elseif t == 'boolean' or t == 'number' then
return tostring(obj)
elseif obj == nil then
return "null"
else
return '"'..tostring(obj)..'"'
end
end
function explode(str, div) -- credit: http://richard.warburton.it
if (div=='') then return false end
local pos,arr = 0,{}
-- for each divider found
for st,sp in function() return string.find(str,div,pos,true) end do
table.insert(arr,string.sub(str,pos,st-1)) -- Attach chars left of current divider
pos = sp + 1 -- Jump past current divider
end
table.insert(arr,string.sub(str,pos)) -- Attach chars right of last divider
return arr
end
function implode(arr, div)
return table.concat(arr,div)
end
function firstToUpper(str)
return (str:gsub("^%l", string.upper))
end
local charset = "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890"
function utils.generate_salt(length)
local ret = {}
local r
for i = 1, length do
r = math.random(1, #charset)
table.insert(ret, charset:sub(r, r))
end
return table.concat(ret)
end

54
silkmvc/router.lua.tpl Normal file
View File

@ -0,0 +1,54 @@
-- the rewrite rule for the framework
-- should be something like this
-- ^\/apps\/+(.*)$ = /apps/router.lua?r=<1>&<query>
-- some global variables
DIR_SEP = "/"
WWW_ROOT = "/opt/www/htdocs/apps"
HTTP_ROOT = "https://apps.localhost:9195/"
-- class path: path.to.class
BASE_FRW = ""
-- class path: path.to.class
CONTROLLER_ROOT = BASE_FRW.."apps.controllers"
MODEL_ROOT = BASE_FRW.."apps.models"
-- file path: path/to/file
VIEW_ROOT = WWW_ROOT..DIR_SEP.."views"
LOG_ROOT = WWW_ROOT..DIR_SEP.."logs"
-- require needed library
require(BASE_FRW.."silk.api")
-- registry object store global variables
local REGISTRY = {}
-- set logging level
REGISTRY.logger = Logger:new{ levels = {INFO = true, ERROR = true, DEBUG = true}}
REGISTRY.db = DBHelper:new{db="iosapps"}
REGISTRY.layout = 'default'
REGISTRY.db:open()
local router = Router:new{registry = REGISTRY}
REGISTRY.router = router
router:setPath(CONTROLLER_ROOT)
--router:route('edit', 'post/edit', "ALL" )
-- example of depedencies to the current main route
-- each layout may have different dependencies
local default_routes_dependencies = {
edit = {
url = "post/edit",
visibility = {
shown = true,
routes = {
["post/index"] = true
}
}
},
--category = {
-- url = "cat/index",
-- visibility = "ALL"
--}
}
router:route('default', default_routes_dependencies )
router:delegate()
if REGISTRY.db then REGISTRY.db:close() end