sassc 0.0.10 → 0.0.11
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/ext/libsass/.gitignore +6 -0
- data/ext/libsass/.travis.yml +5 -1
- data/ext/libsass/Makefile +12 -3
- data/ext/libsass/Makefile.am +16 -28
- data/ext/libsass/Readme.md +1 -0
- data/ext/libsass/appveyor.yml +1 -2
- data/ext/libsass/ast.cpp +9 -0
- data/ext/libsass/ast.hpp +152 -55
- data/ext/libsass/ast_factory.hpp +2 -0
- data/ext/libsass/ast_fwd_decl.hpp +1 -0
- data/ext/libsass/backtrace.hpp +2 -2
- data/ext/libsass/bind.cpp +15 -13
- data/ext/libsass/configure.ac +17 -5
- data/ext/libsass/constants.cpp +22 -2
- data/ext/libsass/constants.hpp +21 -2
- data/ext/libsass/context.cpp +79 -57
- data/ext/libsass/context.hpp +23 -9
- data/ext/libsass/contextualize.cpp +2 -28
- data/ext/libsass/contextualize.hpp +6 -10
- data/ext/libsass/contextualize_eval.cpp +93 -0
- data/ext/libsass/contextualize_eval.hpp +44 -0
- data/ext/libsass/contrib/plugin.cpp +57 -0
- data/ext/libsass/cssize.cpp +3 -1
- data/ext/libsass/debugger.hpp +242 -83
- data/ext/libsass/emitter.cpp +1 -1
- data/ext/libsass/emitter.hpp +1 -1
- data/ext/libsass/environment.hpp +109 -25
- data/ext/libsass/error_handling.cpp +3 -3
- data/ext/libsass/error_handling.hpp +0 -1
- data/ext/libsass/eval.cpp +145 -61
- data/ext/libsass/eval.hpp +9 -1
- data/ext/libsass/expand.cpp +134 -60
- data/ext/libsass/expand.hpp +5 -2
- data/ext/libsass/extend.cpp +7 -5
- data/ext/libsass/file.cpp +176 -123
- data/ext/libsass/file.hpp +44 -7
- data/ext/libsass/functions.cpp +36 -17
- data/ext/libsass/functions.hpp +2 -2
- data/ext/libsass/inspect.cpp +23 -14
- data/ext/libsass/inspect.hpp +1 -0
- data/ext/libsass/json.cpp +132 -135
- data/ext/libsass/lexer.cpp +133 -0
- data/ext/libsass/lexer.hpp +239 -0
- data/ext/libsass/listize.cpp +83 -0
- data/ext/libsass/listize.hpp +41 -0
- data/ext/libsass/operation.hpp +2 -0
- data/ext/libsass/output.cpp +5 -6
- data/ext/libsass/parser.cpp +426 -388
- data/ext/libsass/parser.hpp +97 -109
- data/ext/libsass/plugins.cpp +15 -2
- data/ext/libsass/plugins.hpp +6 -4
- data/ext/libsass/position.cpp +52 -17
- data/ext/libsass/position.hpp +19 -17
- data/ext/libsass/prelexer.cpp +202 -235
- data/ext/libsass/prelexer.hpp +73 -333
- data/ext/libsass/sass.cpp +21 -11
- data/ext/libsass/sass.h +6 -6
- data/ext/libsass/sass_context.cpp +167 -81
- data/ext/libsass/sass_context.h +26 -6
- data/ext/libsass/sass_functions.cpp +49 -40
- data/ext/libsass/sass_functions.h +55 -43
- data/ext/libsass/sass_interface.cpp +9 -8
- data/ext/libsass/sass_interface.h +3 -3
- data/ext/libsass/sass_version.h +8 -0
- data/ext/libsass/sass_version.h.in +8 -0
- data/ext/libsass/script/ci-build-libsass +3 -3
- data/ext/libsass/script/ci-report-coverage +2 -1
- data/ext/libsass/source_map.cpp +2 -2
- data/ext/libsass/util.cpp +60 -11
- data/ext/libsass/util.hpp +6 -1
- data/ext/libsass/win/libsass.filters +12 -0
- data/ext/libsass/win/libsass.vcxproj +10 -0
- data/lib/sassc.rb +3 -1
- data/lib/sassc/cache_stores/base.rb +2 -0
- data/lib/sassc/dependency.rb +3 -1
- data/lib/sassc/engine.rb +31 -16
- data/lib/sassc/error.rb +3 -2
- data/lib/sassc/functions_handler.rb +54 -0
- data/lib/sassc/import_handler.rb +41 -0
- data/lib/sassc/importer.rb +4 -31
- data/lib/sassc/native.rb +1 -1
- data/lib/sassc/native/native_context_api.rb +3 -2
- data/lib/sassc/script.rb +0 -51
- data/lib/sassc/version.rb +1 -1
- data/sassc.gemspec +1 -0
- data/test/custom_importer_test.rb +72 -69
- data/test/engine_test.rb +53 -54
- data/test/functions_test.rb +40 -39
- data/test/native_test.rb +145 -149
- data/test/output_style_test.rb +98 -0
- data/test/test_helper.rb +21 -7
- metadata +28 -2
data/ext/libsass/file.hpp
CHANGED
@@ -2,21 +2,58 @@
|
|
2
2
|
#define SASS_FILE_H
|
3
3
|
|
4
4
|
#include <string>
|
5
|
+
#include <vector>
|
5
6
|
|
6
7
|
namespace Sass {
|
7
8
|
using namespace std;
|
8
9
|
class Context;
|
9
10
|
namespace File {
|
11
|
+
|
12
|
+
// return the current directory
|
13
|
+
// always with forward slashes
|
10
14
|
string get_cwd();
|
11
|
-
|
12
|
-
|
13
|
-
|
15
|
+
|
16
|
+
// test if path exists and is a file
|
17
|
+
bool file_exists(const string& file);
|
18
|
+
|
19
|
+
// return if given path is absolute
|
20
|
+
// works with *nix and windows paths
|
14
21
|
bool is_absolute_path(const string& path);
|
22
|
+
|
23
|
+
// return only the directory part of path
|
24
|
+
string dir_name(const string& path);
|
25
|
+
|
26
|
+
// return only the filename part of path
|
27
|
+
string base_name(const string&);
|
28
|
+
|
29
|
+
// do a locigal clean up of the path
|
30
|
+
// no physical check on the filesystem
|
15
31
|
string make_canonical_path (string path);
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
32
|
+
|
33
|
+
// join two path segments cleanly together
|
34
|
+
// but only if right side is not absolute yet
|
35
|
+
string join_paths(string root, string name);
|
36
|
+
|
37
|
+
// create an absolute path by resolving relative paths with cwd
|
38
|
+
string make_absolute_path(const string& path, const string& cwd = ".");
|
39
|
+
|
40
|
+
// create a path that is relative to the given base directory
|
41
|
+
// path and base will first be resolved against cwd to make them absolute
|
42
|
+
string resolve_relative_path(const string& path, const string& base, const string& cwd = ".");
|
43
|
+
|
44
|
+
// try to find/resolve the filename
|
45
|
+
string resolve_file(const string& file);
|
46
|
+
|
47
|
+
// helper function to resolve a filename
|
48
|
+
string find_file(const string& file, const vector<string> paths);
|
49
|
+
// inc paths can be directly passed from C code
|
50
|
+
string find_file(const string& file, const char** paths);
|
51
|
+
|
52
|
+
// try to load the given filename
|
53
|
+
// returned memory must be freed
|
54
|
+
// will auto convert .sass files
|
55
|
+
char* read_file(const string& file);
|
56
|
+
|
20
57
|
}
|
21
58
|
}
|
22
59
|
|
data/ext/libsass/functions.cpp
CHANGED
@@ -35,7 +35,7 @@ namespace Sass {
|
|
35
35
|
using std::stringstream;
|
36
36
|
using std::endl;
|
37
37
|
|
38
|
-
Definition* make_native_function(Signature sig, Native_Function
|
38
|
+
Definition* make_native_function(Signature sig, Native_Function func, Context& ctx)
|
39
39
|
{
|
40
40
|
Parser sig_parser = Parser::from_c_str(sig, ctx, ParserState("[built-in function]"));
|
41
41
|
sig_parser.lex<Prelexer::identifier>();
|
@@ -45,12 +45,14 @@ namespace Sass {
|
|
45
45
|
sig,
|
46
46
|
name,
|
47
47
|
params,
|
48
|
-
|
48
|
+
func,
|
49
|
+
&ctx,
|
49
50
|
false);
|
50
51
|
}
|
51
52
|
|
52
|
-
Definition* make_c_function(
|
53
|
+
Definition* make_c_function(Sass_Function_Entry c_func, Context& ctx)
|
53
54
|
{
|
55
|
+
const char* sig = sass_function_get_signature(c_func);
|
54
56
|
Parser sig_parser = Parser::from_c_str(sig, ctx, ParserState("[c function]"));
|
55
57
|
// allow to overload generic callback plus @warn, @error and @debug with custom functions
|
56
58
|
sig_parser.lex < alternatives < identifier, exactly <'*'>,
|
@@ -64,8 +66,8 @@ namespace Sass {
|
|
64
66
|
sig,
|
65
67
|
name,
|
66
68
|
params,
|
67
|
-
|
68
|
-
|
69
|
+
c_func,
|
70
|
+
&ctx,
|
69
71
|
false, true);
|
70
72
|
}
|
71
73
|
|
@@ -145,7 +147,11 @@ namespace Sass {
|
|
145
147
|
static mt19937 rand(static_cast<unsigned int>(GetSeed()));
|
146
148
|
|
147
149
|
// features
|
148
|
-
static set<string> features
|
150
|
+
static set<string> features {
|
151
|
+
"global-variable-shadowing",
|
152
|
+
"at-error",
|
153
|
+
"units-level-3"
|
154
|
+
};
|
149
155
|
|
150
156
|
////////////////
|
151
157
|
// RGB FUNCTIONS
|
@@ -940,7 +946,7 @@ namespace Sass {
|
|
940
946
|
error(msg, pstate, backtrace);
|
941
947
|
}
|
942
948
|
catch (...) { throw; }
|
943
|
-
return new (ctx.mem)
|
949
|
+
return new (ctx.mem) String_Quoted(pstate, newstr);
|
944
950
|
}
|
945
951
|
|
946
952
|
Signature to_upper_case_sig = "to-upper-case($string)";
|
@@ -1065,16 +1071,16 @@ namespace Sass {
|
|
1065
1071
|
BUILT_IN(random)
|
1066
1072
|
{
|
1067
1073
|
Number* l = dynamic_cast<Number*>(env["$limit"]);
|
1068
|
-
if (l && trunc(l->value()) != l->value()) error("argument $limit of `" + string(sig) + "` must be an integer", pstate);
|
1069
1074
|
if (l) {
|
1075
|
+
if (trunc(l->value()) != l->value() || l->value() == 0) error("argument $limit of `" + string(sig) + "` must be a positive integer", pstate);
|
1070
1076
|
uniform_real_distribution<> distributor(1, l->value() + 1);
|
1071
1077
|
uint_fast32_t distributed = static_cast<uint_fast32_t>(distributor(rand));
|
1072
1078
|
return new (ctx.mem) Number(pstate, (double)distributed);
|
1073
1079
|
}
|
1074
1080
|
else {
|
1075
1081
|
uniform_real_distribution<> distributor(0, 1);
|
1076
|
-
|
1077
|
-
return new (ctx.mem) Number(pstate,
|
1082
|
+
double distributed = static_cast<double>(distributor(rand));
|
1083
|
+
return new (ctx.mem) Number(pstate, distributed);
|
1078
1084
|
}
|
1079
1085
|
}
|
1080
1086
|
|
@@ -1408,7 +1414,7 @@ namespace Sass {
|
|
1408
1414
|
{
|
1409
1415
|
string s = Util::normalize_underscores(unquote(ARG("$name", String_Constant)->value()));
|
1410
1416
|
|
1411
|
-
if(d_env.
|
1417
|
+
if(d_env.has_global("$"+s)) {
|
1412
1418
|
return new (ctx.mem) Boolean(pstate, true);
|
1413
1419
|
}
|
1414
1420
|
else {
|
@@ -1421,7 +1427,7 @@ namespace Sass {
|
|
1421
1427
|
{
|
1422
1428
|
string s = Util::normalize_underscores(unquote(ARG("$name", String_Constant)->value()));
|
1423
1429
|
|
1424
|
-
if(d_env.
|
1430
|
+
if(d_env.has_global(s+"[f]")) {
|
1425
1431
|
return new (ctx.mem) Boolean(pstate, true);
|
1426
1432
|
}
|
1427
1433
|
else {
|
@@ -1434,7 +1440,7 @@ namespace Sass {
|
|
1434
1440
|
{
|
1435
1441
|
string s = Util::normalize_underscores(unquote(ARG("$name", String_Constant)->value()));
|
1436
1442
|
|
1437
|
-
if(d_env.
|
1443
|
+
if(d_env.has_global(s+"[m]")) {
|
1438
1444
|
return new (ctx.mem) Boolean(pstate, true);
|
1439
1445
|
}
|
1440
1446
|
else {
|
@@ -1463,11 +1469,22 @@ namespace Sass {
|
|
1463
1469
|
|
1464
1470
|
Arguments* args = new (ctx.mem) Arguments(pstate);
|
1465
1471
|
for (size_t i = 0, L = arglist->length(); i < L; ++i) {
|
1466
|
-
|
1467
|
-
|
1472
|
+
Expression* expr = arglist->value_at_index(i);
|
1473
|
+
if (arglist->is_arglist()) {
|
1474
|
+
Argument* arg = static_cast<Argument*>(expr);
|
1475
|
+
*args << new (ctx.mem) Argument(pstate,
|
1476
|
+
expr,
|
1477
|
+
"",
|
1478
|
+
arg->is_rest_argument(),
|
1479
|
+
arg->is_keyword_argument());
|
1480
|
+
} else {
|
1481
|
+
*args << new (ctx.mem) Argument(pstate, expr);
|
1482
|
+
}
|
1468
1483
|
}
|
1469
1484
|
Function_Call* func = new (ctx.mem) Function_Call(pstate, name, args);
|
1470
|
-
|
1485
|
+
Contextualize contextualize(ctx, &d_env, backtrace);
|
1486
|
+
Listize listize(ctx);
|
1487
|
+
Eval eval(ctx, &contextualize, &listize, &d_env, backtrace);
|
1471
1488
|
return func->perform(&eval);
|
1472
1489
|
|
1473
1490
|
}
|
@@ -1485,7 +1502,9 @@ namespace Sass {
|
|
1485
1502
|
// { return ARG("$condition", Expression)->is_false() ? ARG("$if-false", Expression) : ARG("$if-true", Expression); }
|
1486
1503
|
BUILT_IN(sass_if)
|
1487
1504
|
{
|
1488
|
-
|
1505
|
+
Contextualize contextualize(ctx, &d_env, backtrace);
|
1506
|
+
Listize listize(ctx);
|
1507
|
+
Eval eval(ctx, &contextualize, &listize, &d_env, backtrace);
|
1489
1508
|
bool is_true = !ARG("$condition", Expression)->perform(&eval)->is_false();
|
1490
1509
|
if (is_true) {
|
1491
1510
|
return ARG("$if-true", Expression)->perform(&eval);
|
data/ext/libsass/functions.hpp
CHANGED
@@ -20,8 +20,8 @@ namespace Sass {
|
|
20
20
|
typedef const char* Signature;
|
21
21
|
typedef Expression* (*Native_Function)(Env&, Env&, Context&, Signature, ParserState, Backtrace*);
|
22
22
|
|
23
|
-
Definition* make_native_function(Signature, Native_Function, Context&);
|
24
|
-
Definition* make_c_function(
|
23
|
+
Definition* make_native_function(Signature, Native_Function, Context& ctx);
|
24
|
+
Definition* make_c_function(Sass_Function_Entry c_func, Context& ctx);
|
25
25
|
|
26
26
|
namespace Functions {
|
27
27
|
|
data/ext/libsass/inspect.cpp
CHANGED
@@ -44,9 +44,8 @@ namespace Sass {
|
|
44
44
|
|
45
45
|
void Inspect::operator()(Keyframe_Rule* rule)
|
46
46
|
{
|
47
|
-
|
48
|
-
if (rule->
|
49
|
-
rule->block()->perform(this);
|
47
|
+
if (rule->selector()) rule->selector()->perform(this);
|
48
|
+
if (rule->block()) rule->block()->perform(this);
|
50
49
|
}
|
51
50
|
|
52
51
|
void Inspect::operator()(Propset* propset)
|
@@ -59,14 +58,10 @@ namespace Sass {
|
|
59
58
|
void Inspect::operator()(Bubble* bubble)
|
60
59
|
{
|
61
60
|
append_indentation();
|
62
|
-
append_token("
|
63
|
-
|
64
|
-
append_string("(");
|
65
|
-
append_optional_space();
|
61
|
+
append_token("::BUBBLE", bubble);
|
62
|
+
append_scope_opener();
|
66
63
|
bubble->node()->perform(this);
|
67
|
-
|
68
|
-
append_string(")");
|
69
|
-
append_optional_space();
|
64
|
+
append_scope_closer();
|
70
65
|
}
|
71
66
|
|
72
67
|
void Inspect::operator()(Media_Block* media_block)
|
@@ -104,9 +99,9 @@ namespace Sass {
|
|
104
99
|
append_token(at_rule->keyword(), at_rule);
|
105
100
|
if (at_rule->selector()) {
|
106
101
|
append_mandatory_space();
|
107
|
-
|
102
|
+
in_wrapped = true;
|
108
103
|
at_rule->selector()->perform(this);
|
109
|
-
|
104
|
+
in_wrapped = false;
|
110
105
|
}
|
111
106
|
if (at_rule->block()) {
|
112
107
|
at_rule->block()->perform(this);
|
@@ -141,7 +136,7 @@ namespace Sass {
|
|
141
136
|
append_token(assn->variable(), assn);
|
142
137
|
append_colon_separator();
|
143
138
|
assn->value()->perform(this);
|
144
|
-
if (assn->
|
139
|
+
if (assn->is_default()) {
|
145
140
|
append_optional_space();
|
146
141
|
append_string("!default");
|
147
142
|
}
|
@@ -665,6 +660,17 @@ namespace Sass {
|
|
665
660
|
append_token("null", n);
|
666
661
|
}
|
667
662
|
|
663
|
+
void Inspect::operator()(Parent_Selector* p)
|
664
|
+
{
|
665
|
+
if (p->selector()) {
|
666
|
+
p->selector()->perform(this);
|
667
|
+
append_delimiter();
|
668
|
+
}
|
669
|
+
else {
|
670
|
+
append_string("&");
|
671
|
+
}
|
672
|
+
}
|
673
|
+
|
668
674
|
// parameters and arguments
|
669
675
|
void Inspect::operator()(Parameter* p)
|
670
676
|
{
|
@@ -781,9 +787,12 @@ namespace Sass {
|
|
781
787
|
|
782
788
|
void Inspect::operator()(Wrapped_Selector* s)
|
783
789
|
{
|
790
|
+
bool was = in_wrapped;
|
791
|
+
in_wrapped = true;
|
784
792
|
append_token(s->name(), s);
|
785
793
|
s->selector()->perform(this);
|
786
794
|
append_string(")");
|
795
|
+
in_wrapped = was;
|
787
796
|
}
|
788
797
|
|
789
798
|
void Inspect::operator()(Compound_Selector* s)
|
@@ -838,7 +847,7 @@ namespace Sass {
|
|
838
847
|
{
|
839
848
|
if (g->empty()) return;
|
840
849
|
for (size_t i = 0, L = g->length(); i < L; ++i) {
|
841
|
-
if (!
|
850
|
+
if (!in_wrapped && i == 0) append_indentation();
|
842
851
|
(*g)[i]->perform(this);
|
843
852
|
if (i < L - 1) {
|
844
853
|
append_comma_separator();
|
data/ext/libsass/inspect.hpp
CHANGED
@@ -71,6 +71,7 @@ namespace Sass {
|
|
71
71
|
virtual void operator()(Media_Query_Expression*);
|
72
72
|
virtual void operator()(At_Root_Expression*);
|
73
73
|
virtual void operator()(Null*);
|
74
|
+
virtual void operator()(Parent_Selector* p);
|
74
75
|
// parameters and arguments
|
75
76
|
virtual void operator()(Parameter*);
|
76
77
|
virtual void operator()(Parameters*);
|
data/ext/libsass/json.cpp
CHANGED
@@ -102,11 +102,11 @@ static void sb_grow(SB *sb, int need)
|
|
102
102
|
{
|
103
103
|
size_t length = sb->cur - sb->start;
|
104
104
|
size_t alloc = sb->end - sb->start;
|
105
|
-
|
105
|
+
|
106
106
|
do {
|
107
107
|
alloc *= 2;
|
108
108
|
} while (alloc < length + need);
|
109
|
-
|
109
|
+
|
110
110
|
sb->start = (char*) realloc(sb->start, alloc + 1);
|
111
111
|
if (sb->start == NULL)
|
112
112
|
out_of_memory();
|
@@ -150,13 +150,10 @@ static void sb_free(SB *sb)
|
|
150
150
|
* These are taken from the ccan/charset module and customized a bit.
|
151
151
|
* Putting them here means the compiler can (choose to) inline them,
|
152
152
|
* and it keeps ccan/json from having a dependency.
|
153
|
-
|
154
|
-
|
155
|
-
/*
|
156
|
-
* Type for Unicode codepoints.
|
153
|
+
*
|
154
|
+
* We use uint32_t Type for Unicode codepoints.
|
157
155
|
* We need our own because wchar_t might be 16 bits.
|
158
156
|
*/
|
159
|
-
typedef uint32_t uchar_t;
|
160
157
|
|
161
158
|
/*
|
162
159
|
* Validate a single UTF-8 character starting at @s.
|
@@ -181,7 +178,7 @@ typedef uint32_t uchar_t;
|
|
181
178
|
static int utf8_validate_cz(const char *s)
|
182
179
|
{
|
183
180
|
unsigned char c = *s++;
|
184
|
-
|
181
|
+
|
185
182
|
if (c <= 0x7F) { /* 00..7F */
|
186
183
|
return 1;
|
187
184
|
} else if (c <= 0xC1) { /* 80..C1 */
|
@@ -191,33 +188,33 @@ static int utf8_validate_cz(const char *s)
|
|
191
188
|
/* Make sure subsequent byte is in the range 0x80..0xBF. */
|
192
189
|
if (((unsigned char)*s++ & 0xC0) != 0x80)
|
193
190
|
return 0;
|
194
|
-
|
191
|
+
|
195
192
|
return 2;
|
196
193
|
} else if (c <= 0xEF) { /* E0..EF */
|
197
194
|
/* Disallow overlong 3-byte sequence. */
|
198
195
|
if (c == 0xE0 && (unsigned char)*s < 0xA0)
|
199
196
|
return 0;
|
200
|
-
|
197
|
+
|
201
198
|
/* Disallow U+D800..U+DFFF. */
|
202
199
|
if (c == 0xED && (unsigned char)*s > 0x9F)
|
203
200
|
return 0;
|
204
|
-
|
201
|
+
|
205
202
|
/* Make sure subsequent bytes are in the range 0x80..0xBF. */
|
206
203
|
if (((unsigned char)*s++ & 0xC0) != 0x80)
|
207
204
|
return 0;
|
208
205
|
if (((unsigned char)*s++ & 0xC0) != 0x80)
|
209
206
|
return 0;
|
210
|
-
|
207
|
+
|
211
208
|
return 3;
|
212
209
|
} else if (c <= 0xF4) { /* F0..F4 */
|
213
210
|
/* Disallow overlong 4-byte sequence. */
|
214
211
|
if (c == 0xF0 && (unsigned char)*s < 0x90)
|
215
212
|
return 0;
|
216
|
-
|
213
|
+
|
217
214
|
/* Disallow codepoints beyond U+10FFFF. */
|
218
215
|
if (c == 0xF4 && (unsigned char)*s > 0x8F)
|
219
216
|
return 0;
|
220
|
-
|
217
|
+
|
221
218
|
/* Make sure subsequent bytes are in the range 0x80..0xBF. */
|
222
219
|
if (((unsigned char)*s++ & 0xC0) != 0x80)
|
223
220
|
return 0;
|
@@ -225,7 +222,7 @@ static int utf8_validate_cz(const char *s)
|
|
225
222
|
return 0;
|
226
223
|
if (((unsigned char)*s++ & 0xC0) != 0x80)
|
227
224
|
return 0;
|
228
|
-
|
225
|
+
|
229
226
|
return 4;
|
230
227
|
} else { /* F5..FF */
|
231
228
|
return 0;
|
@@ -236,13 +233,13 @@ static int utf8_validate_cz(const char *s)
|
|
236
233
|
static bool utf8_validate(const char *s)
|
237
234
|
{
|
238
235
|
int len;
|
239
|
-
|
236
|
+
|
240
237
|
for (; *s != 0; s += len) {
|
241
238
|
len = utf8_validate_cz(s);
|
242
239
|
if (len == 0)
|
243
240
|
return false;
|
244
241
|
}
|
245
|
-
|
242
|
+
|
246
243
|
return true;
|
247
244
|
}
|
248
245
|
|
@@ -253,10 +250,10 @@ static bool utf8_validate(const char *s)
|
|
253
250
|
* This function assumes input is valid UTF-8,
|
254
251
|
* and that there are enough characters in front of @s.
|
255
252
|
*/
|
256
|
-
static int utf8_read_char(const char *s,
|
253
|
+
static int utf8_read_char(const char *s, uint32_t *out)
|
257
254
|
{
|
258
255
|
const unsigned char *c = (const unsigned char*) s;
|
259
|
-
|
256
|
+
|
260
257
|
assert(utf8_validate_cz(s));
|
261
258
|
|
262
259
|
if (c[0] <= 0x7F) {
|
@@ -265,21 +262,21 @@ static int utf8_read_char(const char *s, uchar_t *out)
|
|
265
262
|
return 1;
|
266
263
|
} else if (c[0] <= 0xDF) {
|
267
264
|
/* C2..DF (unless input is invalid) */
|
268
|
-
*out = ((
|
269
|
-
((
|
265
|
+
*out = ((uint32_t)c[0] & 0x1F) << 6 |
|
266
|
+
((uint32_t)c[1] & 0x3F);
|
270
267
|
return 2;
|
271
268
|
} else if (c[0] <= 0xEF) {
|
272
269
|
/* E0..EF */
|
273
|
-
*out = ((
|
274
|
-
((
|
275
|
-
((
|
270
|
+
*out = ((uint32_t)c[0] & 0xF) << 12 |
|
271
|
+
((uint32_t)c[1] & 0x3F) << 6 |
|
272
|
+
((uint32_t)c[2] & 0x3F);
|
276
273
|
return 3;
|
277
274
|
} else {
|
278
275
|
/* F0..F4 (unless input is invalid) */
|
279
|
-
*out = ((
|
280
|
-
((
|
281
|
-
((
|
282
|
-
((
|
276
|
+
*out = ((uint32_t)c[0] & 0x7) << 18 |
|
277
|
+
((uint32_t)c[1] & 0x3F) << 12 |
|
278
|
+
((uint32_t)c[2] & 0x3F) << 6 |
|
279
|
+
((uint32_t)c[3] & 0x3F);
|
283
280
|
return 4;
|
284
281
|
}
|
285
282
|
}
|
@@ -292,10 +289,10 @@ static int utf8_read_char(const char *s, uchar_t *out)
|
|
292
289
|
*
|
293
290
|
* This function will write up to 4 bytes to @out.
|
294
291
|
*/
|
295
|
-
static int utf8_write_char(
|
292
|
+
static int utf8_write_char(uint32_t unicode, char *out)
|
296
293
|
{
|
297
294
|
unsigned char *o = (unsigned char*) out;
|
298
|
-
|
295
|
+
|
299
296
|
assert(unicode <= 0x10FFFF && !(unicode >= 0xD800 && unicode <= 0xDFFF));
|
300
297
|
|
301
298
|
if (unicode <= 0x7F) {
|
@@ -329,10 +326,10 @@ static int utf8_write_char(uchar_t unicode, char *out)
|
|
329
326
|
* @uc should be 0xD800..0xDBFF, and @lc should be 0xDC00..0xDFFF.
|
330
327
|
* If they aren't, this function returns false.
|
331
328
|
*/
|
332
|
-
static bool from_surrogate_pair(uint16_t uc, uint16_t lc,
|
329
|
+
static bool from_surrogate_pair(uint16_t uc, uint16_t lc, uint32_t *unicode)
|
333
330
|
{
|
334
331
|
if (uc >= 0xD800 && uc <= 0xDBFF && lc >= 0xDC00 && lc <= 0xDFFF) {
|
335
|
-
*unicode = 0x10000 + ((((
|
332
|
+
*unicode = 0x10000 + ((((uint32_t)uc & 0x3FF) << 10) | (lc & 0x3FF));
|
336
333
|
return true;
|
337
334
|
} else {
|
338
335
|
return false;
|
@@ -344,12 +341,12 @@ static bool from_surrogate_pair(uint16_t uc, uint16_t lc, uchar_t *unicode)
|
|
344
341
|
*
|
345
342
|
* @unicode must be U+10000..U+10FFFF.
|
346
343
|
*/
|
347
|
-
static void to_surrogate_pair(
|
344
|
+
static void to_surrogate_pair(uint32_t unicode, uint16_t *uc, uint16_t *lc)
|
348
345
|
{
|
349
|
-
|
350
|
-
|
346
|
+
uint32_t n;
|
347
|
+
|
351
348
|
assert(unicode >= 0x10000 && unicode <= 0x10FFFF);
|
352
|
-
|
349
|
+
|
353
350
|
n = unicode - 0x10000;
|
354
351
|
*uc = ((n >> 10) & 0x3FF) | 0xD800;
|
355
352
|
*lc = (n & 0x3FF) | 0xDC00;
|
@@ -392,17 +389,17 @@ JsonNode *json_decode(const char *json)
|
|
392
389
|
{
|
393
390
|
const char *s = json;
|
394
391
|
JsonNode *ret;
|
395
|
-
|
392
|
+
|
396
393
|
skip_space(&s);
|
397
394
|
if (!parse_value(&s, &ret))
|
398
395
|
return NULL;
|
399
|
-
|
396
|
+
|
400
397
|
skip_space(&s);
|
401
398
|
if (*s != 0) {
|
402
399
|
json_delete(ret);
|
403
400
|
return NULL;
|
404
401
|
}
|
405
|
-
|
402
|
+
|
406
403
|
return ret;
|
407
404
|
}
|
408
405
|
|
@@ -415,9 +412,9 @@ char *json_encode_string(const char *str)
|
|
415
412
|
{
|
416
413
|
SB sb;
|
417
414
|
sb_init(&sb);
|
418
|
-
|
415
|
+
|
419
416
|
emit_string(&sb, str);
|
420
|
-
|
417
|
+
|
421
418
|
return sb_finish(&sb);
|
422
419
|
}
|
423
420
|
|
@@ -425,12 +422,12 @@ char *json_stringify(const JsonNode *node, const char *space)
|
|
425
422
|
{
|
426
423
|
SB sb;
|
427
424
|
sb_init(&sb);
|
428
|
-
|
425
|
+
|
429
426
|
if (space != NULL)
|
430
427
|
emit_value_indented(&sb, node, space, 0);
|
431
428
|
else
|
432
429
|
emit_value(&sb, node);
|
433
|
-
|
430
|
+
|
434
431
|
return sb_finish(&sb);
|
435
432
|
}
|
436
433
|
|
@@ -438,7 +435,7 @@ void json_delete(JsonNode *node)
|
|
438
435
|
{
|
439
436
|
if (node != NULL) {
|
440
437
|
json_remove_from_parent(node);
|
441
|
-
|
438
|
+
|
442
439
|
switch (node->tag) {
|
443
440
|
case JSON_STRING:
|
444
441
|
free(node->string_);
|
@@ -455,7 +452,7 @@ void json_delete(JsonNode *node)
|
|
455
452
|
}
|
456
453
|
default:;
|
457
454
|
}
|
458
|
-
|
455
|
+
|
459
456
|
free(node);
|
460
457
|
}
|
461
458
|
}
|
@@ -463,15 +460,15 @@ void json_delete(JsonNode *node)
|
|
463
460
|
bool json_validate(const char *json)
|
464
461
|
{
|
465
462
|
const char *s = json;
|
466
|
-
|
463
|
+
|
467
464
|
skip_space(&s);
|
468
465
|
if (!parse_value(&s, NULL))
|
469
466
|
return false;
|
470
|
-
|
467
|
+
|
471
468
|
skip_space(&s);
|
472
469
|
if (*s != 0)
|
473
470
|
return false;
|
474
|
-
|
471
|
+
|
475
472
|
return true;
|
476
473
|
}
|
477
474
|
|
@@ -479,30 +476,30 @@ JsonNode *json_find_element(JsonNode *array, int index)
|
|
479
476
|
{
|
480
477
|
JsonNode *element;
|
481
478
|
int i = 0;
|
482
|
-
|
479
|
+
|
483
480
|
if (array == NULL || array->tag != JSON_ARRAY)
|
484
481
|
return NULL;
|
485
|
-
|
482
|
+
|
486
483
|
json_foreach(element, array) {
|
487
484
|
if (i == index)
|
488
485
|
return element;
|
489
486
|
i++;
|
490
487
|
}
|
491
|
-
|
488
|
+
|
492
489
|
return NULL;
|
493
490
|
}
|
494
491
|
|
495
492
|
JsonNode *json_find_member(JsonNode *object, const char *name)
|
496
493
|
{
|
497
494
|
JsonNode *member;
|
498
|
-
|
495
|
+
|
499
496
|
if (object == NULL || object->tag != JSON_OBJECT)
|
500
497
|
return NULL;
|
501
|
-
|
498
|
+
|
502
499
|
json_foreach(member, object)
|
503
500
|
if (strcmp(member->key, name) == 0)
|
504
501
|
return member;
|
505
|
-
|
502
|
+
|
506
503
|
return NULL;
|
507
504
|
}
|
508
505
|
|
@@ -568,7 +565,7 @@ static void append_node(JsonNode *parent, JsonNode *child)
|
|
568
565
|
child->parent = parent;
|
569
566
|
child->prev = parent->children.tail;
|
570
567
|
child->next = NULL;
|
571
|
-
|
568
|
+
|
572
569
|
if (parent->children.tail != NULL)
|
573
570
|
parent->children.tail->next = child;
|
574
571
|
else
|
@@ -581,7 +578,7 @@ static void prepend_node(JsonNode *parent, JsonNode *child)
|
|
581
578
|
child->parent = parent;
|
582
579
|
child->prev = NULL;
|
583
580
|
child->next = parent->children.head;
|
584
|
-
|
581
|
+
|
585
582
|
if (parent->children.head != NULL)
|
586
583
|
parent->children.head->prev = child;
|
587
584
|
else
|
@@ -599,7 +596,7 @@ void json_append_element(JsonNode *array, JsonNode *element)
|
|
599
596
|
{
|
600
597
|
assert(array->tag == JSON_ARRAY);
|
601
598
|
assert(element->parent == NULL);
|
602
|
-
|
599
|
+
|
603
600
|
append_node(array, element);
|
604
601
|
}
|
605
602
|
|
@@ -607,7 +604,7 @@ void json_prepend_element(JsonNode *array, JsonNode *element)
|
|
607
604
|
{
|
608
605
|
assert(array->tag == JSON_ARRAY);
|
609
606
|
assert(element->parent == NULL);
|
610
|
-
|
607
|
+
|
611
608
|
prepend_node(array, element);
|
612
609
|
}
|
613
610
|
|
@@ -615,7 +612,7 @@ void json_append_member(JsonNode *object, const char *key, JsonNode *value)
|
|
615
612
|
{
|
616
613
|
assert(object->tag == JSON_OBJECT);
|
617
614
|
assert(value->parent == NULL);
|
618
|
-
|
615
|
+
|
619
616
|
append_member(object, json_strdup(key), value);
|
620
617
|
}
|
621
618
|
|
@@ -623,7 +620,7 @@ void json_prepend_member(JsonNode *object, const char *key, JsonNode *value)
|
|
623
620
|
{
|
624
621
|
assert(object->tag == JSON_OBJECT);
|
625
622
|
assert(value->parent == NULL);
|
626
|
-
|
623
|
+
|
627
624
|
value->key = json_strdup(key);
|
628
625
|
prepend_node(object, value);
|
629
626
|
}
|
@@ -631,7 +628,7 @@ void json_prepend_member(JsonNode *object, const char *key, JsonNode *value)
|
|
631
628
|
void json_remove_from_parent(JsonNode *node)
|
632
629
|
{
|
633
630
|
JsonNode *parent = node->parent;
|
634
|
-
|
631
|
+
|
635
632
|
if (parent != NULL) {
|
636
633
|
if (node->prev != NULL)
|
637
634
|
node->prev->next = node->next;
|
@@ -641,9 +638,9 @@ void json_remove_from_parent(JsonNode *node)
|
|
641
638
|
node->next->prev = node->prev;
|
642
639
|
else
|
643
640
|
parent->children.tail = node->prev;
|
644
|
-
|
641
|
+
|
645
642
|
free(node->key);
|
646
|
-
|
643
|
+
|
647
644
|
node->parent = NULL;
|
648
645
|
node->prev = node->next = NULL;
|
649
646
|
node->key = NULL;
|
@@ -653,7 +650,7 @@ void json_remove_from_parent(JsonNode *node)
|
|
653
650
|
static bool parse_value(const char **sp, JsonNode **out)
|
654
651
|
{
|
655
652
|
const char *s = *sp;
|
656
|
-
|
653
|
+
|
657
654
|
switch (*s) {
|
658
655
|
case 'n':
|
659
656
|
if (expect_literal(&s, "null")) {
|
@@ -663,7 +660,7 @@ static bool parse_value(const char **sp, JsonNode **out)
|
|
663
660
|
return true;
|
664
661
|
}
|
665
662
|
return false;
|
666
|
-
|
663
|
+
|
667
664
|
case 'f':
|
668
665
|
if (expect_literal(&s, "false")) {
|
669
666
|
if (out)
|
@@ -672,7 +669,7 @@ static bool parse_value(const char **sp, JsonNode **out)
|
|
672
669
|
return true;
|
673
670
|
}
|
674
671
|
return false;
|
675
|
-
|
672
|
+
|
676
673
|
case 't':
|
677
674
|
if (expect_literal(&s, "true")) {
|
678
675
|
if (out)
|
@@ -681,7 +678,7 @@ static bool parse_value(const char **sp, JsonNode **out)
|
|
681
678
|
return true;
|
682
679
|
}
|
683
680
|
return false;
|
684
|
-
|
681
|
+
|
685
682
|
case '"': {
|
686
683
|
char *str;
|
687
684
|
if (parse_string(&s, out ? &str : NULL)) {
|
@@ -692,21 +689,21 @@ static bool parse_value(const char **sp, JsonNode **out)
|
|
692
689
|
}
|
693
690
|
return false;
|
694
691
|
}
|
695
|
-
|
692
|
+
|
696
693
|
case '[':
|
697
694
|
if (parse_array(&s, out)) {
|
698
695
|
*sp = s;
|
699
696
|
return true;
|
700
697
|
}
|
701
698
|
return false;
|
702
|
-
|
699
|
+
|
703
700
|
case '{':
|
704
701
|
if (parse_object(&s, out)) {
|
705
702
|
*sp = s;
|
706
703
|
return true;
|
707
704
|
}
|
708
705
|
return false;
|
709
|
-
|
706
|
+
|
710
707
|
default: {
|
711
708
|
double num;
|
712
709
|
if (parse_number(&s, out ? &num : NULL)) {
|
@@ -725,34 +722,34 @@ static bool parse_array(const char **sp, JsonNode **out)
|
|
725
722
|
const char *s = *sp;
|
726
723
|
JsonNode *ret = out ? json_mkarray() : NULL;
|
727
724
|
JsonNode *element;
|
728
|
-
|
725
|
+
|
729
726
|
if (*s++ != '[')
|
730
727
|
goto failure;
|
731
728
|
skip_space(&s);
|
732
|
-
|
729
|
+
|
733
730
|
if (*s == ']') {
|
734
731
|
s++;
|
735
732
|
goto success;
|
736
733
|
}
|
737
|
-
|
734
|
+
|
738
735
|
for (;;) {
|
739
736
|
if (!parse_value(&s, out ? &element : NULL))
|
740
737
|
goto failure;
|
741
738
|
skip_space(&s);
|
742
|
-
|
739
|
+
|
743
740
|
if (out)
|
744
741
|
json_append_element(ret, element);
|
745
|
-
|
742
|
+
|
746
743
|
if (*s == ']') {
|
747
744
|
s++;
|
748
745
|
goto success;
|
749
746
|
}
|
750
|
-
|
747
|
+
|
751
748
|
if (*s++ != ',')
|
752
749
|
goto failure;
|
753
750
|
skip_space(&s);
|
754
751
|
}
|
755
|
-
|
752
|
+
|
756
753
|
success:
|
757
754
|
*sp = s;
|
758
755
|
if (out)
|
@@ -770,42 +767,42 @@ static bool parse_object(const char **sp, JsonNode **out)
|
|
770
767
|
JsonNode *ret = out ? json_mkobject() : NULL;
|
771
768
|
char *key;
|
772
769
|
JsonNode *value;
|
773
|
-
|
770
|
+
|
774
771
|
if (*s++ != '{')
|
775
772
|
goto failure;
|
776
773
|
skip_space(&s);
|
777
|
-
|
774
|
+
|
778
775
|
if (*s == '}') {
|
779
776
|
s++;
|
780
777
|
goto success;
|
781
778
|
}
|
782
|
-
|
779
|
+
|
783
780
|
for (;;) {
|
784
781
|
if (!parse_string(&s, out ? &key : NULL))
|
785
782
|
goto failure;
|
786
783
|
skip_space(&s);
|
787
|
-
|
784
|
+
|
788
785
|
if (*s++ != ':')
|
789
786
|
goto failure_free_key;
|
790
787
|
skip_space(&s);
|
791
|
-
|
788
|
+
|
792
789
|
if (!parse_value(&s, out ? &value : NULL))
|
793
790
|
goto failure_free_key;
|
794
791
|
skip_space(&s);
|
795
|
-
|
792
|
+
|
796
793
|
if (out)
|
797
794
|
append_member(ret, key, value);
|
798
|
-
|
795
|
+
|
799
796
|
if (*s == '}') {
|
800
797
|
s++;
|
801
798
|
goto success;
|
802
799
|
}
|
803
|
-
|
800
|
+
|
804
801
|
if (*s++ != ',')
|
805
802
|
goto failure;
|
806
803
|
skip_space(&s);
|
807
804
|
}
|
808
|
-
|
805
|
+
|
809
806
|
success:
|
810
807
|
*sp = s;
|
811
808
|
if (out)
|
@@ -827,10 +824,10 @@ bool parse_string(const char **sp, char **out)
|
|
827
824
|
char throwaway_buffer[4];
|
828
825
|
/* enough space for a UTF-8 character */
|
829
826
|
char *b;
|
830
|
-
|
827
|
+
|
831
828
|
if (*s++ != '"')
|
832
829
|
return false;
|
833
|
-
|
830
|
+
|
834
831
|
if (out) {
|
835
832
|
sb_init(&sb);
|
836
833
|
sb_need(&sb, 4);
|
@@ -838,10 +835,10 @@ bool parse_string(const char **sp, char **out)
|
|
838
835
|
} else {
|
839
836
|
b = throwaway_buffer;
|
840
837
|
}
|
841
|
-
|
838
|
+
|
842
839
|
while (*s != '"') {
|
843
840
|
unsigned char c = *s++;
|
844
|
-
|
841
|
+
|
845
842
|
/* Parse next character, and write it to b. */
|
846
843
|
if (c == '\\') {
|
847
844
|
c = *s++;
|
@@ -869,11 +866,11 @@ bool parse_string(const char **sp, char **out)
|
|
869
866
|
case 'u':
|
870
867
|
{
|
871
868
|
uint16_t uc, lc;
|
872
|
-
|
873
|
-
|
869
|
+
uint32_t unicode;
|
870
|
+
|
874
871
|
if (!parse_hex16(&s, &uc))
|
875
872
|
goto failed;
|
876
|
-
|
873
|
+
|
877
874
|
if (uc >= 0xD800 && uc <= 0xDFFF) {
|
878
875
|
/* Handle UTF-16 surrogate pair. */
|
879
876
|
if (*s++ != '\\' || *s++ != 'u' || !parse_hex16(&s, &lc))
|
@@ -886,7 +883,7 @@ bool parse_string(const char **sp, char **out)
|
|
886
883
|
} else {
|
887
884
|
unicode = uc;
|
888
885
|
}
|
889
|
-
|
886
|
+
|
890
887
|
b += utf8_write_char(unicode, b);
|
891
888
|
break;
|
892
889
|
}
|
@@ -900,16 +897,16 @@ bool parse_string(const char **sp, char **out)
|
|
900
897
|
} else {
|
901
898
|
/* Validate and echo a UTF-8 character. */
|
902
899
|
int len;
|
903
|
-
|
900
|
+
|
904
901
|
s--;
|
905
902
|
len = utf8_validate_cz(s);
|
906
903
|
if (len == 0)
|
907
904
|
goto failed; /* Invalid UTF-8 character. */
|
908
|
-
|
905
|
+
|
909
906
|
while (len--)
|
910
907
|
*b++ = *s++;
|
911
908
|
}
|
912
|
-
|
909
|
+
|
913
910
|
/*
|
914
911
|
* Update sb to know about the new bytes,
|
915
912
|
* and set up b to write another character.
|
@@ -923,7 +920,7 @@ bool parse_string(const char **sp, char **out)
|
|
923
920
|
}
|
924
921
|
}
|
925
922
|
s++;
|
926
|
-
|
923
|
+
|
927
924
|
if (out)
|
928
925
|
*out = sb_finish(&sb);
|
929
926
|
*sp = s;
|
@@ -1058,7 +1055,7 @@ void emit_value_indented(SB *out, const JsonNode *node, const char *space, int i
|
|
1058
1055
|
static void emit_array(SB *out, const JsonNode *array)
|
1059
1056
|
{
|
1060
1057
|
const JsonNode *element;
|
1061
|
-
|
1058
|
+
|
1062
1059
|
sb_putc(out, '[');
|
1063
1060
|
json_foreach(element, array) {
|
1064
1061
|
emit_value(out, element);
|
@@ -1072,18 +1069,18 @@ static void emit_array_indented(SB *out, const JsonNode *array, const char *spac
|
|
1072
1069
|
{
|
1073
1070
|
const JsonNode *element = array->children.head;
|
1074
1071
|
int i;
|
1075
|
-
|
1072
|
+
|
1076
1073
|
if (element == NULL) {
|
1077
1074
|
sb_puts(out, "[]");
|
1078
1075
|
return;
|
1079
1076
|
}
|
1080
|
-
|
1077
|
+
|
1081
1078
|
sb_puts(out, "[\n");
|
1082
1079
|
while (element != NULL) {
|
1083
1080
|
for (i = 0; i < indent_level + 1; i++)
|
1084
1081
|
sb_puts(out, space);
|
1085
1082
|
emit_value_indented(out, element, space, indent_level + 1);
|
1086
|
-
|
1083
|
+
|
1087
1084
|
element = element->next;
|
1088
1085
|
sb_puts(out, element != NULL ? ",\n" : "\n");
|
1089
1086
|
}
|
@@ -1095,7 +1092,7 @@ static void emit_array_indented(SB *out, const JsonNode *array, const char *spac
|
|
1095
1092
|
static void emit_object(SB *out, const JsonNode *object)
|
1096
1093
|
{
|
1097
1094
|
const JsonNode *member;
|
1098
|
-
|
1095
|
+
|
1099
1096
|
sb_putc(out, '{');
|
1100
1097
|
json_foreach(member, object) {
|
1101
1098
|
emit_string(out, member->key);
|
@@ -1111,12 +1108,12 @@ static void emit_object_indented(SB *out, const JsonNode *object, const char *sp
|
|
1111
1108
|
{
|
1112
1109
|
const JsonNode *member = object->children.head;
|
1113
1110
|
int i;
|
1114
|
-
|
1111
|
+
|
1115
1112
|
if (member == NULL) {
|
1116
1113
|
sb_puts(out, "{}");
|
1117
1114
|
return;
|
1118
1115
|
}
|
1119
|
-
|
1116
|
+
|
1120
1117
|
sb_puts(out, "{\n");
|
1121
1118
|
while (member != NULL) {
|
1122
1119
|
for (i = 0; i < indent_level + 1; i++)
|
@@ -1124,7 +1121,7 @@ static void emit_object_indented(SB *out, const JsonNode *object, const char *sp
|
|
1124
1121
|
emit_string(out, member->key);
|
1125
1122
|
sb_puts(out, ": ");
|
1126
1123
|
emit_value_indented(out, member, space, indent_level + 1);
|
1127
|
-
|
1124
|
+
|
1128
1125
|
member = member->next;
|
1129
1126
|
sb_puts(out, member != NULL ? ",\n" : "\n");
|
1130
1127
|
}
|
@@ -1138,20 +1135,20 @@ void emit_string(SB *out, const char *str)
|
|
1138
1135
|
bool escape_unicode = false;
|
1139
1136
|
const char *s = str;
|
1140
1137
|
char *b;
|
1141
|
-
|
1138
|
+
|
1142
1139
|
assert(utf8_validate(str));
|
1143
|
-
|
1140
|
+
|
1144
1141
|
/*
|
1145
1142
|
* 14 bytes is enough space to write up to two
|
1146
1143
|
* \uXXXX escapes and two quotation marks.
|
1147
1144
|
*/
|
1148
1145
|
sb_need(out, 14);
|
1149
1146
|
b = out->cur;
|
1150
|
-
|
1147
|
+
|
1151
1148
|
*b++ = '"';
|
1152
1149
|
while (*s != 0) {
|
1153
1150
|
unsigned char c = *s++;
|
1154
|
-
|
1151
|
+
|
1155
1152
|
/* Encode the next character, and write it to b. */
|
1156
1153
|
switch (c) {
|
1157
1154
|
case '"':
|
@@ -1184,10 +1181,10 @@ void emit_string(SB *out, const char *str)
|
|
1184
1181
|
break;
|
1185
1182
|
default: {
|
1186
1183
|
int len;
|
1187
|
-
|
1184
|
+
|
1188
1185
|
s--;
|
1189
1186
|
len = utf8_validate_cz(s);
|
1190
|
-
|
1187
|
+
|
1191
1188
|
if (len == 0) {
|
1192
1189
|
/*
|
1193
1190
|
* Handle invalid UTF-8 character gracefully in production
|
@@ -1210,9 +1207,9 @@ void emit_string(SB *out, const char *str)
|
|
1210
1207
|
} else if (c < 0x1F || (c >= 0x80 && escape_unicode)) {
|
1211
1208
|
/* Encode using \u.... */
|
1212
1209
|
uint32_t unicode;
|
1213
|
-
|
1210
|
+
|
1214
1211
|
s += utf8_read_char(s, &unicode);
|
1215
|
-
|
1212
|
+
|
1216
1213
|
if (unicode <= 0xFFFF) {
|
1217
1214
|
*b++ = '\\';
|
1218
1215
|
*b++ = 'u';
|
@@ -1234,11 +1231,11 @@ void emit_string(SB *out, const char *str)
|
|
1234
1231
|
while (len--)
|
1235
1232
|
*b++ = *s++;
|
1236
1233
|
}
|
1237
|
-
|
1234
|
+
|
1238
1235
|
break;
|
1239
1236
|
}
|
1240
1237
|
}
|
1241
|
-
|
1238
|
+
|
1242
1239
|
/*
|
1243
1240
|
* Update *out to know about the new bytes,
|
1244
1241
|
* and set up b to write another encoded character.
|
@@ -1248,7 +1245,7 @@ void emit_string(SB *out, const char *str)
|
|
1248
1245
|
b = out->cur;
|
1249
1246
|
}
|
1250
1247
|
*b++ = '"';
|
1251
|
-
|
1248
|
+
|
1252
1249
|
out->cur = b;
|
1253
1250
|
}
|
1254
1251
|
|
@@ -1262,7 +1259,7 @@ static void emit_number(SB *out, double num)
|
|
1262
1259
|
*/
|
1263
1260
|
char buf[64];
|
1264
1261
|
sprintf(buf, "%.16g", num);
|
1265
|
-
|
1262
|
+
|
1266
1263
|
if (number_is_valid(buf))
|
1267
1264
|
sb_puts(out, buf);
|
1268
1265
|
else
|
@@ -1282,11 +1279,11 @@ static bool number_is_valid(const char *num)
|
|
1282
1279
|
static bool expect_literal(const char **sp, const char *str)
|
1283
1280
|
{
|
1284
1281
|
const char *s = *sp;
|
1285
|
-
|
1282
|
+
|
1286
1283
|
while (*str != '\0')
|
1287
1284
|
if (*s++ != *str++)
|
1288
1285
|
return false;
|
1289
|
-
|
1286
|
+
|
1290
1287
|
*sp = s;
|
1291
1288
|
return true;
|
1292
1289
|
}
|
@@ -1317,7 +1314,7 @@ static bool parse_hex16(const char **sp, uint16_t *out)
|
|
1317
1314
|
ret <<= 4;
|
1318
1315
|
ret += tmp;
|
1319
1316
|
}
|
1320
|
-
|
1317
|
+
|
1321
1318
|
if (out)
|
1322
1319
|
*out = ret;
|
1323
1320
|
*sp = s;
|
@@ -1331,12 +1328,12 @@ static bool parse_hex16(const char **sp, uint16_t *out)
|
|
1331
1328
|
static int write_hex16(char *out, uint16_t val)
|
1332
1329
|
{
|
1333
1330
|
const char *hex = "0123456789ABCDEF";
|
1334
|
-
|
1331
|
+
|
1335
1332
|
*out++ = hex[(val >> 12) & 0xF];
|
1336
1333
|
*out++ = hex[(val >> 8) & 0xF];
|
1337
1334
|
*out++ = hex[(val >> 4) & 0xF];
|
1338
1335
|
*out++ = hex[ val & 0xF];
|
1339
|
-
|
1336
|
+
|
1340
1337
|
return 4;
|
1341
1338
|
}
|
1342
1339
|
|
@@ -1347,13 +1344,13 @@ bool json_check(const JsonNode *node, char errmsg[256])
|
|
1347
1344
|
snprintf(errmsg, 256, __VA_ARGS__); \
|
1348
1345
|
return false; \
|
1349
1346
|
} while (0)
|
1350
|
-
|
1347
|
+
|
1351
1348
|
if (node->key != NULL && !utf8_validate(node->key))
|
1352
1349
|
problem("key contains invalid UTF-8");
|
1353
|
-
|
1350
|
+
|
1354
1351
|
if (!tag_is_valid(node->tag))
|
1355
1352
|
problem("tag is invalid (%u)", node->tag);
|
1356
|
-
|
1353
|
+
|
1357
1354
|
if (node->tag == JSON_BOOL) {
|
1358
1355
|
if (node->bool_ != false && node->bool_ != true)
|
1359
1356
|
problem("bool_ is neither false (%d) nor true (%d)", (int)false, (int)true);
|
@@ -1365,7 +1362,7 @@ bool json_check(const JsonNode *node, char errmsg[256])
|
|
1365
1362
|
} else if (node->tag == JSON_ARRAY || node->tag == JSON_OBJECT) {
|
1366
1363
|
JsonNode *head = node->children.head;
|
1367
1364
|
JsonNode *tail = node->children.tail;
|
1368
|
-
|
1365
|
+
|
1369
1366
|
if (head == NULL || tail == NULL) {
|
1370
1367
|
if (head != NULL)
|
1371
1368
|
problem("tail is NULL, but head is not");
|
@@ -1374,10 +1371,10 @@ bool json_check(const JsonNode *node, char errmsg[256])
|
|
1374
1371
|
} else {
|
1375
1372
|
JsonNode *child;
|
1376
1373
|
JsonNode *last = NULL;
|
1377
|
-
|
1374
|
+
|
1378
1375
|
if (head->prev != NULL)
|
1379
1376
|
problem("First child's prev pointer is not NULL");
|
1380
|
-
|
1377
|
+
|
1381
1378
|
for (child = head; child != NULL; last = child, child = child->next) {
|
1382
1379
|
if (child == node)
|
1383
1380
|
problem("node is its own child");
|
@@ -1385,27 +1382,27 @@ bool json_check(const JsonNode *node, char errmsg[256])
|
|
1385
1382
|
problem("child->next == child (cycle)");
|
1386
1383
|
if (child->next == head)
|
1387
1384
|
problem("child->next == head (cycle)");
|
1388
|
-
|
1385
|
+
|
1389
1386
|
if (child->parent != node)
|
1390
1387
|
problem("child does not point back to parent");
|
1391
1388
|
if (child->next != NULL && child->next->prev != child)
|
1392
1389
|
problem("child->next does not point back to child");
|
1393
|
-
|
1390
|
+
|
1394
1391
|
if (node->tag == JSON_ARRAY && child->key != NULL)
|
1395
1392
|
problem("Array element's key is not NULL");
|
1396
1393
|
if (node->tag == JSON_OBJECT && child->key == NULL)
|
1397
1394
|
problem("Object member's key is NULL");
|
1398
|
-
|
1395
|
+
|
1399
1396
|
if (!json_check(child, errmsg))
|
1400
1397
|
return false;
|
1401
1398
|
}
|
1402
|
-
|
1399
|
+
|
1403
1400
|
if (last != tail)
|
1404
1401
|
problem("tail does not match pointer found by starting at head and following next links");
|
1405
1402
|
}
|
1406
1403
|
}
|
1407
|
-
|
1404
|
+
|
1408
1405
|
return true;
|
1409
|
-
|
1406
|
+
|
1410
1407
|
#undef problem
|
1411
1408
|
}
|