mirror of
https://github.com/lxsang/silk.git
synced 2024-12-26 04:48:21 +01:00
Add C based module + lua based API code (WIP)
This commit is contained in:
parent
8417bccefb
commit
d4bb12c6b5
109
.gitignore
vendored
109
.gitignore
vendored
@ -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
40
Makefile.am
Normal 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
87
configure.ac
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
# initialise autoconf and set up some basic information about the program we’re packaging
|
||||||
|
AC_INIT([silk], [0.1.0], [xsang.le@gmail.com])
|
||||||
|
|
||||||
|
# We’re 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
20
modules/3rd/jsmn/LICENSE
Executable 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
167
modules/3rd/jsmn/README.md
Executable 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
|
126
modules/3rd/jsmn/example/jsondump.c
Executable file
126
modules/3rd/jsmn/example/jsondump.c
Executable 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;
|
||||||
|
}
|
76
modules/3rd/jsmn/example/simple.c
Executable file
76
modules/3rd/jsmn/example/simple.c
Executable 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
314
modules/3rd/jsmn/jsmn.c
Executable 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
76
modules/3rd/jsmn/jsmn.h
Executable 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
16
modules/3rd/jsmn/library.json
Executable 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
27
modules/3rd/jsmn/test/test.h
Executable 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
398
modules/3rd/jsmn/test/tests.c
Executable 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);
|
||||||
|
}
|
94
modules/3rd/jsmn/test/testutil.h
Executable file
94
modules/3rd/jsmn/test/testutil.h
Executable 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
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
42
modules/3rd/md4c/entity.h
Normal 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 */
|
750
modules/3rd/md4c/md4c-html.c
Normal file
750
modules/3rd/md4c/md4c-html.c
Normal 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, "&");
|
||||||
|
break;
|
||||||
|
case '<':
|
||||||
|
RENDER_VERBATIM(r, "<");
|
||||||
|
break;
|
||||||
|
case '>':
|
||||||
|
RENDER_VERBATIM(r, ">");
|
||||||
|
break;
|
||||||
|
case '"':
|
||||||
|
RENDER_VERBATIM(r, """);
|
||||||
|
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, "&");
|
||||||
|
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. "�")). */
|
||||||
|
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. " "). */
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
70
modules/3rd/md4c/md4c-html.h
Normal file
70
modules/3rd/md4c/md4c-html.h
Normal 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
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
397
modules/3rd/md4c/md4c.h
Normal 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.
|
||||||
|
* (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. Ӓ
|
||||||
|
* (c) Hexadecimal entity, e.g. ካ
|
||||||
|
*
|
||||||
|
* 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 " bar')
|
||||||
|
*
|
||||||
|
* The image alt text is propagated as a normal text via the MD_PARSER::text()
|
||||||
|
* callback. However, the image title ('foo " 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]: """ (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
50
modules/3rd/zip/main.c
Normal 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
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
471
modules/3rd/zip/zip.c
Executable 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
90
modules/3rd/zip/zip.h
Executable 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
49
modules/Makefile.am
Normal 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
185
modules/base64.c
Normal 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
77
modules/base64.h
Normal 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
231
modules/enc.c
Normal 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
264
modules/json.c
Normal 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
301
modules/lua/lua54/lauxlib.h
Normal 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
518
modules/lua/lua54/lua.h
Normal 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
786
modules/lua/lua54/luaconf.h
Normal 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
|
||||||
|
|
52
modules/lua/lua54/lualib.h
Normal file
52
modules/lua/lua54/lualib.h
Normal 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
38
modules/lua/lualib.h
Normal 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
63
modules/md.c
Normal 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
265
modules/sha1.c
Normal 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
23
modules/sha1.h
Normal 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
141
modules/slice.c
Normal 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
125
modules/sqlitedb.c
Normal 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
704
modules/stmr.c
Normal 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
866
modules/ulib.c
Normal 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
108
silkmvc/BaseController.lua
Normal 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
51
silkmvc/BaseModel.lua
Normal 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
34
silkmvc/BaseObject.lua
Normal 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
144
silkmvc/DBHelper.lua
Normal 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
30
silkmvc/Logger.lua
Normal 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
164
silkmvc/Router.lua
Normal 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
58
silkmvc/Template.lua
Normal 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
57
silkmvc/api.lua
Normal 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
40
silkmvc/core/OOP.lua
Normal 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
260
silkmvc/core/api.lua
Normal 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
63
silkmvc/core/cif.lua
Normal 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
|
79
silkmvc/core/extra_mime.lua
Normal file
79
silkmvc/core/extra_mime.lua
Normal 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
157
silkmvc/core/sqlite.lua
Normal 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
171
silkmvc/core/std.lua
Normal 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
161
silkmvc/core/utils.lua
Normal 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
54
silkmvc/router.lua.tpl
Normal 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
|
||||||
|
|
Loading…
Reference in New Issue
Block a user