Merge remote-tracking branch 'vim/master'

This commit is contained in:
Kazuki Sakamoto
2016-02-02 14:16:00 -08:00
16 changed files with 845 additions and 416 deletions
+1
View File
@@ -41,6 +41,7 @@ SRC_ALL = \
src/hardcopy.c \
src/hashtab.c \
src/json.c \
src/json_test.c \
src/keymap.h \
src/macros.h \
src/main.c \
+29 -25
View File
@@ -1,4 +1,4 @@
*eval.txt* For Vim version 7.4. Last change: 2016 Feb 01
*eval.txt* For Vim version 7.4. Last change: 2016 Feb 02
VIM REFERENCE MANUAL by Bram Moolenaar
@@ -1810,6 +1810,13 @@ byteidxcomp( {expr}, {nr}) Number byte index of {nr}'th char in {expr}
call( {func}, {arglist} [, {dict}])
any call {func} with arguments {arglist}
ceil( {expr}) Float round {expr} up
ch_close( {handle}) none close a channel
ch_open( {address}, {mode} [, {callback}])
Number open a channel
ch_sendexpr( {handle}, {expr} [, {callback}])
any send {expr} over JSON channel {handle}
ch_sendraw( {handle}, {string} [, {callback}])
any send {string} over raw channel {handle}
changenr() Number current change number
char2nr( {expr}[, {utf8}]) Number ASCII/UTF8 value of first char in {expr}
cindent( {lnum}) Number C indent for line {lnum}
@@ -1820,8 +1827,6 @@ complete_add( {expr}) Number add completion match
complete_check() Number check for key typed during completion
confirm( {msg} [, {choices} [, {default} [, {type}]]])
Number number of choice picked by user
connect( {address}, {mode} [, {callback}])
Number open a channel
copy( {expr}) any make a shallow copy of {expr}
cos( {expr}) Float cosine of {expr}
cosh( {expr}) Float hyperbolic cosine of {expr}
@@ -2029,10 +2034,6 @@ searchpairpos( {start}, {middle}, {end} [, {flags} [, {skip} [...]]])
List search for other end of start/end pair
searchpos( {pattern} [, {flags} [, {stopline} [, {timeout}]]])
List search for {pattern}
sendexpr( {handle}, {expr} [, {callback}])
any send {expr} over JSON channel {handle}
sendraw( {handle}, {string} [, {callback}])
any send {string} over raw channel {handle}
server2client( {clientid}, {string})
Number send reply string
serverlist() String get a list of available servers
@@ -2666,7 +2667,10 @@ confirm({msg} [, {choices} [, {default} [, {type}]]])
don't fit, a vertical layout is used anyway. For some systems
the horizontal layout is always used.
connect({address}, {mode} [, {callback}]) *connect()*
ch_close({handle}) *ch_close()*
Close channel {handle}. See |channel|.
ch_open({address}, {mode} [, {callback}]) *ch_open()*
Open a channel to {address}. See |channel|.
Returns the channel handle on success. Returns a negative
number for failure.
@@ -2680,6 +2684,23 @@ connect({address}, {mode} [, {callback}]) *connect()*
{callback} is a function that handles received messages on the
channel. See |channel-callback|.
ch_sendexpr({handle}, {expr} [, {callback}]) ch_*sendexpr()*
Send {expr} over JSON channel {handle}. See |channel-use|.
When {callback} is given returns immediately. Without
{callback} waits for a JSON response and returns the decoded
expression. When there is an error or timeout returns an
empty string.
When {callback} is zero no response is expected.
Otherwise {callback} must be a Funcref or the name of a
function. It is called when the response is received. See
|channel-callback|.
ch_sendraw({handle}, {string} [, {callback}]) *ch_sendraw()*
Send {string} over raw channel {handle}. See |channel-raw|.
Works like |ch_sendexpr()|, but does not decode the response.
*copy()*
copy({expr}) Make a copy of {expr}. For Numbers and Strings this isn't
different from using {expr} directly.
@@ -5615,23 +5636,6 @@ searchpos({pattern} [, {flags} [, {stopline} [, {timeout}]]]) *searchpos()*
< In this example "submatch" is 2 when a lowercase letter is
found |/\l|, 3 when an uppercase letter is found |/\u|.
sendexpr({handle}, {expr} [, {callback}]) *sendexpr()*
Send {expr} over JSON channel {handle}. See |channel-use|.
When {callback} is given returns immediately. Without
{callback} waits for a JSON response and returns the decoded
expression. When there is an error or timeout returns an
empty string.
When {callback} is zero no response is expected.
Otherwise {callback} must be a Funcref or the name of a
function. It is called when the response is received. See
|channel-callback|.
sendraw({handle}, {string} [, {callback}]) *sendraw()*
Send {string} over raw channel {handle}. See |channel-raw|.
Works like |sendexpr()|, but does not decode the response.
server2client( {clientid}, {string}) *server2client()*
Send a reply string to {clientid}. The most recent {clientid}
that sent a string can be retrieved with expand("<client>").
+8 -2
View File
@@ -1,15 +1,21 @@
#!/usr/bin/python
#
# Server that will accept connections from a Vim channel.
# Run this server and then in Vim you can open the channel:
# :let handle = connect('localhost:8765', 'json')
# :let handle = ch_open('localhost:8765', 'json')
#
# Then Vim can send requests to the server:
# :let response = sendexpr(handle, 'hello!')
# :let response = ch_sendexpr(handle, 'hello!')
#
# And you can control Vim by typing a JSON message here, e.g.:
# ["ex","echo 'hi there'"]
#
# There is no prompt, just type a line and press Enter.
# To exit cleanly type "quit<Enter>".
#
# See ":help channel-demo" in Vim.
#
# This requires Python 2.6 or later.
from __future__ import print_function
import json
+1 -1
View File
@@ -52,7 +52,7 @@ gvimext.obj: gvimext.h
$(cc) $(cflags) -DFEAT_GETTEXT $(cvarsmt) $*.cpp
gvimext.res: gvimext.rc
$(rc) $(rcflags) $(rcvars) gvimext.rc
$(rc) /nologo $(rcflags) $(rcvars) gvimext.rc
clean:
- if exist gvimext.dll del gvimext.dll
+1 -1
View File
@@ -1294,7 +1294,7 @@ $(OUTDIR)/xpm_w32.obj: $(OUTDIR) xpm_w32.c
$(OUTDIR)/vim.res: $(OUTDIR) vim.rc gvim.exe.mnf version.h tools.bmp \
tearoff.bmp vim.ico vim_error.ico \
vim_alert.ico vim_info.ico vim_quest.ico
$(RC) /l 0x409 /Fo$(OUTDIR)/vim.res $(RCFLAGS) vim.rc
$(RC) /nologo /l 0x409 /Fo$(OUTDIR)/vim.res $(RCFLAGS) vim.rc
iid_ole.c if_ole.h vim.tlb: if_ole.idl
midl /nologo /error none /proxy nul /iid iid_ole.c /tlb vim.tlb \
+33 -5
View File
@@ -1562,11 +1562,13 @@ EXTRA_SRC = hangulin.c if_lua.c if_mzsch.c auto/if_perl.c if_perlsfio.c \
$(GRESOURCE_SRC)
# Unittest files
JSON_TEST_SRC = json_test.c
JSON_TEST_TARGET = json_test$(EXEEXT)
MEMFILE_TEST_SRC = memfile_test.c
MEMFILE_TEST_TARGET = memfile_test$(EXEEXT)
UNITTEST_SRC = $(MEMFILE_TEST_SRC)
UNITTEST_TARGETS = $(MEMFILE_TEST_TARGET)
UNITTEST_SRC = $(JSON_TEST_SRC) $(MEMFILE_TEST_SRC)
UNITTEST_TARGETS = $(JSON_TEST_TARGET) $(MEMFILE_TEST_TARGET)
# All sources, also the ones that are not configured
ALL_SRC = $(BASIC_SRC) $(ALL_GUI_SRC) $(UNITTEST_SRC) $(EXTRA_SRC)
@@ -1605,9 +1607,8 @@ OBJ_COMMON = \
$(HANGULIN_OBJ) \
objects/if_cscope.o \
objects/if_xcmdsrv.o \
objects/json.o \
objects/mark.o \
objects/memline.o \
objects/memline.o \
objects/menu.o \
objects/message.o \
objects/misc1.o \
@@ -1649,11 +1650,17 @@ OBJ_COMMON = \
$(WSDEBUG_OBJ)
OBJ = $(OBJ_COMMON) \
objects/json.o \
objects/main.o \
objects/memfile.o
JSON_TEST_OBJ = $(OBJ_COMMON) \
objects/json_test.o \
objects/memfile.o
MEMFILE_TEST_OBJ = $(OBJ_COMMON) \
objects/memfile_test.o
objects/json.o \
objects/memfile_test.o
PRO_AUTO = \
blowfish.pro \
@@ -1931,6 +1938,7 @@ types.vim: $(TAGS_SRC) $(TAGS_INCL)
ctags --c-kinds=gstu -o- $(TAGS_SRC) $(TAGS_INCL) |\
awk 'BEGIN{printf("syntax keyword Type\t")}\
{printf("%s ", $$1)}END{print ""}' > $@
echo "syn keyword Constant OK FAIL TRUE FALSE MAYBE" >> $@
# Execute the test scripts. Run these after compiling Vim, before installing.
# This doesn't depend on $(VIMTARGET), because that won't work when configure
@@ -1965,6 +1973,12 @@ unittest unittests: $(UNITTEST_TARGETS)
./$$t || exit 1; echo $$t passed; \
done
run_json_test: $(JSON_TEST_TARGET)
./$(JSON_TEST_TARGET)
run_memfile_test: $(MEMFILE_TEST_TARGET)
./$(MEMFILE_TEST_TARGET)
# Run individual OLD style test, assuming that Vim was already compiled.
test1 \
test_autocmd_option \
@@ -2057,6 +2071,13 @@ testclean:
# Unittests
# It's build just like Vim to satisfy all dependencies.
$(JSON_TEST_TARGET): auto/config.mk objects $(JSON_TEST_OBJ)
$(CCC) version.c -o objects/version.o
@LINK="$(PURIFY) $(SHRPENV) $(CClink) $(ALL_LIB_DIRS) $(LDFLAGS) \
-o $(JSON_TEST_TARGET) $(JSON_TEST_OBJ) $(ALL_LIBS)" \
MAKE="$(MAKE)" LINK_AS_NEEDED=$(LINK_AS_NEEDED) \
sh $(srcdir)/link.sh
$(MEMFILE_TEST_TARGET): auto/config.mk objects $(MEMFILE_TEST_OBJ)
$(CCC) version.c -o objects/version.o
@LINK="$(PURIFY) $(SHRPENV) $(CClink) $(ALL_LIB_DIRS) $(LDFLAGS) \
@@ -2837,6 +2858,9 @@ objects/integration.o: integration.c
objects/json.o: json.c
$(CCC) -o $@ json.c
objects/json_test.o: json_test.c
$(CCC) -o $@ json_test.c
objects/main.o: main.c
$(CCC) -o $@ main.c
@@ -3360,6 +3384,10 @@ objects/gui_at_fs.o: gui_at_fs.c vim.h auto/config.h feature.h os_unix.h \
objects/pty.o: pty.c vim.h auto/config.h feature.h os_unix.h auto/osdef.h ascii.h \
keymap.h term.h macros.h option.h structs.h regexp.h gui.h gui_beval.h \
proto/gui_beval.pro alloc.h ex_cmds.h proto.h globals.h farsi.h arabic.h
objects/json_test.o: json_test.c main.c vim.h auto/config.h feature.h os_unix.h \
auto/osdef.h ascii.h keymap.h term.h macros.h option.h structs.h \
regexp.h gui.h gui_beval.h proto/gui_beval.pro alloc.h ex_cmds.h proto.h \
globals.h farsi.h arabic.h farsi.c arabic.c json.c
objects/memfile_test.o: memfile_test.c main.c vim.h auto/config.h feature.h \
os_unix.h auto/osdef.h ascii.h keymap.h term.h macros.h option.h \
structs.h regexp.h gui.h gui_beval.h proto/gui_beval.pro alloc.h \
+21 -11
View File
@@ -565,9 +565,8 @@ channel_read_json(int ch_idx)
/* TODO: make reader work properly */
/* reader.js_buf = channel_peek(ch_idx); */
reader.js_buf = channel_get_all(ch_idx);
reader.js_eof = TRUE;
/* reader.js_eof = FALSE; */
reader.js_used = 0;
reader.js_fill = NULL;
/* reader.js_fill = channel_fill; */
reader.js_cookie = &ch_idx;
if (json_decode(&reader, &listtv) == OK)
@@ -593,6 +592,13 @@ channel_read_json(int ch_idx)
}
}
}
/* Put the unread part back into the channel.
* TODO: insert in front */
if (reader.js_buf[reader.js_used] != NUL)
channel_save(ch_idx, reader.js_buf + reader.js_used,
(int)(reader.js_end - reader.js_buf) - reader.js_used);
vim_free(reader.js_buf);
}
/*
@@ -723,8 +729,9 @@ channel_exe_cmd(int idx, char_u *cmd, typval_T *arg2, typval_T *arg3)
/*
* Invoke a callback for channel "idx" if needed.
* Return OK when a message was handled, there might be another one.
*/
static void
static int
may_invoke_callback(int idx)
{
char_u *msg = NULL;
@@ -736,22 +743,22 @@ may_invoke_callback(int idx)
int json_mode = channels[idx].ch_json_mode;
if (channel_peek(idx) == NULL)
return;
return FALSE;
if (channels[idx].ch_close_cb != NULL)
/* this channel is handled elsewhere (netbeans) */
return;
return FALSE;
if (json_mode)
{
/* Get any json message. Return if there isn't one. */
channel_read_json(idx);
if (channel_get_json(idx, -1, &listtv) == FAIL)
return;
return FALSE;
if (listtv->v_type != VAR_LIST)
{
/* TODO: give error */
clear_tv(listtv);
return;
return FALSE;
}
list = listtv->vval.v_list;
@@ -759,7 +766,7 @@ may_invoke_callback(int idx)
{
/* TODO: give error */
clear_tv(listtv);
return;
return FALSE;
}
argv[1] = list->lv_first->li_next->li_tv;
@@ -774,14 +781,14 @@ may_invoke_callback(int idx)
arg3 = &list->lv_last->li_tv;
channel_exe_cmd(idx, cmd, &argv[1], arg3);
clear_tv(listtv);
return;
return TRUE;
}
if (typetv->v_type != VAR_NUMBER)
{
/* TODO: give error */
clear_tv(listtv);
return;
return FALSE;
}
seq_nr = typetv->vval.v_number;
}
@@ -811,6 +818,8 @@ may_invoke_callback(int idx)
if (listtv != NULL)
clear_tv(listtv);
vim_free(msg);
return TRUE;
}
/*
@@ -1270,7 +1279,8 @@ channel_parse_messages(void)
int i;
for (i = 0; i < channel_count; ++i)
may_invoke_callback(i);
while (may_invoke_callback(i) == OK)
;
}
#endif /* FEAT_CHANNEL */
+221 -234
View File
@@ -499,6 +499,12 @@ static void f_call(typval_T *argvars, typval_T *rettv);
#ifdef FEAT_FLOAT
static void f_ceil(typval_T *argvars, typval_T *rettv);
#endif
#ifdef FEAT_CHANNEL
static void f_ch_open(typval_T *argvars, typval_T *rettv);
static void f_ch_close(typval_T *argvars, typval_T *rettv);
static void f_ch_sendexpr(typval_T *argvars, typval_T *rettv);
static void f_ch_sendraw(typval_T *argvars, typval_T *rettv);
#endif
static void f_changenr(typval_T *argvars, typval_T *rettv);
static void f_char2nr(typval_T *argvars, typval_T *rettv);
static void f_cindent(typval_T *argvars, typval_T *rettv);
@@ -515,9 +521,6 @@ static void f_copy(typval_T *argvars, typval_T *rettv);
static void f_cos(typval_T *argvars, typval_T *rettv);
static void f_cosh(typval_T *argvars, typval_T *rettv);
#endif
#ifdef FEAT_CHANNEL
static void f_connect(typval_T *argvars, typval_T *rettv);
#endif
static void f_count(typval_T *argvars, typval_T *rettv);
static void f_cscope_connection(typval_T *argvars, typval_T *rettv);
static void f_cursor(typval_T *argsvars, typval_T *rettv);
@@ -526,9 +529,6 @@ static void f_delete(typval_T *argvars, typval_T *rettv);
static void f_did_filetype(typval_T *argvars, typval_T *rettv);
static void f_diff_filler(typval_T *argvars, typval_T *rettv);
static void f_diff_hlID(typval_T *argvars, typval_T *rettv);
#ifdef FEAT_CHANNEL
static void f_disconnect(typval_T *argvars, typval_T *rettv);
#endif
static void f_empty(typval_T *argvars, typval_T *rettv);
static void f_escape(typval_T *argvars, typval_T *rettv);
static void f_eval(typval_T *argvars, typval_T *rettv);
@@ -703,10 +703,6 @@ static void f_searchdecl(typval_T *argvars, typval_T *rettv);
static void f_searchpair(typval_T *argvars, typval_T *rettv);
static void f_searchpairpos(typval_T *argvars, typval_T *rettv);
static void f_searchpos(typval_T *argvars, typval_T *rettv);
#ifdef FEAT_CHANNEL
static void f_sendexpr(typval_T *argvars, typval_T *rettv);
static void f_sendraw(typval_T *argvars, typval_T *rettv);
#endif
static void f_server2client(typval_T *argvars, typval_T *rettv);
static void f_serverlist(typval_T *argvars, typval_T *rettv);
static void f_setbufvar(typval_T *argvars, typval_T *rettv);
@@ -8002,6 +7998,12 @@ static struct fst
{"call", 2, 3, f_call},
#ifdef FEAT_FLOAT
{"ceil", 1, 1, f_ceil},
#endif
#ifdef FEAT_CHANNEL
{"ch_close", 1, 1, f_ch_close},
{"ch_open", 2, 3, f_ch_open},
{"ch_sendexpr", 2, 3, f_ch_sendexpr},
{"ch_sendraw", 2, 3, f_ch_sendraw},
#endif
{"changenr", 0, 0, f_changenr},
{"char2nr", 1, 2, f_char2nr},
@@ -8014,9 +8016,6 @@ static struct fst
{"complete_check", 0, 0, f_complete_check},
#endif
{"confirm", 1, 4, f_confirm},
#ifdef FEAT_CHANNEL
{"connect", 2, 3, f_connect},
#endif
{"copy", 1, 1, f_copy},
#ifdef FEAT_FLOAT
{"cos", 1, 1, f_cos},
@@ -8030,9 +8029,6 @@ static struct fst
{"did_filetype", 0, 0, f_did_filetype},
{"diff_filler", 1, 1, f_diff_filler},
{"diff_hlID", 2, 2, f_diff_hlID},
#ifdef FEAT_CHANNEL
{"disconnect", 1, 1, f_disconnect},
#endif
{"empty", 1, 1, f_empty},
{"escape", 2, 2, f_escape},
{"eval", 1, 1, f_eval},
@@ -8211,10 +8207,6 @@ static struct fst
{"searchpair", 3, 7, f_searchpair},
{"searchpairpos", 3, 7, f_searchpairpos},
{"searchpos", 1, 4, f_searchpos},
#ifdef FEAT_CHANNEL
{"sendexpr", 2, 3, f_sendexpr},
{"sendraw", 2, 3, f_sendraw},
#endif
{"server2client", 2, 2, f_server2client},
{"serverlist", 0, 0, f_serverlist},
{"setbufvar", 3, 3, f_setbufvar},
@@ -9685,6 +9677,213 @@ f_ceil(typval_T *argvars, typval_T *rettv)
}
#endif
#ifdef FEAT_CHANNEL
/*
* Get the channel index from the handle argument.
* Returns -1 if the handle is invalid or the channel is closed.
*/
static int
get_channel_arg(typval_T *tv)
{
int ch_idx;
if (tv->v_type != VAR_NUMBER)
{
EMSG2(_(e_invarg2), get_tv_string(tv));
return -1;
}
ch_idx = tv->vval.v_number;
if (!channel_is_open(ch_idx))
{
EMSGN(_("E906: not an open channel"), ch_idx);
return -1;
}
return ch_idx;
}
/*
* "ch_close()" function
*/
static void
f_ch_close(typval_T *argvars, typval_T *rettv UNUSED)
{
int ch_idx = get_channel_arg(&argvars[0]);
if (ch_idx >= 0)
channel_close(ch_idx);
}
/*
* Get a callback from "arg". It can be a Funcref or a function name.
* When "arg" is zero return an empty string.
* Return NULL for an invalid argument.
*/
static char_u *
get_callback(typval_T *arg)
{
if (arg->v_type == VAR_FUNC || arg->v_type == VAR_STRING)
return arg->vval.v_string;
if (arg->v_type == VAR_NUMBER && arg->vval.v_number == 0)
return (char_u *)"";
EMSG(_("E999: Invalid callback argument"));
return NULL;
}
/*
* "ch_open()" function
*/
static void
f_ch_open(typval_T *argvars, typval_T *rettv)
{
char_u *address;
char_u *mode;
char_u *callback = NULL;
char_u buf1[NUMBUFLEN];
char_u *p;
int port;
int json_mode = FALSE;
/* default: fail */
rettv->vval.v_number = -1;
address = get_tv_string(&argvars[0]);
mode = get_tv_string_buf(&argvars[1], buf1);
if (argvars[2].v_type != VAR_UNKNOWN)
{
callback = get_callback(&argvars[2]);
if (callback == NULL)
return;
}
/* parse address */
p = vim_strchr(address, ':');
if (p == NULL)
{
EMSG2(_(e_invarg2), address);
return;
}
*p++ = NUL;
port = atoi((char *)p);
if (*address == NUL || port <= 0)
{
p[-1] = ':';
EMSG2(_(e_invarg2), address);
return;
}
/* parse mode */
if (STRCMP(mode, "json") == 0)
json_mode = TRUE;
else if (STRCMP(mode, "raw") != 0)
{
EMSG2(_(e_invarg2), mode);
return;
}
rettv->vval.v_number = channel_open((char *)address, port, NULL);
if (rettv->vval.v_number >= 0)
{
channel_set_json_mode(rettv->vval.v_number, json_mode);
if (callback != NULL && *callback != NUL)
channel_set_callback(rettv->vval.v_number, callback);
}
}
/*
* common for "sendexpr()" and "sendraw()"
* Returns the channel index if the caller should read the response.
* Otherwise returns -1.
*/
static int
send_common(typval_T *argvars, char_u *text, char *fun)
{
int ch_idx;
char_u *callback = NULL;
ch_idx = get_channel_arg(&argvars[0]);
if (ch_idx < 0)
return -1;
if (argvars[2].v_type != VAR_UNKNOWN)
{
callback = get_callback(&argvars[2]);
if (callback == NULL)
return -1;
}
/* Set the callback or clear it. An empty callback means no callback and
* not reading the response. */
channel_set_req_callback(ch_idx,
callback != NULL && *callback == NUL ? NULL : callback);
if (channel_send(ch_idx, text, fun) == OK && callback == NULL)
return ch_idx;
return -1;
}
/*
* "ch_sendexpr()" function
*/
static void
f_ch_sendexpr(typval_T *argvars, typval_T *rettv)
{
char_u *text;
typval_T *listtv;
int ch_idx;
int id;
/* return an empty string by default */
rettv->v_type = VAR_STRING;
rettv->vval.v_string = NULL;
id = channel_get_id();
text = json_encode_nr_expr(id, &argvars[1]);
if (text == NULL)
return;
ch_idx = send_common(argvars, text, "sendexpr");
if (ch_idx >= 0)
{
if (channel_read_json_block(ch_idx, id, &listtv) == OK)
{
if (listtv->v_type == VAR_LIST)
{
list_T *list = listtv->vval.v_list;
if (list->lv_len == 2)
{
/* Move the item from the list and then change the type to
* avoid the value being freed. */
*rettv = list->lv_last->li_tv;
list->lv_last->li_tv.v_type = VAR_NUMBER;
}
}
clear_tv(listtv);
}
}
}
/*
* "ch_sendraw()" function
*/
static void
f_ch_sendraw(typval_T *argvars, typval_T *rettv)
{
char_u buf[NUMBUFLEN];
char_u *text;
int ch_idx;
/* return an empty string by default */
rettv->v_type = VAR_STRING;
rettv->vval.v_string = NULL;
text = get_tv_string_buf(&argvars[1], buf);
ch_idx = send_common(argvars, text, "sendraw");
if (ch_idx >= 0)
rettv->vval.v_string = channel_read_block(ch_idx);
}
#endif
/*
* "changenr()" function
*/
@@ -10033,84 +10232,6 @@ f_count(typval_T *argvars, typval_T *rettv)
rettv->vval.v_number = n;
}
#ifdef FEAT_CHANNEL
/*
* Get a callback from "arg". It can be a Funcref or a function name.
* When "arg" is zero return an empty string.
* Return NULL for an invalid argument.
*/
static char_u *
get_callback(typval_T *arg)
{
if (arg->v_type == VAR_FUNC || arg->v_type == VAR_STRING)
return arg->vval.v_string;
if (arg->v_type == VAR_NUMBER && arg->vval.v_number == 0)
return (char_u *)"";
EMSG(_("E999: Invalid callback argument"));
return NULL;
}
/*
* "connect()" function
*/
static void
f_connect(typval_T *argvars, typval_T *rettv)
{
char_u *address;
char_u *mode;
char_u *callback = NULL;
char_u buf1[NUMBUFLEN];
char_u *p;
int port;
int json_mode = FALSE;
/* default: fail */
rettv->vval.v_number = -1;
address = get_tv_string(&argvars[0]);
mode = get_tv_string_buf(&argvars[1], buf1);
if (argvars[2].v_type != VAR_UNKNOWN)
{
callback = get_callback(&argvars[2]);
if (callback == NULL)
return;
}
/* parse address */
p = vim_strchr(address, ':');
if (p == NULL)
{
EMSG2(_(e_invarg2), address);
return;
}
*p++ = NUL;
port = atoi((char *)p);
if (*address == NUL || port <= 0)
{
p[-1] = ':';
EMSG2(_(e_invarg2), address);
return;
}
/* parse mode */
if (STRCMP(mode, "json") == 0)
json_mode = TRUE;
else if (STRCMP(mode, "raw") != 0)
{
EMSG2(_(e_invarg2), mode);
return;
}
rettv->vval.v_number = channel_open((char *)address, port, NULL);
if (rettv->vval.v_number >= 0)
{
channel_set_json_mode(rettv->vval.v_number, json_mode);
if (callback != NULL && *callback != NUL)
channel_set_callback(rettv->vval.v_number, callback);
}
}
#endif
/*
* "cscope_connection([{num} , {dbpath} [, {prepend}]])" function
*
@@ -10349,44 +10470,6 @@ f_diff_hlID(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
#endif
}
#ifdef FEAT_CHANNEL
/*
* Get the channel index from the handle argument.
* Returns -1 if the handle is invalid or the channel is closed.
*/
static int
get_channel_arg(typval_T *tv)
{
int ch_idx;
if (tv->v_type != VAR_NUMBER)
{
EMSG2(_(e_invarg2), get_tv_string(tv));
return -1;
}
ch_idx = tv->vval.v_number;
if (!channel_is_open(ch_idx))
{
EMSGN(_("E906: not an open channel"), ch_idx);
return -1;
}
return ch_idx;
}
/*
* "disconnect()" function
*/
static void
f_disconnect(typval_T *argvars, typval_T *rettv UNUSED)
{
int ch_idx = get_channel_arg(&argvars[0]);
if (ch_idx >= 0)
channel_close(ch_idx);
}
#endif
/*
* "empty({expr})" function
*/
@@ -14119,9 +14202,9 @@ f_jsondecode(typval_T *argvars, typval_T *rettv)
js_read_T reader;
reader.js_buf = get_tv_string(&argvars[0]);
reader.js_eof = TRUE;
reader.js_fill = NULL;
reader.js_used = 0;
if (json_decode(&reader, rettv) == FAIL)
if (json_decode_all(&reader, rettv) != OK)
EMSG(_(e_invarg));
}
@@ -16887,102 +16970,6 @@ f_searchpos(typval_T *argvars, typval_T *rettv)
list_append_number(rettv->vval.v_list, (varnumber_T)n);
}
#ifdef FEAT_CHANNEL
/*
* common for "sendexpr()" and "sendraw()"
* Returns the channel index if the caller should read the response.
* Otherwise returns -1.
*/
static int
send_common(typval_T *argvars, char_u *text, char *fun)
{
int ch_idx;
char_u *callback = NULL;
ch_idx = get_channel_arg(&argvars[0]);
if (ch_idx < 0)
return -1;
if (argvars[2].v_type != VAR_UNKNOWN)
{
callback = get_callback(&argvars[2]);
if (callback == NULL)
return -1;
}
/* Set the callback or clear it. An empty callback means no callback and
* not reading the response. */
channel_set_req_callback(ch_idx,
callback != NULL && *callback == NUL ? NULL : callback);
if (channel_send(ch_idx, text, fun) == OK && callback == NULL)
return ch_idx;
return -1;
}
/*
* "sendexpr()" function
*/
static void
f_sendexpr(typval_T *argvars, typval_T *rettv)
{
char_u *text;
typval_T *listtv;
int ch_idx;
int id;
/* return an empty string by default */
rettv->v_type = VAR_STRING;
rettv->vval.v_string = NULL;
id = channel_get_id();
text = json_encode_nr_expr(id, &argvars[1]);
if (text == NULL)
return;
ch_idx = send_common(argvars, text, "sendexpr");
if (ch_idx >= 0)
{
if (channel_read_json_block(ch_idx, id, &listtv) == OK)
{
if (listtv->v_type == VAR_LIST)
{
list_T *list = listtv->vval.v_list;
if (list->lv_len == 2)
{
/* Move the item from the list and then change the type to
* avoid the value being freed. */
*rettv = list->lv_last->li_tv;
list->lv_last->li_tv.v_type = VAR_NUMBER;
}
}
clear_tv(listtv);
}
}
}
/*
* "sendraw()" function
*/
static void
f_sendraw(typval_T *argvars, typval_T *rettv)
{
char_u buf[NUMBUFLEN];
char_u *text;
int ch_idx;
/* return an empty string by default */
rettv->v_type = VAR_STRING;
rettv->vval.v_string = NULL;
text = get_tv_string_buf(&argvars[1], buf);
ch_idx = send_common(argvars, text, "sendraw");
if (ch_idx >= 0)
rettv->vval.v_string = channel_read_block(ch_idx);
}
#endif
static void
f_server2client(typval_T *argvars UNUSED, typval_T *rettv)
{
+303 -126
View File
@@ -17,7 +17,7 @@
#if defined(FEAT_EVAL) || defined(PROTO)
static int json_encode_item(garray_T *gap, typval_T *val, int copyID);
static void json_decode_item(js_read_T *reader, typval_T *res);
static int json_decode_item(js_read_T *reader, typval_T *res);
/*
* Encode "val" into a JSON format string.
@@ -234,37 +234,60 @@ json_encode_item(garray_T *gap, typval_T *val, int copyID)
return OK;
}
/*
* When "reader" has less than NUMBUFLEN bytes available, call the fill
* callback to get more.
*/
static void
fill_numbuflen(js_read_T *reader)
{
if (reader->js_fill != NULL && (int)(reader->js_end - reader->js_buf)
- reader->js_used < NUMBUFLEN)
{
if (reader->js_fill(reader))
reader->js_end = reader->js_buf + STRLEN(reader->js_buf);
}
}
/*
* Skip white space in "reader".
* Also tops up readahead when needed.
*/
static void
json_skip_white(js_read_T *reader)
{
int c;
while ((c = reader->js_buf[reader->js_used]) == ' '
|| c == TAB || c == NL || c == CAR)
for (;;)
{
c = reader->js_buf[reader->js_used];
if (reader->js_fill != NULL && c == NUL)
{
if (reader->js_fill(reader))
reader->js_end = reader->js_buf + STRLEN(reader->js_buf);
continue;
}
if (c != ' ' && c != TAB && c != NL && c != CAR)
break;
++reader->js_used;
}
fill_numbuflen(reader);
}
/*
* Make sure there are at least enough characters buffered to read a number.
*/
static void
json_fill_buffer(js_read_T *reader UNUSED)
{
/* TODO */
}
static void
static int
json_decode_array(js_read_T *reader, typval_T *res)
{
char_u *p;
typval_T item;
listitem_T *li;
int ret;
if (rettv_list_alloc(res) == FAIL)
goto failsilent;
if (res != NULL && rettv_list_alloc(res) == FAIL)
{
res->v_type = VAR_SPECIAL;
res->vval.v_number = VVAL_NONE;
return FAIL;
}
++reader->js_used; /* consume the '[' */
while (TRUE)
@@ -272,38 +295,43 @@ json_decode_array(js_read_T *reader, typval_T *res)
json_skip_white(reader);
p = reader->js_buf + reader->js_used;
if (*p == NUL)
goto fail;
return MAYBE;
if (*p == ']')
{
++reader->js_used; /* consume the ']' */
return;
break;
}
if (!reader->js_eof && (int)(reader->js_end - p) < NUMBUFLEN)
json_fill_buffer(reader);
json_decode_item(reader, &item);
li = listitem_alloc();
if (li == NULL)
return;
li->li_tv = item;
list_append(res->vval.v_list, li);
ret = json_decode_item(reader, res == NULL ? NULL : &item);
if (ret != OK)
return ret;
if (res != NULL)
{
li = listitem_alloc();
if (li == NULL)
{
clear_tv(&item);
return FAIL;
}
li->li_tv = item;
list_append(res->vval.v_list, li);
}
json_skip_white(reader);
p = reader->js_buf + reader->js_used;
if (*p == ',')
++reader->js_used;
else if (*p != ']')
goto fail;
{
if (*p == NUL)
return MAYBE;
return FAIL;
}
}
fail:
EMSG(_(e_invarg));
failsilent:
res->v_type = VAR_SPECIAL;
res->vval.v_number = VVAL_NONE;
return OK;
}
static void
static int
json_decode_object(js_read_T *reader, typval_T *res)
{
char_u *p;
@@ -311,10 +339,15 @@ json_decode_object(js_read_T *reader, typval_T *res)
typval_T item;
dictitem_T *di;
char_u buf[NUMBUFLEN];
char_u *key;
char_u *key = NULL;
int ret;
if (rettv_dict_alloc(res) == FAIL)
goto failsilent;
if (res != NULL && rettv_dict_alloc(res) == FAIL)
{
res->v_type = VAR_SPECIAL;
res->vval.v_number = VVAL_NONE;
return FAIL;
}
++reader->js_used; /* consume the '{' */
while (TRUE)
@@ -322,243 +355,387 @@ json_decode_object(js_read_T *reader, typval_T *res)
json_skip_white(reader);
p = reader->js_buf + reader->js_used;
if (*p == NUL)
goto fail;
return MAYBE;
if (*p == '}')
{
++reader->js_used; /* consume the '}' */
return;
break;
}
if (!reader->js_eof && (int)(reader->js_end - p) < NUMBUFLEN)
json_fill_buffer(reader);
json_decode_item(reader, &tvkey);
key = get_tv_string_buf_chk(&tvkey, buf);
if (key == NULL || *key == NUL)
ret = json_decode_item(reader, res == NULL ? NULL : &tvkey);
if (ret != OK)
return ret;
if (res != NULL)
{
/* "key" is NULL when get_tv_string_buf_chk() gave an errmsg */
if (key != NULL)
EMSG(_(e_emptykey));
clear_tv(&tvkey);
goto failsilent;
key = get_tv_string_buf_chk(&tvkey, buf);
if (key == NULL || *key == NUL)
{
clear_tv(&tvkey);
return FAIL;
}
}
json_skip_white(reader);
p = reader->js_buf + reader->js_used;
if (*p != ':')
{
clear_tv(&tvkey);
goto fail;
if (res != NULL)
clear_tv(&tvkey);
if (*p == NUL)
return MAYBE;
return FAIL;
}
++reader->js_used;
json_skip_white(reader);
if (!reader->js_eof && (int)(reader->js_end - p) < NUMBUFLEN)
json_fill_buffer(reader);
json_decode_item(reader, &item);
di = dictitem_alloc(key);
clear_tv(&tvkey);
if (di == NULL)
ret = json_decode_item(reader, res == NULL ? NULL : &item);
if (ret != OK)
{
clear_tv(&item);
goto fail;
if (res != NULL)
clear_tv(&tvkey);
return ret;
}
if (res != NULL)
{
di = dictitem_alloc(key);
clear_tv(&tvkey);
if (di == NULL)
{
clear_tv(&item);
return FAIL;
}
di->di_tv = item;
if (dict_add(res->vval.v_dict, di) == FAIL)
{
dictitem_free(di);
return FAIL;
}
}
di->di_tv = item;
if (dict_add(res->vval.v_dict, di) == FAIL)
dictitem_free(di);
json_skip_white(reader);
p = reader->js_buf + reader->js_used;
if (*p == ',')
++reader->js_used;
else if (*p != '}')
goto fail;
{
if (*p == NUL)
return MAYBE;
return FAIL;
}
}
fail:
EMSG(_(e_invarg));
failsilent:
res->v_type = VAR_SPECIAL;
res->vval.v_number = VVAL_NONE;
return OK;
}
static void
static int
json_decode_string(js_read_T *reader, typval_T *res)
{
garray_T ga;
int len;
char_u *p = reader->js_buf + reader->js_used + 1;
char_u *p;
int c;
long nr;
char_u buf[NUMBUFLEN];
ga_init2(&ga, 1, 200);
if (res != NULL)
ga_init2(&ga, 1, 200);
/* TODO: fill buffer when needed. */
while (*p != NUL && *p != '"')
p = reader->js_buf + reader->js_used + 1; /* skip over " */
while (*p != '"')
{
if (*p == NUL || p[1] == NUL
#ifdef FEAT_MBYTE
|| utf_ptr2len(p) < utf_byte2len(*p)
#endif
)
{
if (reader->js_fill == NULL)
break;
len = (int)(reader->js_end - p);
reader->js_used = (int)(p - reader->js_buf);
if (!reader->js_fill(reader))
break; /* didn't get more */
p = reader->js_buf + reader->js_used;
reader->js_end = reader->js_buf + STRLEN(reader->js_buf);
continue;
}
if (*p == '\\')
{
c = -1;
switch (p[1])
{
case '\\': c = '\\'; break;
case '"': c = '"'; break;
case 'b': c = BS; break;
case 't': c = TAB; break;
case 'n': c = NL; break;
case 'f': c = FF; break;
case 'r': c = CAR; break;
case 'u':
if (reader->js_fill != NULL
&& (int)(reader->js_end - p) < NUMBUFLEN)
{
reader->js_used = (int)(p - reader->js_buf);
if (reader->js_fill(reader))
{
p = reader->js_buf + reader->js_used;
reader->js_end = reader->js_buf
+ STRLEN(reader->js_buf);
}
}
vim_str2nr(p + 2, NULL, &len,
STR2NR_HEX + STR2NR_FORCE, &nr, NULL, 4);
p += len + 2;
if (res != NULL)
{
#ifdef FEAT_MBYTE
buf[(*mb_char2bytes)((int)nr, buf)] = NUL;
ga_concat(&ga, buf);
buf[(*mb_char2bytes)((int)nr, buf)] = NUL;
ga_concat(&ga, buf);
#else
ga_append(&ga, nr);
ga_append(&ga, nr);
#endif
}
break;
default: c = p[1]; break;
default:
/* not a special char, skip over \ */
++p;
continue;
}
if (c > 0)
{
p += 2;
ga_append(&ga, c);
if (res != NULL)
ga_append(&ga, c);
}
}
else
{
len = MB_PTR2LEN(p);
if (ga_grow(&ga, len) == OK)
if (res != NULL)
{
if (ga_grow(&ga, len) == FAIL)
{
ga_clear(&ga);
return FAIL;
}
mch_memmove((char *)ga.ga_data + ga.ga_len, p, (size_t)len);
ga.ga_len += len;
}
p += len;
}
if (!reader->js_eof && (int)(reader->js_end - p) < NUMBUFLEN)
{
reader->js_used = (int)(p - reader->js_buf);
json_fill_buffer(reader);
p = reader->js_buf + reader->js_used;
}
}
reader->js_used = (int)(p - reader->js_buf);
if (*p == '"')
{
++reader->js_used;
res->v_type = VAR_STRING;
if (ga.ga_data == NULL)
res->vval.v_string = NULL;
else
res->vval.v_string = vim_strsave(ga.ga_data);
if (res != NULL)
{
res->v_type = VAR_STRING;
if (ga.ga_data == NULL)
res->vval.v_string = NULL;
else
res->vval.v_string = vim_strsave(ga.ga_data);
}
return OK;
}
else
if (res != NULL)
{
EMSG(_(e_invarg));
res->v_type = VAR_SPECIAL;
res->vval.v_number = VVAL_NONE;
ga_clear(&ga);
}
ga_clear(&ga);
return MAYBE;
}
/*
* Decode one item and put it in "result".
* Decode one item and put it in "res". If "res" is NULL only advance.
* Must already have skipped white space.
*
* Return FAIL for a decoding error.
* Return MAYBE for an incomplete message.
*/
static void
static int
json_decode_item(js_read_T *reader, typval_T *res)
{
char_u *p = reader->js_buf + reader->js_used;
char_u *p;
int len;
fill_numbuflen(reader);
p = reader->js_buf + reader->js_used;
switch (*p)
{
case '[': /* array */
json_decode_array(reader, res);
return;
return json_decode_array(reader, res);
case '{': /* object */
json_decode_object(reader, res);
return;
return json_decode_object(reader, res);
case '"': /* string */
json_decode_string(reader, res);
return;
return json_decode_string(reader, res);
case ',': /* comma: empty item */
case NUL: /* empty */
res->v_type = VAR_SPECIAL;
res->vval.v_number = VVAL_NONE;
return;
if (res != NULL)
{
res->v_type = VAR_SPECIAL;
res->vval.v_number = VVAL_NONE;
}
return OK;
default:
if (VIM_ISDIGIT(*p) || *p == '-')
{
int len;
char_u *sp = p;
#ifdef FEAT_FLOAT
if (*sp == '-')
{
++sp;
if (*sp == NUL)
return MAYBE;
if (!VIM_ISDIGIT(*sp))
return FAIL;
}
sp = skipdigits(sp);
if (*sp == '.' || *sp == 'e' || *sp == 'E')
{
res->v_type = VAR_FLOAT;
len = string2float(p, &res->vval.v_float);
if (res == NULL)
{
float_T f;
len = string2float(p, &f);
}
else
{
res->v_type = VAR_FLOAT;
len = string2float(p, &res->vval.v_float);
}
}
else
#endif
{
long nr;
res->v_type = VAR_NUMBER;
vim_str2nr(reader->js_buf + reader->js_used,
NULL, &len, 0, /* what */
&nr, NULL, 0);
res->vval.v_number = nr;
if (res != NULL)
{
res->v_type = VAR_NUMBER;
res->vval.v_number = nr;
}
}
reader->js_used += len;
return;
return OK;
}
if (STRNICMP((char *)p, "false", 5) == 0)
{
reader->js_used += 5;
res->v_type = VAR_SPECIAL;
res->vval.v_number = VVAL_FALSE;
return;
if (res != NULL)
{
res->v_type = VAR_SPECIAL;
res->vval.v_number = VVAL_FALSE;
}
return OK;
}
if (STRNICMP((char *)p, "true", 4) == 0)
{
reader->js_used += 4;
res->v_type = VAR_SPECIAL;
res->vval.v_number = VVAL_TRUE;
return;
if (res != NULL)
{
res->v_type = VAR_SPECIAL;
res->vval.v_number = VVAL_TRUE;
}
return OK;
}
if (STRNICMP((char *)p, "null", 4) == 0)
{
reader->js_used += 4;
res->v_type = VAR_SPECIAL;
res->vval.v_number = VVAL_NULL;
return;
if (res != NULL)
{
res->v_type = VAR_SPECIAL;
res->vval.v_number = VVAL_NULL;
}
return OK;
}
/* check for truncated name */
len = (int)(reader->js_end - (reader->js_buf + reader->js_used));
if ((len < 5 && STRNICMP((char *)p, "false", len) == 0)
|| (len < 4 && (STRNICMP((char *)p, "true", len) == 0
|| STRNICMP((char *)p, "null", len) == 0)))
return MAYBE;
break;
}
EMSG(_(e_invarg));
res->v_type = VAR_SPECIAL;
res->vval.v_number = VVAL_NONE;
if (res != NUL)
{
res->v_type = VAR_SPECIAL;
res->vval.v_number = VVAL_NONE;
}
return FAIL;
}
/*
* Decode the JSON from "reader" and store the result in "res".
* Return OK or FAIL;
* Return FAIL if not the whole message was consumed.
*/
int
json_decode(js_read_T *reader, typval_T *res)
json_decode_all(js_read_T *reader, typval_T *res)
{
int ret;
/* We get the end once, to avoid calling strlen() many times. */
reader->js_end = reader->js_buf + STRLEN(reader->js_buf);
json_skip_white(reader);
json_decode_item(reader, res);
ret = json_decode_item(reader, res);
if (ret != OK)
return FAIL;
json_skip_white(reader);
if (reader->js_buf[reader->js_used] != NUL)
return FAIL;
return OK;
}
/*
* Decode the JSON from "reader" and store the result in "res".
* Return FAIL if the message has a decoding error or the message is
* truncated. Consumes the message anyway.
*/
int
json_decode(js_read_T *reader, typval_T *res)
{
int ret;
/* We get the end once, to avoid calling strlen() many times. */
reader->js_end = reader->js_buf + STRLEN(reader->js_buf);
json_skip_white(reader);
ret = json_decode_item(reader, res);
json_skip_white(reader);
return ret == OK ? OK : FAIL;
}
/*
* Decode the JSON from "reader" to find the end of the message.
* Return FAIL if the message has a decoding error.
* Return MAYBE if the message is truncated, need to read more.
* This only works reliable if the message contains an object, array or
* string. A number might be trucated without knowing.
* Does not advance the reader.
*/
int
json_find_end(js_read_T *reader)
{
int used_save = reader->js_used;
int ret;
/* We get the end once, to avoid calling strlen() many times. */
reader->js_end = reader->js_buf + STRLEN(reader->js_buf);
json_skip_white(reader);
ret = json_decode_item(reader, NULL);
reader->js_used = used_save;
return ret;
}
#endif
+197
View File
@@ -0,0 +1,197 @@
/* vi:set ts=8 sts=4 sw=4:
*
* VIM - Vi IMproved by Bram Moolenaar
*
* Do ":help uganda" in Vim to read copying and usage conditions.
* Do ":help credits" in Vim to see a list of people who contributed.
* See README.txt for an overview of the Vim source code.
*/
/*
* json_test.c: Unittests for json.c
*/
#undef NDEBUG
#include <assert.h>
/* Must include main.c because it contains much more than just main() */
#define NO_VIM_MAIN
#include "main.c"
/* This file has to be included because the tested functions are static */
#include "json.c"
#if defined(FEAT_EVAL)
/*
* Test json_find_end() with imcomplete items.
*/
static void
test_decode_find_end(void)
{
js_read_T reader;
reader.js_fill = NULL;
reader.js_used = 0;
/* string and incomplete string */
reader.js_buf = (char_u *)"\"hello\"";
assert(json_find_end(&reader) == OK);
reader.js_buf = (char_u *)" \"hello\" ";
assert(json_find_end(&reader) == OK);
reader.js_buf = (char_u *)"\"hello";
assert(json_find_end(&reader) == MAYBE);
/* number and dash (incomplete number) */
reader.js_buf = (char_u *)"123";
assert(json_find_end(&reader) == OK);
reader.js_buf = (char_u *)"-";
assert(json_find_end(&reader) == MAYBE);
/* false, true and null, also incomplete */
reader.js_buf = (char_u *)"false";
assert(json_find_end(&reader) == OK);
reader.js_buf = (char_u *)"f";
assert(json_find_end(&reader) == MAYBE);
reader.js_buf = (char_u *)"fa";
assert(json_find_end(&reader) == MAYBE);
reader.js_buf = (char_u *)"fal";
assert(json_find_end(&reader) == MAYBE);
reader.js_buf = (char_u *)"fals";
assert(json_find_end(&reader) == MAYBE);
reader.js_buf = (char_u *)"true";
assert(json_find_end(&reader) == OK);
reader.js_buf = (char_u *)"t";
assert(json_find_end(&reader) == MAYBE);
reader.js_buf = (char_u *)"tr";
assert(json_find_end(&reader) == MAYBE);
reader.js_buf = (char_u *)"tru";
assert(json_find_end(&reader) == MAYBE);
reader.js_buf = (char_u *)"null";
assert(json_find_end(&reader) == OK);
reader.js_buf = (char_u *)"n";
assert(json_find_end(&reader) == MAYBE);
reader.js_buf = (char_u *)"nu";
assert(json_find_end(&reader) == MAYBE);
reader.js_buf = (char_u *)"nul";
assert(json_find_end(&reader) == MAYBE);
/* object without white space */
reader.js_buf = (char_u *)"{\"a\":123}";
assert(json_find_end(&reader) == OK);
reader.js_buf = (char_u *)"{\"a\":123";
assert(json_find_end(&reader) == MAYBE);
reader.js_buf = (char_u *)"{\"a\":";
assert(json_find_end(&reader) == MAYBE);
reader.js_buf = (char_u *)"{\"a\"";
assert(json_find_end(&reader) == MAYBE);
reader.js_buf = (char_u *)"{\"a";
assert(json_find_end(&reader) == MAYBE);
reader.js_buf = (char_u *)"{\"";
assert(json_find_end(&reader) == MAYBE);
reader.js_buf = (char_u *)"{";
assert(json_find_end(&reader) == MAYBE);
/* object with white space */
reader.js_buf = (char_u *)" { \"a\" : 123 } ";
assert(json_find_end(&reader) == OK);
reader.js_buf = (char_u *)" { \"a\" : 123 ";
assert(json_find_end(&reader) == MAYBE);
reader.js_buf = (char_u *)" { \"a\" : ";
assert(json_find_end(&reader) == MAYBE);
reader.js_buf = (char_u *)" { \"a\" ";
assert(json_find_end(&reader) == MAYBE);
reader.js_buf = (char_u *)" { \"a ";
assert(json_find_end(&reader) == MAYBE);
reader.js_buf = (char_u *)" { ";
assert(json_find_end(&reader) == MAYBE);
/* array without white space */
reader.js_buf = (char_u *)"[\"a\",123]";
assert(json_find_end(&reader) == OK);
reader.js_buf = (char_u *)"[\"a\",123";
assert(json_find_end(&reader) == MAYBE);
reader.js_buf = (char_u *)"[\"a\",";
assert(json_find_end(&reader) == MAYBE);
reader.js_buf = (char_u *)"[\"a\"";
assert(json_find_end(&reader) == MAYBE);
reader.js_buf = (char_u *)"[\"a";
assert(json_find_end(&reader) == MAYBE);
reader.js_buf = (char_u *)"[\"";
assert(json_find_end(&reader) == MAYBE);
reader.js_buf = (char_u *)"[";
assert(json_find_end(&reader) == MAYBE);
/* array with white space */
reader.js_buf = (char_u *)" [ \"a\" , 123 ] ";
assert(json_find_end(&reader) == OK);
reader.js_buf = (char_u *)" [ \"a\" , 123 ";
assert(json_find_end(&reader) == MAYBE);
reader.js_buf = (char_u *)" [ \"a\" , ";
assert(json_find_end(&reader) == MAYBE);
reader.js_buf = (char_u *)" [ \"a\" ";
assert(json_find_end(&reader) == MAYBE);
reader.js_buf = (char_u *)" [ \"a ";
assert(json_find_end(&reader) == MAYBE);
reader.js_buf = (char_u *)" [ ";
assert(json_find_end(&reader) == MAYBE);
}
static int
fill_from_cookie(js_read_T *reader)
{
reader->js_buf = reader->js_cookie;
return TRUE;
}
/*
* Test json_find_end with an incomplete array, calling the fill function.
*/
static void
test_fill_called_on_find_end(void)
{
js_read_T reader;
reader.js_fill = fill_from_cookie;
reader.js_used = 0;
reader.js_buf = (char_u *)" [ \"a\" , 123 ";
reader.js_cookie = " [ \"a\" , 123 ] ";
assert(json_find_end(&reader) == OK);
reader.js_buf = (char_u *)" [ \"a\" , ";
assert(json_find_end(&reader) == OK);
reader.js_buf = (char_u *)" [ \"a\" ";
assert(json_find_end(&reader) == OK);
reader.js_buf = (char_u *)" [ \"a";
assert(json_find_end(&reader) == OK);
reader.js_buf = (char_u *)" [ ";
assert(json_find_end(&reader) == OK);
}
/*
* Test json_find_end with an incomplete string, calling the fill function.
*/
static void
test_fill_called_on_string(void)
{
js_read_T reader;
reader.js_fill = fill_from_cookie;
reader.js_used = 0;
reader.js_buf = (char_u *)" \"foo";
reader.js_end = reader.js_buf + STRLEN(reader.js_buf);
reader.js_cookie = " \"foobar\" ";
assert(json_decode_string(&reader, NULL) == OK);
}
#endif
int
main(void)
{
#if defined(FEAT_EVAL)
test_decode_find_end();
test_fill_called_on_find_end();
test_fill_called_on_string();
#endif
return 0;
}
-2
View File
@@ -25,8 +25,6 @@
#define index_to_key(i) ((i) ^ 15167)
#define TEST_COUNT 50000
static void test_mf_hash(void);
/*
* Test mf_hash_*() functions.
*/
+1 -2
View File
@@ -4075,8 +4075,7 @@ attention_message(
}
/* Some of these messages are long to allow translation to
* other languages. */
MSG_PUTS(_("\n(1) Another program may be editing the same file. If this is the case,\n be careful not to end up with two different instances of the same\n file when making changes."));
MSG_PUTS(_(" Quit, or continue with caution.\n"));
MSG_PUTS(_("\n(1) Another program may be editing the same file. If this is the case,\n be careful not to end up with two different instances of the same\n file when making changes. Quit, or continue with caution.\n"));
MSG_PUTS(_("(2) An edit session for this file crashed.\n"));
MSG_PUTS(_(" If this is the case, use \":recover\" or \"vim -r "));
msg_outtrans(buf->b_fname);
+2
View File
@@ -1,5 +1,7 @@
/* json.c */
char_u *json_encode(typval_T *val);
char_u *json_encode_nr_expr(int nr, typval_T *val);
int json_decode_all(js_read_T *reader, typval_T *res);
int json_decode(js_read_T *reader, typval_T *res);
int json_find_end(js_read_T *reader);
/* vim: set ft=c : */
+8 -6
View File
@@ -2703,12 +2703,14 @@ typedef struct {
/*
* Structure used for reading in json_decode().
*/
typedef struct
struct js_reader
{
char_u *js_buf; /* text to be decoded */
char_u *js_end; /* NUL in js_buf when js_eof is FALSE */
char_u *js_end; /* NUL in js_buf */
int js_used; /* bytes used from js_buf */
int js_eof; /* when TRUE js_buf is all there is */
int (*js_fill)(void *); /* function to fill the buffer */
void *js_cookie; /* passed to js_fill */
} js_read_T;
int (*js_fill)(struct js_reader *);
/* function to fill the buffer or NULL;
* return TRUE when the buffer was filled */
void *js_cookie; /* can be used by js_fill */
};
typedef struct js_reader js_read_T;
+1 -1
View File
@@ -1,7 +1,7 @@
# A very (if not the most) simplistic Makefile for MSVC
CC=cl
CFLAGS=/O2
CFLAGS=/O2 /nologo
tee.exe: tee.obj
$(CC) $(CFLAGS) /Fo$@ $**
+18
View File
@@ -757,6 +757,24 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
1245,
/**/
1244,
/**/
1243,
/**/
1242,
/**/
1241,
/**/
1240,
/**/
1239,
/**/
1238,
/**/
1237,
/**/
1236,
/**/