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

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

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

@ -0,0 +1,20 @@
Copyright (c) 2010 Serge A. Zaitsev
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

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

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

View File

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

View File

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

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

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

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

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

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

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

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

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

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

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

View File

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

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

File diff suppressed because it is too large Load Diff

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

@ -0,0 +1,42 @@
/*
* MD4C: Markdown parser for C
* (http://github.com/mity/md4c)
*
* Copyright (c) 2016-2019 Martin Mitas
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#ifndef MD4C_ENTITY_H
#define MD4C_ENTITY_H
#include <stdlib.h>
/* Most entities are formed by single Unicode codepoint, few by two codepoints.
* Single-codepoint entities have codepoints[1] set to zero. */
struct entity {
const char* name;
unsigned codepoints[2];
};
const struct entity* entity_lookup(const char* name, size_t name_size);
#endif /* MD4C_ENTITY_H */

View File

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

View File

@ -0,0 +1,70 @@
/*
* MD4C: Markdown parser for C
* (http://github.com/mity/md4c)
*
* Copyright (c) 2016-2017 Martin Mitas
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#ifndef MD4C_HTML_H
#define MD4C_HTML_H
#include "md4c.h"
#ifdef __cplusplus
extern "C" {
#endif
/* If set, debug output from md_parse() is sent to stderr. */
#define MD_HTML_FLAG_DEBUG 0x0001
#define MD_HTML_FLAG_VERBATIM_ENTITIES 0x0002
#define MD_HTML_FLAG_SKIP_UTF8_BOM 0x0004
#define MD_HTML_FLAG_XHTML 0x0008
/* Render Markdown into HTML.
*
* Note only contents of <body> tag is generated. Caller must generate
* HTML header/footer manually before/after calling md_html().
*
* Params input and input_size specify the Markdown input.
* Callback process_output() gets called with chunks of HTML output.
* (Typical implementation may just output the bytes to a file or append to
* some buffer).
* Param userdata is just propgated back to process_output() callback.
* Param parser_flags are flags from md4c.h propagated to md_parse().
* Param render_flags is bitmask of MD_HTML_FLAG_xxxx.
*
* Returns -1 on error (if md_parse() fails.)
* Returns 0 on success.
*/
int md_html(const MD_CHAR* input, MD_SIZE input_size,
void (*process_output)(const MD_CHAR*, MD_SIZE, void*),
void* userdata, unsigned parser_flags, unsigned renderer_flags);
void reset_hd_cnt();
#ifdef __cplusplus
} /* extern "C" { */
#endif
#endif /* MD4C_HTML_H */

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

File diff suppressed because it is too large Load Diff

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

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

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

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

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

File diff suppressed because it is too large Load Diff

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

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

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

@ -0,0 +1,90 @@
/*
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
*/
#pragma once
#ifndef ZIP_H
#define ZIP_H
#include <string.h>
#ifdef __cplusplus
extern "C" {
#endif
#ifndef MAX_PATH
#define MAX_PATH 32767 /* # chars in a path name including NULL */
#endif
#define ZIP_DEFAULT_COMPRESSION_LEVEL 6
/* This data structure is used throughout the library to represent zip archive - forward declaration. */
struct zip_t;
/*
Opens zip archive with compression level.
If append is 0 then new archive will be created, otherwise function will try to append to the specified zip archive,
instead of creating a new one.
Compression levels: 0-9 are the standard zlib-style levels.
Returns pointer to zip_t structure or NULL on error.
*/
struct zip_t *zip_open(const char *zipname, int level, int append);
/* Closes zip archive, releases resources - always finalize. */
void zip_close(struct zip_t *zip);
/*
Opens a new entry for writing in a zip archive.
Returns negative number (< 0) on error, 0 on success.
*/
int zip_entry_open(struct zip_t *zip, const char *entryname);
/*
Closes zip entry, flushes buffer and releases resources.
Returns negative number (< 0) on error, 0 on success.
*/
int zip_entry_close(struct zip_t *zip);
/*
Compresses an input buffer for the current zip entry.
Returns negative number (< 0) on error, 0 on success.
*/
int zip_entry_write(struct zip_t *zip, const void *buf, size_t bufsize);
/*
Compresses a file for the current zip entry.
Returns negative number (< 0) on error, 0 on success.
*/
int zip_entry_fwrite(struct zip_t *zip, const char *filename);
/*
Creates a new archive and puts len files into a single zip archive
Returns negative number (< 0) on error, 0 on success.
*/
int zip_create(const char *zipname, const char *filenames[], size_t len);
/*
Extracts a zip archive file into dir.
If on_extract_entry is not NULL, the callback will be called after successfully extracted each zip entry.
Returning a negative value from the callback will cause abort the extract and return an error.
The last argument (void *arg) is optional, which you can use to pass some data to the on_extract_entry callback.
Returns negative number (< 0) on error, 0 on success.
*/
int zip_extract(const char *zipname, const char *dir, int (* on_extract_entry)(const char *filename, void *arg), void *arg);
#ifdef __cplusplus
}
#endif
#endif