1
0
mirror of https://github.com/samba-team/samba.git synced 2025-10-30 08:23:49 +03:00
Files
samba-mirror/source/scripting/ejs/literal.c

671 lines
31 KiB
C

/*
* Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd.
* Michael Clark <michael@metaparadigm.com>
* Copyright (c) 2006 Derrell Lipman
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See COPYING for details.
*
* Derrell Lipman:
* This version is modified from the original. It has been modified to
* natively use EJS variables rather than the original C object interface, and
* to use the talloc() family of functions for memory allocation.
*/
#include "includes.h"
#include "scripting/ejs/smbcalls.h"
enum json_tokener_error {
json_tokener_success,
json_tokener_error_oom, /* out of memory */
json_tokener_error_parse_unexpected,
json_tokener_error_parse_null,
json_tokener_error_parse_boolean,
json_tokener_error_parse_number,
json_tokener_error_parse_array,
json_tokener_error_parse_object,
json_tokener_error_parse_string,
json_tokener_error_parse_comment,
json_tokener_error_parse_eof
};
enum json_tokener_state {
json_tokener_state_eatws, /* 0 */
json_tokener_state_start, /* 1 */
json_tokener_state_finish, /* 2 */
json_tokener_state_null, /* 3 */
json_tokener_state_comment_start, /* 4 */
json_tokener_state_comment, /* 5 */
json_tokener_state_comment_eol, /* 6 */
json_tokener_state_comment_end, /* 7 */
json_tokener_state_string, /* 8 */
json_tokener_state_string_escape, /* 9 */
json_tokener_state_escape_unicode, /* 10 */
json_tokener_state_boolean, /* 11 */
json_tokener_state_number, /* 12 */
json_tokener_state_array, /* 13 */
json_tokener_state_array_sep, /* 14 */
json_tokener_state_object, /* 15 */
json_tokener_state_object_field_start, /* 16 */
json_tokener_state_object_field, /* 17 */
json_tokener_state_object_field_end, /* 18 */
json_tokener_state_object_value, /* 19 */
json_tokener_state_object_sep /* 20 */
};
struct json_tokener
{
char *source;
int pos;
void *ctx;
void *pb;
};
static const char *json_number_chars = "0123456789.+-e";
static const char *json_hex_chars = "0123456789abcdef";
#define hexdigit(x) (((x) <= '9') ? (x) - '0' : ((x) & 7) + 9)
extern struct MprVar json_tokener_parse(char *s);
static struct MprVar json_tokener_do_parse(struct json_tokener *this,
enum json_tokener_error *err_p);
/*
* literal_to_var() parses a string into an ejs variable. The ejs
* variable is returned. Upon error, the javascript variable will be
* `undefined`. This was created for parsing JSON, but is generally useful
* for parsing the literal forms of objects and arrays, since ejs doesn't
* procide that functionality.
*/
int literal_to_var(int eid, int argc, char **argv)
{
struct json_tokener tok;
struct MprVar obj;
enum json_tokener_error err = json_tokener_success;
if (argc != 1) {
ejsSetErrorMsg(eid,
"literal_to_var() requires one parameter: "
"the string to be parsed.");
return -1;
}
tok.source = argv[0];
tok.pos = 0;
tok.ctx = talloc_new(mprMemCtx());
if (tok.ctx == NULL) {
mpr_Return(eid, mprCreateUndefinedVar());
return 0;
}
tok.pb = talloc_zero_size(tok.ctx, 1);
if (tok.pb == NULL) {
mpr_Return(eid, mprCreateUndefinedVar());
return 0;
}
obj = json_tokener_do_parse(&tok, &err);
talloc_free(tok.pb);
if (err != json_tokener_success) {
mprDestroyVar(&obj);
mpr_Return(eid, mprCreateUndefinedVar());
return 0;
}
mpr_Return(eid, obj);
return 0;
}
static void *append_string(void *ctx,
char *orig,
char *append,
int size)
{
char c;
char *end_p = append + size;
void *ret;
/*
* We need to null terminate the string to be copied. Save character at
* the size limit of the source string.
*/
c = *end_p;
/* Temporarily null-terminate it */
*end_p = '\0';
/* Append the requested data */
ret = talloc_append_string(ctx, orig, append);
/* Restore the original character in place of our temporary null byte */
*end_p = c;
/* Give 'em what they came for */
return ret;
}
static struct MprVar json_tokener_do_parse(struct json_tokener *this,
enum json_tokener_error *err_p)
{
enum json_tokener_state state, saved_state;
struct MprVar current = mprCreateUndefinedVar();
struct MprVar obj;
enum json_tokener_error err = json_tokener_success;
char *obj_field_name = NULL;
char quote_char;
int deemed_double;
int start_offset;
char c;
state = json_tokener_state_eatws;
saved_state = json_tokener_state_start;
do {
c = this->source[this->pos];
switch(state) {
case json_tokener_state_eatws:
if(isspace(c)) {
this->pos++;
} else if(c == '/') {
state = json_tokener_state_comment_start;
start_offset = this->pos++;
} else {
state = saved_state;
}
break;
case json_tokener_state_start:
switch(c) {
case '{':
state = json_tokener_state_eatws;
saved_state = json_tokener_state_object;
current = mprObject(NULL);
this->pos++;
break;
case '[':
state = json_tokener_state_eatws;
saved_state = json_tokener_state_array;
current = mprArray(NULL);
this->pos++;
break;
case 'N':
case 'n':
state = json_tokener_state_null;
start_offset = this->pos++;
break;
case '"':
case '\'':
quote_char = c;
talloc_free(this->pb);
this->pb = talloc_zero_size(this->ctx, 1);
if (this->pb == NULL) {
*err_p = json_tokener_error_oom;
goto out;
}
state = json_tokener_state_string;
start_offset = ++this->pos;
break;
case 'T':
case 't':
case 'F':
case 'f':
state = json_tokener_state_boolean;
start_offset = this->pos++;
break;
#if defined(__GNUC__)
case '0' ... '9':
#else
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
#endif
case '-':
deemed_double = 0;
state = json_tokener_state_number;
start_offset = this->pos++;
break;
default:
err = json_tokener_error_parse_unexpected;
goto out;
}
break;
case json_tokener_state_finish:
goto out;
case json_tokener_state_null:
if(strncasecmp("null", this->source + start_offset,
this->pos - start_offset)) {
*err_p = json_tokener_error_parse_null;
mprDestroyVar(&current);
return mprCreateUndefinedVar();
}
if(this->pos - start_offset == 4) {
mprDestroyVar(&current);
current = mprCreateNullVar();
saved_state = json_tokener_state_finish;
state = json_tokener_state_eatws;
} else {
this->pos++;
}
break;
case json_tokener_state_comment_start:
if(c == '*') {
state = json_tokener_state_comment;
} else if(c == '/') {
state = json_tokener_state_comment_eol;
} else {
err = json_tokener_error_parse_comment;
goto out;
}
this->pos++;
break;
case json_tokener_state_comment:
if(c == '*') state = json_tokener_state_comment_end;
this->pos++;
break;
case json_tokener_state_comment_eol:
if(c == '\n') {
state = json_tokener_state_eatws;
}
this->pos++;
break;
case json_tokener_state_comment_end:
if(c == '/') {
state = json_tokener_state_eatws;
} else {
state = json_tokener_state_comment;
}
this->pos++;
break;
case json_tokener_state_string:
if(c == quote_char) {
if ((this->pb = append_string(
this->ctx,
this->pb,
this->source + start_offset,
this->pos - start_offset)) == NULL) {
err = json_tokener_error_oom;
goto out;
}
current = mprString(this->pb);
saved_state = json_tokener_state_finish;
state = json_tokener_state_eatws;
} else if(c == '\\') {
saved_state = json_tokener_state_string;
state = json_tokener_state_string_escape;
}
this->pos++;
break;
case json_tokener_state_string_escape:
switch(c) {
case '"':
case '\\':
if ((this->pb = append_string(
this->ctx,
this->pb,
this->source + start_offset,
this->pos - start_offset - 1)) == NULL) {
err = json_tokener_error_oom;
goto out;
}
start_offset = this->pos++;
state = saved_state;
break;
case 'b':
case 'n':
case 'r':
case 't':
if ((this->pb = append_string(
this->ctx,
this->pb,
this->source + start_offset,
this->pos - start_offset - 1)) == NULL) {
err = json_tokener_error_oom;
goto out;
}
if (c == 'b') {
/*
* second param to append_string()
* gets temporarily modified; can't
* pass string constant.
*/
char buf[] = "\b";
if ((this->pb = append_string(
this->ctx,
this->pb,
buf,
1)) == NULL) {
err = json_tokener_error_oom;
goto out;
}
} else if (c == 'n') {
char buf[] = "\n";
if ((this->pb = append_string(
this->ctx,
this->pb,
buf,
1)) == NULL) {
err = json_tokener_error_oom;
goto out;
}
} else if (c == 'r') {
char buf[] = "\r";
if ((this->pb = append_string(
this->ctx,
this->pb,
buf,
1)) == NULL) {
err = json_tokener_error_oom;
goto out;
}
} else if (c == 't') {
char buf[] = "\t";
if ((this->pb = append_string(
this->ctx,
this->pb,
buf,
1)) == NULL) {
err = json_tokener_error_oom;
goto out;
}
}
start_offset = ++this->pos;
state = saved_state;
break;
case 'u':
if ((this->pb = append_string(
this->ctx,
this->pb,
this->source + start_offset,
this->pos - start_offset - 1)) == NULL) {
err = json_tokener_error_oom;
goto out;
}
start_offset = ++this->pos;
state = json_tokener_state_escape_unicode;
break;
default:
err = json_tokener_error_parse_string;
goto out;
}
break;
case json_tokener_state_escape_unicode:
if(strchr(json_hex_chars, c)) {
this->pos++;
if(this->pos - start_offset == 4) {
unsigned char utf_out[3];
unsigned int ucs_char =
(hexdigit(*(this->source + start_offset)) << 12) +
(hexdigit(*(this->source + start_offset + 1)) << 8) +
(hexdigit(*(this->source + start_offset + 2)) << 4) +
hexdigit(*(this->source + start_offset + 3));
if (ucs_char < 0x80) {
utf_out[0] = ucs_char;
if ((this->pb = append_string(
this->ctx,
this->pb,
(char *) utf_out,
1)) == NULL) {
err = json_tokener_error_oom;
goto out;
}
} else if (ucs_char < 0x800) {
utf_out[0] = 0xc0 | (ucs_char >> 6);
utf_out[1] = 0x80 | (ucs_char & 0x3f);
if ((this->pb = append_string(
this->ctx,
this->pb,
(char *) utf_out,
2)) == NULL) {
err = json_tokener_error_oom;
goto out;
}
} else {
utf_out[0] = 0xe0 | (ucs_char >> 12);
utf_out[1] = 0x80 | ((ucs_char >> 6) & 0x3f);
utf_out[2] = 0x80 | (ucs_char & 0x3f);
if ((this->pb = append_string(
this->ctx,
this->pb,
(char *) utf_out,
3)) == NULL) {
err = json_tokener_error_oom;
goto out;
}
}
start_offset = this->pos;
state = saved_state;
}
} else {
err = json_tokener_error_parse_string;
goto out;
}
break;
case json_tokener_state_boolean:
if(strncasecmp("true", this->source + start_offset,
this->pos - start_offset) == 0) {
if(this->pos - start_offset == 4) {
current = mprCreateBoolVar(1);
saved_state = json_tokener_state_finish;
state = json_tokener_state_eatws;
} else {
this->pos++;
}
} else if(strncasecmp("false", this->source + start_offset,
this->pos - start_offset) == 0) {
if(this->pos - start_offset == 5) {
current = mprCreateBoolVar(0);
saved_state = json_tokener_state_finish;
state = json_tokener_state_eatws;
} else {
this->pos++;
}
} else {
err = json_tokener_error_parse_boolean;
goto out;
}
break;
case json_tokener_state_number:
if(!c || !strchr(json_number_chars, c)) {
int numi;
double numd;
char *tmp = talloc_strndup(
this->ctx,
this->source + start_offset,
this->pos - start_offset);
if (tmp == NULL) {
err = json_tokener_error_oom;
goto out;
}
if(!deemed_double && sscanf(tmp, "%d", &numi) == 1) {
current = mprCreateIntegerVar(numi);
} else if(deemed_double && sscanf(tmp, "%lf", &numd) == 1) {
current = mprCreateFloatVar(numd);
} else {
talloc_free(tmp);
err = json_tokener_error_parse_number;
goto out;
}
talloc_free(tmp);
saved_state = json_tokener_state_finish;
state = json_tokener_state_eatws;
} else {
if(c == '.' || c == 'e') deemed_double = 1;
this->pos++;
}
break;
case json_tokener_state_array:
if(c == ']') {
this->pos++;
saved_state = json_tokener_state_finish;
state = json_tokener_state_eatws;
} else {
int oldlen;
char idx[16];
obj = json_tokener_do_parse(this, &err);
if (err != json_tokener_success) {
goto out;
}
oldlen = mprToInt(mprGetProperty(&current,
"length",
NULL));
mprItoa(oldlen, idx, sizeof(idx));
mprSetVar(&current, idx, obj);
saved_state = json_tokener_state_array_sep;
state = json_tokener_state_eatws;
}
break;
case json_tokener_state_array_sep:
if(c == ']') {
this->pos++;
saved_state = json_tokener_state_finish;
state = json_tokener_state_eatws;
} else if(c == ',') {
this->pos++;
saved_state = json_tokener_state_array;
state = json_tokener_state_eatws;
} else {
*err_p = json_tokener_error_parse_array;
mprDestroyVar(&current);
return mprCreateUndefinedVar();
}
break;
case json_tokener_state_object:
state = json_tokener_state_object_field_start;
start_offset = this->pos;
break;
case json_tokener_state_object_field_start:
if(c == '}') {
this->pos++;
saved_state = json_tokener_state_finish;
state = json_tokener_state_eatws;
} else if (c == '"' || c == '\'') {
quote_char = c;
talloc_free(this->pb);
this->pb = talloc_zero_size(this->ctx, 1);
if (this->pb == NULL) {
*err_p = json_tokener_error_oom;
goto out;
}
state = json_tokener_state_object_field;
start_offset = ++this->pos;
} else {
err = json_tokener_error_parse_object;
goto out;
}
break;
case json_tokener_state_object_field:
if(c == quote_char) {
if ((this->pb = append_string(
this->ctx,
this->pb,
this->source + start_offset,
this->pos - start_offset)) == NULL) {
err = json_tokener_error_oom;
goto out;
}
if ((obj_field_name =
talloc_strdup(this->ctx,
this->pb)) == NULL) {
err = json_tokener_error_oom;
goto out;
}
saved_state = json_tokener_state_object_field_end;
state = json_tokener_state_eatws;
} else if(c == '\\') {
saved_state = json_tokener_state_object_field;
state = json_tokener_state_string_escape;
}
this->pos++;
break;
case json_tokener_state_object_field_end:
if(c == ':') {
this->pos++;
saved_state = json_tokener_state_object_value;
state = json_tokener_state_eatws;
} else {
*err_p = json_tokener_error_parse_object;
mprDestroyVar(&current);
return mprCreateUndefinedVar();
}
break;
case json_tokener_state_object_value:
obj = json_tokener_do_parse(this, &err);
if (err != json_tokener_success) {
goto out;
}
mprSetVar(&current, obj_field_name, obj);
talloc_free(obj_field_name);
obj_field_name = NULL;
saved_state = json_tokener_state_object_sep;
state = json_tokener_state_eatws;
break;
case json_tokener_state_object_sep:
if(c == '}') {
this->pos++;
saved_state = json_tokener_state_finish;
state = json_tokener_state_eatws;
} else if(c == ',') {
this->pos++;
saved_state = json_tokener_state_object;
state = json_tokener_state_eatws;
} else {
err = json_tokener_error_parse_object;
goto out;
}
break;
}
} while(c);
if(state != json_tokener_state_finish &&
saved_state != json_tokener_state_finish)
err = json_tokener_error_parse_eof;
out:
talloc_free(obj_field_name);
if(err == json_tokener_success) {
return current;
}
mprDestroyVar(&current);
*err_p = err;
return mprCreateUndefinedVar();
}
void smb_setup_ejs_literal(void)
{
ejsDefineStringCFunction(-1,
"literal_to_var",
literal_to_var,
NULL,
MPR_VAR_SCRIPT_HANDLE);
}