Created
March 13, 2015 06:56
-
-
Save ynkdir/996d53a402f533fed518 to your computer and use it in GitHub Desktop.
Vim clientserver without X
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff -r 15b934a16641 -r 2082fc32d223 runtime/plugin/rrhelper.vim | |
--- a/runtime/plugin/rrhelper.vim Wed Sep 14 19:04:40 2011 +0200 | |
+++ b/runtime/plugin/rrhelper.vim Thu Sep 15 21:25:11 2011 +0900 | |
@@ -16,7 +16,7 @@ | |
let max = argc() | |
let id = expand("<client>") | |
- if id == 0 | |
+ if id == '' | |
return | |
endif | |
while cnt < max | |
diff -r 15b934a16641 -r 2082fc32d223 src/Make_ming.mak | |
--- a/src/Make_ming.mak Wed Sep 14 19:04:40 2011 +0200 | |
+++ b/src/Make_ming.mak Thu Sep 15 21:25:11 2011 +0900 | |
@@ -466,6 +466,7 @@ | |
$(OUTDIR)/getchar.o \ | |
$(OUTDIR)/hardcopy.o \ | |
$(OUTDIR)/hashtab.o \ | |
+ $(OUTDIR)/if_cmdsrv.o \ | |
$(OUTDIR)/main.o \ | |
$(OUTDIR)/mark.o \ | |
$(OUTDIR)/memfile.o \ | |
@@ -715,6 +716,8 @@ | |
mzscheme_base.c: | |
$(MZSCHEME)/mzc --c-mods mzscheme_base.c ++lib scheme/base | |
+$(OUTDIR)/if_cmdsrv.o: $(OUTDIR) if_cmdsrv.c if_cmdsrv_win32.c $(INCL) | |
+ | |
pathdef.c: $(INCL) | |
ifneq (sh.exe, $(SHELL)) | |
@echo creating pathdef.c | |
diff -r 15b934a16641 -r 2082fc32d223 src/Make_mvc.mak | |
--- a/src/Make_mvc.mak Wed Sep 14 19:04:40 2011 +0200 | |
+++ b/src/Make_mvc.mak Thu Sep 15 21:25:11 2011 +0900 | |
@@ -287,6 +287,10 @@ | |
!endif | |
!endif | |
+CMDSRV_PRO = proto/if_cmdsrv.pro | |
+CMDSRV_OBJ = $(OBJDIR)/if_cmdsrv.obj | |
+CMDSRV_DEFS = | |
+ | |
# Set which version of the CRT to use | |
!if defined(USE_MSVCRT) | |
# CVARS = $(cvarsdll) | |
@@ -321,6 +325,7 @@ | |
CFLAGS = -c /W3 /nologo $(CVARS) -I. -Iproto -DHAVE_PATHDEF -DWIN32 \ | |
$(SNIFF_DEFS) $(CSCOPE_DEFS) $(NETBEANS_DEFS) \ | |
$(NBDEBUG_DEFS) $(XPM_DEFS) \ | |
+ $(CMDSRV_DEFS) \ | |
$(DEFINES) -DWINVER=$(WINVER) -D_WIN32_WINNT=$(WINVER) \ | |
/Fo$(OUTDIR)/ | |
@@ -901,12 +906,12 @@ | |
$(VIM).exe: $(OUTDIR) $(OBJ) $(GUI_OBJ) $(OLE_OBJ) $(OLE_IDL) $(MZSCHEME_OBJ) \ | |
$(LUA_OBJ) $(PERL_OBJ) $(PYTHON_OBJ) $(PYTHON3_OBJ) $(RUBY_OBJ) $(TCL_OBJ) \ | |
$(SNIFF_OBJ) $(CSCOPE_OBJ) $(NETBEANS_OBJ) $(XPM_OBJ) \ | |
- version.c version.h | |
+ $(CMDSRV_OBJ) version.c version.h | |
$(CC) $(CFLAGS) version.c | |
$(link) $(LINKARGS1) -out:$(VIM).exe $(OBJ) $(GUI_OBJ) $(OLE_OBJ) \ | |
$(LUA_OBJ) $(MZSCHEME_OBJ) $(PERL_OBJ) $(PYTHON_OBJ) $(PYTHON3_OBJ) $(RUBY_OBJ) \ | |
$(TCL_OBJ) $(SNIFF_OBJ) $(CSCOPE_OBJ) $(NETBEANS_OBJ) \ | |
- $(XPM_OBJ) $(OUTDIR)\version.obj $(LINKARGS2) | |
+ $(XPM_OBJ) $(CMDSRV_OBJ) $(OUTDIR)\version.obj $(LINKARGS2) | |
$(VIM): $(VIM).exe | |
@@ -1100,6 +1105,8 @@ | |
$(OUTDIR)/netbeans.obj: $(OUTDIR) netbeans.c $(NBDEBUG_SRC) $(INCL) | |
+$(OUTDIR)/if_cmdsrv.obj: $(OUTDIR) if_cmdsrv.c if_cmdsrv_win32.c $(INCL) | |
+ | |
$(OUTDIR)/normal.obj: $(OUTDIR) normal.c $(INCL) | |
$(OUTDIR)/option.obj: $(OUTDIR) option.c $(INCL) | |
@@ -1229,7 +1236,8 @@ | |
proto/ui.pro \ | |
proto/undo.pro \ | |
proto/window.pro \ | |
- $(NETBEANS_PRO) | |
+ $(NETBEANS_PRO) \ | |
+ $(CMDSRV_PRO) | |
.SUFFIXES: .cod .i | |
diff -r 15b934a16641 -r 2082fc32d223 src/Makefile | |
--- a/src/Makefile Wed Sep 14 19:04:40 2011 +0200 | |
+++ b/src/Makefile Thu Sep 15 21:25:11 2011 +0900 | |
@@ -1425,7 +1425,7 @@ | |
hardcopy.c \ | |
hashtab.c \ | |
if_cscope.c \ | |
- if_xcmdsrv.c \ | |
+ if_cmdsrv.c \ | |
main.c \ | |
mark.c \ | |
memfile.c \ | |
@@ -1515,7 +1515,7 @@ | |
objects/hashtab.o \ | |
$(HANGULIN_OBJ) \ | |
objects/if_cscope.o \ | |
- objects/if_xcmdsrv.o \ | |
+ objects/if_cmdsrv.o \ | |
objects/mark.o \ | |
objects/memline.o \ | |
objects/menu.o \ | |
@@ -1584,7 +1584,7 @@ | |
hashtab.pro \ | |
hangulin.pro \ | |
if_cscope.pro \ | |
- if_xcmdsrv.pro \ | |
+ if_cmdsrv.pro \ | |
if_python.pro \ | |
if_python3.pro \ | |
if_ruby.pro \ | |
@@ -2531,8 +2531,8 @@ | |
objects/if_cscope.o: if_cscope.c | |
$(CCC) -o $@ if_cscope.c | |
-objects/if_xcmdsrv.o: if_xcmdsrv.c | |
- $(CCC) -o $@ if_xcmdsrv.c | |
+objects/if_cmdsrv.o: if_cmdsrv.c | |
+ $(CCC) -o $@ if_cmdsrv.c | |
objects/if_lua.o: if_lua.c | |
$(CCC) $(LUA_CFLAGS) -o $@ if_lua.c | |
@@ -2852,10 +2852,10 @@ | |
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 ex_cmds.h proto.h \ | |
globals.h farsi.h arabic.h if_cscope.h | |
-objects/if_xcmdsrv.o: if_xcmdsrv.c vim.h auto/config.h feature.h os_unix.h \ | |
+objects/if_cmdsrv.o: if_cmdsrv.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 ex_cmds.h proto.h \ | |
- globals.h farsi.h arabic.h version.h | |
+ globals.h farsi.h arabic.h version.h if_cmdsrv_unix.c | |
objects/main.o: 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 ex_cmds.h proto.h globals.h farsi.h \ | |
diff -r 15b934a16641 -r 2082fc32d223 src/charset.c | |
--- a/src/charset.c Wed Sep 14 19:04:40 2011 +0200 | |
+++ b/src/charset.c Thu Sep 15 21:25:11 2011 +0900 | |
@@ -1897,7 +1897,7 @@ | |
return c - '0'; | |
} | |
-#if defined(FEAT_TERMRESPONSE) \ | |
+#if defined(FEAT_TERMRESPONSE) || defined(FEAT_CLIENTSERVER) \ | |
|| (defined(FEAT_GUI_GTK) && defined(FEAT_WINDOWS)) || defined(PROTO) | |
/* | |
* Convert two hex characters to a byte. | |
diff -r 15b934a16641 -r 2082fc32d223 src/config.h.in | |
--- a/src/config.h.in Wed Sep 14 19:04:40 2011 +0200 | |
+++ b/src/config.h.in Thu Sep 15 21:25:11 2011 +0900 | |
@@ -210,6 +210,8 @@ | |
#undef HAVE_USLEEP | |
#undef HAVE_UTIME | |
#undef HAVE_BIND_TEXTDOMAIN_CODESET | |
+#undef HAVE_GETPEERUCRED | |
+#undef HAVE_GETPEEREID | |
/* Define, if needed, for accessing large files. */ | |
#undef _LARGE_FILES | |
diff -r 15b934a16641 -r 2082fc32d223 src/configure.in | |
--- a/src/configure.in Wed Sep 14 19:04:40 2011 +0200 | |
+++ b/src/configure.in Thu Sep 15 21:25:11 2011 +0900 | |
@@ -2973,7 +2973,7 @@ | |
setpgid setsid sigaltstack sigstack sigset sigsetjmp sigaction \ | |
sigvec strcasecmp strerror strftime stricmp strncasecmp \ | |
strnicmp strpbrk strtol tgetent towlower towupper iswupper \ | |
- usleep utime utimes) | |
+ usleep utime utimes getpeerucred getpeereid) | |
AC_FUNC_FSEEKO | |
dnl define _LARGE_FILES, _FILE_OFFSET_BITS and _LARGEFILE_SOURCE when | |
diff -r 15b934a16641 -r 2082fc32d223 src/eval.c | |
--- a/src/eval.c Wed Sep 14 19:04:40 2011 +0200 | |
+++ b/src/eval.c Thu Sep 15 21:25:11 2011 +0900 | |
@@ -14535,38 +14535,6 @@ | |
#endif | |
} | |
-#if defined(FEAT_CLIENTSERVER) && defined(FEAT_X11) | |
-static void make_connection __ARGS((void)); | |
-static int check_connection __ARGS((void)); | |
- | |
- static void | |
-make_connection() | |
-{ | |
- if (X_DISPLAY == NULL | |
-# ifdef FEAT_GUI | |
- && !gui.in_use | |
-# endif | |
- ) | |
- { | |
- x_force_connect = TRUE; | |
- setup_term_clip(); | |
- x_force_connect = FALSE; | |
- } | |
-} | |
- | |
- static int | |
-check_connection() | |
-{ | |
- make_connection(); | |
- if (X_DISPLAY == NULL) | |
- { | |
- EMSG(_("E240: No connection to Vim server")); | |
- return FAIL; | |
- } | |
- return OK; | |
-} | |
-#endif | |
- | |
#ifdef FEAT_CLIENTSERVER | |
static void remote_common __ARGS((typval_T *argvars, typval_T *rettv, int expr)); | |
@@ -14576,39 +14544,42 @@ | |
typval_T *rettv; | |
int expr; | |
{ | |
- char_u *server_name; | |
+ char_u *name; | |
char_u *keys; | |
+ char_u *serverid; | |
char_u *r = NULL; | |
char_u buf[NUMBUFLEN]; | |
-# ifdef WIN32 | |
- HWND w; | |
-# else | |
- Window w; | |
-# endif | |
+ int ret; | |
if (check_restricted() || check_secure()) | |
return; | |
-# ifdef FEAT_X11 | |
- if (check_connection() == FAIL) | |
- return; | |
-# endif | |
- | |
- server_name = get_tv_string_chk(&argvars[0]); | |
- if (server_name == NULL) | |
+ name = get_tv_string_chk(&argvars[0]); | |
+ if (name == NULL) | |
return; /* type error; errmsg already given */ | |
keys = get_tv_string_buf(&argvars[1], buf); | |
-# ifdef WIN32 | |
- if (serverSendToVim(server_name, keys, &r, &w, expr, TRUE) < 0) | |
-# else | |
- if (serverSendToVim(X_DISPLAY, server_name, keys, &r, &w, expr, 0, TRUE) | |
- < 0) | |
-# endif | |
+ | |
+ serverid = cmdsrv_find_server(name, TRUE); | |
+ if (serverid == NULL) | |
+ { | |
+ ret = -1; | |
+ } | |
+ else | |
+ { | |
+ if (expr) | |
+ ret = cmdsrv_send_expr(serverid, keys, &r); | |
+ else | |
+ ret = cmdsrv_send_keys(serverid, keys); | |
+ } | |
+ | |
+ if (ret != 0) | |
{ | |
if (r != NULL) | |
EMSG(r); /* sending worked but evaluation failed */ | |
else | |
- EMSG2(_("E241: Unable to send to %s"), server_name); | |
+ EMSG2(_(e_unabletosend), name); | |
+ vim_free(r); | |
+ vim_free(serverid); | |
return; | |
} | |
@@ -14617,17 +14588,20 @@ | |
if (argvars[2].v_type != VAR_UNKNOWN) | |
{ | |
dictitem_T v; | |
- char_u str[30]; | |
char_u *idvar; | |
- sprintf((char *)str, PRINTF_HEX_LONG_U, (long_u)w); | |
v.di_tv.v_type = VAR_STRING; | |
- v.di_tv.vval.v_string = vim_strsave(str); | |
+ if (serverid == NULL) | |
+ v.di_tv.vval.v_string = vim_strsave((char_u *)""); | |
+ else | |
+ v.di_tv.vval.v_string = vim_strsave(serverid); | |
idvar = get_tv_string_chk(&argvars[2]); | |
if (idvar != NULL) | |
set_var(idvar, &v.di_tv, FALSE); | |
vim_free(v.di_tv.vval.v_string); | |
} | |
+ | |
+ vim_free(serverid); | |
} | |
#endif | |
@@ -14655,22 +14629,26 @@ | |
typval_T *rettv UNUSED; | |
{ | |
#ifdef FEAT_CLIENTSERVER | |
-# ifdef WIN32 | |
- /* On Win32 it's done in this application. */ | |
- { | |
- char_u *server_name = get_tv_string_chk(&argvars[0]); | |
- | |
- if (server_name != NULL) | |
- serverForeground(server_name); | |
- } | |
-# else | |
- /* Send a foreground() expression to the server. */ | |
- argvars[1].v_type = VAR_STRING; | |
- argvars[1].vval.v_string = vim_strsave((char_u *)"foreground()"); | |
- argvars[2].v_type = VAR_UNKNOWN; | |
- remote_common(argvars, rettv, TRUE); | |
- vim_free(argvars[1].vval.v_string); | |
-# endif | |
+ char_u *name; | |
+ char_u *serverid; | |
+ | |
+ name = get_tv_string_chk(&argvars[0]); | |
+ if (name == NULL) | |
+ return; | |
+ | |
+ serverid = cmdsrv_find_server(name, TRUE); | |
+ if (serverid == NULL) | |
+ { | |
+ EMSG2(_(e_unabletosend), name); | |
+ return; | |
+ } | |
+ | |
+ if (cmdsrv_foreground(serverid) != 0) | |
+ { | |
+ EMSG2(_(e_unabletosend), name); | |
+ } | |
+ | |
+ vim_free(serverid); | |
#endif | |
} | |
@@ -14682,9 +14660,6 @@ | |
#ifdef FEAT_CLIENTSERVER | |
dictitem_T v; | |
char_u *s = NULL; | |
-# ifdef WIN32 | |
- long_u n = 0; | |
-# endif | |
char_u *serverid; | |
if (check_restricted() || check_secure()) | |
@@ -14698,22 +14673,13 @@ | |
rettv->vval.v_number = -1; | |
return; /* type error; errmsg already given */ | |
} | |
-# ifdef WIN32 | |
- sscanf(serverid, SCANF_HEX_LONG_U, &n); | |
- if (n == 0) | |
+ | |
+ if (cmdsrv_peek_reply(serverid, &s) != 0) | |
rettv->vval.v_number = -1; | |
- else | |
- { | |
- s = serverGetReply((HWND)n, FALSE, FALSE, FALSE); | |
- rettv->vval.v_number = (s != NULL); | |
- } | |
-# else | |
- if (check_connection() == FAIL) | |
- return; | |
- | |
- rettv->vval.v_number = serverPeekReply(X_DISPLAY, | |
- serverStrToWin(serverid), &s); | |
-# endif | |
+ else if (s != NULL) | |
+ rettv->vval.v_number = 1; | |
+ else | |
+ rettv->vval.v_number = 0; | |
if (argvars[1].v_type != VAR_UNKNOWN && rettv->vval.v_number > 0) | |
{ | |
@@ -14743,18 +14709,7 @@ | |
if (serverid != NULL && !check_restricted() && !check_secure()) | |
{ | |
-# ifdef WIN32 | |
- /* The server's HWND is encoded in the 'id' parameter */ | |
- long_u n = 0; | |
- | |
- sscanf(serverid, SCANF_HEX_LONG_U, &n); | |
- if (n != 0) | |
- r = serverGetReply((HWND)n, FALSE, TRUE, TRUE); | |
- if (r == NULL) | |
-# else | |
- if (check_connection() == FAIL || serverReadReply(X_DISPLAY, | |
- serverStrToWin(serverid), &r, FALSE) < 0) | |
-# endif | |
+ if (cmdsrv_wait_reply(serverid, &r) != 0) | |
EMSG(_("E277: Unable to read a server reply")); | |
} | |
#endif | |
@@ -15722,20 +15677,16 @@ | |
{ | |
#ifdef FEAT_CLIENTSERVER | |
char_u buf[NUMBUFLEN]; | |
- char_u *server = get_tv_string_chk(&argvars[0]); | |
+ char_u *serverid = get_tv_string_chk(&argvars[0]); | |
char_u *reply = get_tv_string_buf_chk(&argvars[1], buf); | |
rettv->vval.v_number = -1; | |
- if (server == NULL || reply == NULL) | |
+ if (serverid == NULL || reply == NULL) | |
return; | |
if (check_restricted() || check_secure()) | |
return; | |
-# ifdef FEAT_X11 | |
- if (check_connection() == FAIL) | |
- return; | |
-# endif | |
- | |
- if (serverSendReply(server, reply) < 0) | |
+ | |
+ if (cmdsrv_send_notification(serverid, reply) < 0) | |
{ | |
EMSG(_("E258: Unable to send to client")); | |
return; | |
@@ -15754,14 +15705,9 @@ | |
char_u *r = NULL; | |
#ifdef FEAT_CLIENTSERVER | |
-# ifdef WIN32 | |
- r = serverGetVimNames(); | |
-# else | |
- make_connection(); | |
- if (X_DISPLAY != NULL) | |
- r = serverGetVimNames(X_DISPLAY); | |
-# endif | |
-#endif | |
+ cmdsrv_server_list(&r); | |
+#endif | |
+ | |
rettv->v_type = VAR_STRING; | |
rettv->vval.v_string = r; | |
} | |
diff -r 15b934a16641 -r 2082fc32d223 src/ex_docmd.c | |
--- a/src/ex_docmd.c Wed Sep 14 19:04:40 2011 +0200 | |
+++ b/src/ex_docmd.c Thu Sep 15 21:25:11 2011 +0900 | |
@@ -9855,9 +9855,9 @@ | |
break; | |
#if defined(FEAT_CLIENTSERVER) | |
case SPEC_CLIENT: /* Source of last submitted input */ | |
- sprintf((char *)strbuf, PRINTF_HEX_LONG_U, | |
- (long_u)clientWindow); | |
- result = strbuf; | |
+ result = cmdsrv_clientid; | |
+ if (result == NULL) | |
+ result = (char_u *)""; | |
break; | |
#endif | |
} | |
diff -r 15b934a16641 -r 2082fc32d223 src/feature.h | |
--- a/src/feature.h Wed Sep 14 19:04:40 2011 +0200 | |
+++ b/src/feature.h Thu Sep 15 21:25:11 2011 +0900 | |
@@ -1125,7 +1125,7 @@ | |
* +clientserver Remote control via the remote_send() function | |
* and the --remote argument | |
*/ | |
-#if (defined(WIN32) || defined(FEAT_XCLIPBOARD)) && defined(FEAT_EVAL) | |
+#if (defined(WIN32) || defined(UNIX)) && defined(FEAT_EVAL) | |
# define FEAT_CLIENTSERVER | |
#endif | |
diff -r 15b934a16641 -r 2082fc32d223 src/getchar.c | |
--- a/src/getchar.c Wed Sep 14 19:04:40 2011 +0200 | |
+++ b/src/getchar.c Thu Sep 15 21:25:11 2011 +0900 | |
@@ -2954,6 +2954,10 @@ | |
netbeans_parse_messages(); | |
#endif | |
+#if defined(FEAT_CLIENTSERVER) | |
+ cmdsrv_handle_requests(); | |
+#endif | |
+ | |
if (got_int || (script_char = getc(scriptin[curscript])) < 0) | |
{ | |
/* Reached EOF. | |
diff -r 15b934a16641 -r 2082fc32d223 src/globals.h | |
--- a/src/globals.h Wed Sep 14 19:04:40 2011 +0200 | |
+++ b/src/globals.h Thu Sep 15 21:25:11 2011 +0900 | |
@@ -203,11 +203,6 @@ | |
#endif | |
#if (defined(UNIX) || defined(VMS)) && defined(FEAT_X11) | |
EXTERN int x_no_connect INIT(= FALSE); /* don't connect to X server */ | |
-# if defined(FEAT_CLIENTSERVER) | |
-EXTERN int x_force_connect INIT(= FALSE); /* Do connect to X server. | |
- Overrules x_no_connect and | |
- "exclude" in 'clipboard'. */ | |
-# endif | |
#endif | |
EXTERN int ex_keep_indent INIT(= FALSE); /* getexmodeline(): keep indent */ | |
EXTERN int vgetc_busy INIT(= 0); /* when inside vgetc() then > 0 */ | |
@@ -1300,17 +1295,18 @@ | |
#ifdef FEAT_CLIENTSERVER | |
EXTERN char_u *serverName INIT(= NULL); /* name of the server */ | |
-# ifdef FEAT_X11 | |
-EXTERN Window commWindow INIT(= None); | |
-EXTERN Window clientWindow INIT(= None); | |
-EXTERN Atom commProperty INIT(= None); | |
-EXTERN char_u *serverDelayedStartName INIT(= NULL); | |
+EXTERN char_u *cmdsrv_clientid INIT(= NULL); /* Source of last | |
+ submitted input */ | |
+# ifdef WIN3264 | |
+EXTERN HANDLE *cmdsrv_events INIT(= NULL); /* network events */ | |
# else | |
-# ifdef PROTO | |
-typedef int HWND; | |
+EXTERN int cmdsrv_listenfd INIT(= -1); /* server socket */ | |
# endif | |
-EXTERN HWND clientWindow INIT(= 0); | |
-# endif | |
+/* The maximum length of the pending connections queue. | |
+ * For win32, length of cmdsrv_events */ | |
+# define CMDSRV_INSTANCES 5 | |
+/* Temporary server name to receive --remote-wait response */ | |
+# define CMDSRV_TMPNAME "tmp" | |
#endif | |
#if defined(UNIX) || defined(VMS) | |
@@ -1468,6 +1464,7 @@ | |
EXTERN char_u e_noroom[] INIT(= N_("E36: Not enough room")); | |
#endif | |
#ifdef FEAT_CLIENTSERVER | |
+EXTERN char_u e_unabletosend[] INIT(= N_("E241: Unable to send to %s")); | |
EXTERN char_u e_noserver[] INIT(= N_("E247: no registered server named \"%s\"")); | |
#endif | |
EXTERN char_u e_notcreate[] INIT(= N_("E482: Can't create file %s")); | |
diff -r 15b934a16641 -r 2082fc32d223 src/gui.c | |
--- a/src/gui.c Wed Sep 14 19:04:40 2011 +0200 | |
+++ b/src/gui.c Thu Sep 15 21:25:11 2011 +0900 | |
@@ -747,6 +747,10 @@ | |
if (p_ch != 1L) | |
command_height(); | |
+#ifdef FEAT_CLIENTSERVER | |
+ cmdsrv_gui_register(); | |
+#endif | |
+ | |
return; | |
} | |
@@ -766,6 +770,9 @@ | |
gui_exit(rc) | |
int rc; | |
{ | |
+#ifdef FEAT_CLIENTSERVER | |
+ cmdsrv_gui_unregister(); | |
+#endif | |
#ifndef __BEOS__ | |
/* don't free the fonts, it leads to a BUS error | |
* [email protected] Jul 99 */ | |
@@ -4933,6 +4940,10 @@ | |
#ifdef FEAT_NETBEANS_INTG | |
netbeans_gui_register(); | |
#endif | |
+#ifdef FEAT_CLIENTSERVER | |
+ /* TODO: unnecessary? */ | |
+ cmdsrv_gui_register(); | |
+#endif | |
} | |
if (!ends_excmd(*eap->arg)) | |
ex_next(eap); | |
diff -r 15b934a16641 -r 2082fc32d223 src/gui_gtk_x11.c | |
--- a/src/gui_gtk_x11.c Wed Sep 14 19:04:40 2011 +0200 | |
+++ b/src/gui_gtk_x11.c Thu Sep 15 21:25:11 2011 +0900 | |
@@ -629,33 +629,6 @@ | |
return FALSE; | |
} | |
-#ifdef FEAT_CLIENTSERVER | |
-/* | |
- * Handle changes to the "Comm" property | |
- */ | |
- static gint | |
-property_event(GtkWidget *widget, | |
- GdkEventProperty *event, | |
- gpointer data UNUSED) | |
-{ | |
- if (event->type == GDK_PROPERTY_NOTIFY | |
- && event->state == (int)GDK_PROPERTY_NEW_VALUE | |
- && GDK_WINDOW_XWINDOW(event->window) == commWindow | |
- && GET_X_ATOM(event->atom) == commProperty) | |
- { | |
- XEvent xev; | |
- | |
- /* Translate to XLib */ | |
- xev.xproperty.type = PropertyNotify; | |
- xev.xproperty.atom = commProperty; | |
- xev.xproperty.window = commWindow; | |
- xev.xproperty.state = PropertyNewValue; | |
- serverEventProc(GDK_WINDOW_XDISPLAY(widget->window), &xev); | |
- } | |
- return FALSE; | |
-} | |
-#endif | |
- | |
/**************************************************************************** | |
* Focus handlers: | |
@@ -2367,30 +2340,6 @@ | |
if (using_gnome) | |
#endif | |
setup_save_yourself(); | |
- | |
-#ifdef FEAT_CLIENTSERVER | |
- if (serverName == NULL && serverDelayedStartName != NULL) | |
- { | |
- /* This is a :gui command in a plain vim with no previous server */ | |
- commWindow = GDK_WINDOW_XWINDOW(gui.mainwin->window); | |
- | |
- (void)serverRegisterName(GDK_WINDOW_XDISPLAY(gui.mainwin->window), | |
- serverDelayedStartName); | |
- } | |
- else | |
- { | |
- /* | |
- * Cannot handle "XLib-only" windows with gtk event routines, we'll | |
- * have to change the "server" registration to that of the main window | |
- * If we have not registered a name yet, remember the window | |
- */ | |
- serverChangeRegisteredWindow(GDK_WINDOW_XDISPLAY(gui.mainwin->window), | |
- GDK_WINDOW_XWINDOW(gui.mainwin->window)); | |
- } | |
- gtk_widget_add_events(gui.mainwin, GDK_PROPERTY_CHANGE_MASK); | |
- gtk_signal_connect(GTK_OBJECT(gui.mainwin), "property_notify_event", | |
- GTK_SIGNAL_FUNC(property_event), NULL); | |
-#endif | |
} | |
static GdkCursor * | |
@@ -5155,8 +5104,7 @@ | |
} | |
#endif | |
-#if defined(FEAT_CLIENTSERVER) \ | |
- || (defined(FEAT_X11) && defined(FEAT_CLIPBOARD)) || defined(PROTO) | |
+#if (defined(FEAT_X11) && defined(FEAT_CLIPBOARD)) || defined(PROTO) | |
Display * | |
gui_mch_get_display(void) | |
@@ -5431,6 +5379,10 @@ | |
netbeans_parse_messages(); | |
#endif | |
+#if defined(FEAT_CLIENTSERVER) | |
+ cmdsrv_handle_requests(); | |
+#endif | |
+ | |
/* | |
* Loop in GTK+ processing until a timeout or input occurs. | |
* Skip this if input is available anyway (can happen in rare | |
diff -r 15b934a16641 -r 2082fc32d223 src/gui_w48.c | |
--- a/src/gui_w48.c Wed Sep 14 19:04:40 2011 +0200 | |
+++ b/src/gui_w48.c Thu Sep 15 21:25:11 2011 +0900 | |
@@ -301,8 +301,6 @@ | |
static int s_x_pending; | |
static int s_y_pending; | |
static UINT s_kFlags_pending; | |
-static UINT s_wait_timer = 0; /* Timer for get char from user */ | |
-static int s_timed_out = FALSE; | |
static int dead_key = 0; /* 0 - no dead key, 1 - dead key pressed */ | |
#ifdef WIN3264 | |
@@ -460,29 +458,6 @@ | |
*/ | |
/*ARGSUSED*/ | |
- static VOID CALLBACK | |
-_OnTimer( | |
- HWND hwnd, | |
- UINT uMsg, | |
- UINT idEvent, | |
- DWORD dwTime) | |
-{ | |
- MSG msg; | |
- | |
- /* | |
- TRACE2("Got timer event, id %d, s_wait_timer %d\n", idEvent, s_wait_timer); | |
- */ | |
- KillTimer(NULL, idEvent); | |
- s_timed_out = TRUE; | |
- | |
- /* Eat spurious WM_TIMER messages */ | |
- while (pPeekMessage(&msg, hwnd, WM_TIMER, WM_TIMER, PM_REMOVE)) | |
- ; | |
- if (idEvent == s_wait_timer) | |
- s_wait_timer = 0; | |
-} | |
- | |
-/*ARGSUSED*/ | |
static void | |
_OnDeadChar( | |
HWND hwnd, | |
@@ -1960,24 +1935,41 @@ | |
int | |
gui_mch_wait_for_chars(int wtime) | |
{ | |
- MSG msg; | |
int focus; | |
- | |
- s_timed_out = FALSE; | |
+ DWORD starttime; | |
+ DWORD lefttime; | |
+ DWORD waittime; | |
+ DWORD dwwait; | |
+#ifdef FEAT_CLIENTSERVER | |
+ HANDLE wait_objects[CMDSRV_INSTANCES]; | |
+#else | |
+ HANDLE wait_objects[1]; /* dummy */ | |
+#endif | |
+ int wait_nitems = 0; | |
if (wtime > 0) | |
{ | |
/* Don't do anything while processing a (scroll) message. */ | |
if (s_busy_processing) | |
return FAIL; | |
- s_wait_timer = (UINT)SetTimer(NULL, 0, (UINT)wtime, | |
- (TIMERPROC)_OnTimer); | |
} | |
+#ifdef FEAT_CLIENTSERVER | |
+ if (cmdsrv_events != NULL) | |
+ { | |
+ mch_memmove(wait_objects + wait_nitems, cmdsrv_events, | |
+ sizeof(HANDLE) * CMDSRV_INSTANCES); | |
+ wait_nitems += CMDSRV_INSTANCES; | |
+ } | |
+#endif | |
+ | |
allow_scrollbar = TRUE; | |
focus = gui.in_focus; | |
- while (!s_timed_out) | |
+ | |
+ starttime = GetTickCount(); | |
+ | |
+ for (;;) | |
{ | |
/* Stop or start blinking when focus changes */ | |
if (gui.in_focus != focus) | |
@@ -1999,42 +1991,57 @@ | |
s_need_activate = FALSE; | |
} | |
+ /* Process the queued messages. */ | |
+ gui_mch_update(); | |
+ if (input_available()) | |
+ break; | |
+ | |
#ifdef FEAT_NETBEANS_INTG | |
/* Process the queued netbeans messages. */ | |
netbeans_parse_messages(); | |
+ if (input_available()) | |
+ break; | |
#endif | |
- /* | |
- * Don't use gui_mch_update() because then we will spin-lock until a | |
- * char arrives, instead we use GetMessage() to hang until an | |
- * event arrives. No need to check for input_buf_full because we are | |
- * returning as soon as it contains a single char -- webb | |
- */ | |
- process_message(); | |
- | |
+#if defined(FEAT_CLIENTSERVER) | |
+ /* Process the pending clientserver request. */ | |
+ cmdsrv_handle_requests(); | |
if (input_available()) | |
- { | |
- if (s_wait_timer != 0 && !s_timed_out) | |
- { | |
- KillTimer(NULL, s_wait_timer); | |
- | |
- /* Eat spurious WM_TIMER messages */ | |
- while (pPeekMessage(&msg, s_hwnd, WM_TIMER, WM_TIMER, PM_REMOVE)) | |
- ; | |
- s_wait_timer = 0; | |
- } | |
- allow_scrollbar = FALSE; | |
- | |
- /* Clear pending mouse button, the release event may have been | |
- * taken by the dialog window. But don't do this when getting | |
- * focus, we need the mouse-up event then. */ | |
- if (!s_getting_focus) | |
- s_button_pending = -1; | |
- | |
- return OK; | |
- } | |
+ break; | |
+#endif | |
+ | |
+ lefttime = GetTickCount() - starttime; | |
+ if (wtime == 0) | |
+ break; | |
+ else if (wtime > 0 && (DWORD)wtime <= lefttime) | |
+ break; | |
+ | |
+ /* Wait messages and objects. */ | |
+ if (wtime < 0) | |
+ waittime = INFINITE; | |
+ else | |
+ waittime = wtime - lefttime; | |
+ dwwait = MsgWaitForMultipleObjects(wait_nitems, wait_objects, | |
+ FALSE, waittime, QS_ALLINPUT); | |
+ if (dwwait == WAIT_FAILED) | |
+ break; | |
+ else if (dwwait == WAIT_TIMEOUT) | |
+ break; | |
} | |
+ | |
allow_scrollbar = FALSE; | |
+ | |
+ if (input_available()) | |
+ { | |
+ /* Clear pending mouse button, the release event may have been | |
+ * taken by the dialog window. But don't do this when getting | |
+ * focus, we need the mouse-up event then. */ | |
+ if (!s_getting_focus) | |
+ s_button_pending = -1; | |
+ | |
+ return OK; | |
+ } | |
+ | |
return FAIL; | |
} | |
diff -r 15b934a16641 -r 2082fc32d223 src/gui_x11.c | |
--- a/src/gui_x11.c Wed Sep 14 19:04:40 2011 +0200 | |
+++ b/src/gui_x11.c Thu Sep 15 21:25:11 2011 +0900 | |
@@ -149,9 +149,6 @@ | |
static void gui_x11_sniff_request_cb __ARGS((XtPointer closure, int *source, XtInputId *id)); | |
#endif | |
static void gui_x11_check_copy_area __ARGS((void)); | |
-#ifdef FEAT_CLIENTSERVER | |
-static void gui_x11_send_event_handler __ARGS((Widget, XtPointer, XEvent *, Boolean *)); | |
-#endif | |
static void gui_x11_wm_protocol_handler __ARGS((Widget, XtPointer, XEvent *, Boolean *)); | |
static void gui_x11_blink_cb __ARGS((XtPointer timed_out, XtIntervalId *interval_id)); | |
static Cursor gui_x11_create_blank_mouse __ARGS((void)); | |
@@ -1643,27 +1640,6 @@ | |
(XtPointer)NULL); | |
#endif | |
-#ifdef FEAT_CLIENTSERVER | |
- if (serverName == NULL && serverDelayedStartName != NULL) | |
- { | |
- /* This is a :gui command in a plain vim with no previous server */ | |
- commWindow = XtWindow(vimShell); | |
- (void)serverRegisterName(gui.dpy, serverDelayedStartName); | |
- } | |
- else | |
- { | |
- /* | |
- * Cannot handle "widget-less" windows with XtProcessEvent() we'll | |
- * have to change the "server" registration to that of the main window | |
- * If we have not registered a name yet, remember the window | |
- */ | |
- serverChangeRegisteredWindow(gui.dpy, XtWindow(vimShell)); | |
- } | |
- XtAddEventHandler(vimShell, PropertyChangeMask, False, | |
- gui_x11_send_event_handler, NULL); | |
-#endif | |
- | |
- | |
#if defined(FEAT_MENU) && defined(FEAT_GUI_ATHENA) | |
/* The Athena GUI needs this again after opening the window */ | |
gui_position_menu(); | |
@@ -2900,6 +2876,10 @@ | |
netbeans_parse_messages(); | |
#endif | |
+#if defined(FEAT_CLIENTSERVER) | |
+ cmdsrv_handle_requests(); | |
+#endif | |
+ | |
/* | |
* Don't use gui_mch_update() because then we will spin-lock until a | |
* char arrives, instead we use XtAppProcessEvent() to hang until an | |
@@ -3183,27 +3163,6 @@ | |
gui_shell_closed(); | |
} | |
-#ifdef FEAT_CLIENTSERVER | |
-/* | |
- * Function called when property changed. Check for incoming commands | |
- */ | |
- static void | |
-gui_x11_send_event_handler(w, client_data, event, dum) | |
- Widget w UNUSED; | |
- XtPointer client_data UNUSED; | |
- XEvent *event; | |
- Boolean *dum UNUSED; | |
-{ | |
- XPropertyEvent *e = (XPropertyEvent *) event; | |
- | |
- if (e->type == PropertyNotify && e->window == commWindow | |
- && e->atom == commProperty && e->state == PropertyNewValue) | |
- { | |
- serverEventProc(gui.dpy, event); | |
- } | |
-} | |
-#endif | |
- | |
/* | |
* Cursor blink functions. | |
* | |
diff -r 15b934a16641 -r 2082fc32d223 src/if_cmdsrv.c | |
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 | |
+++ b/src/if_cmdsrv.c Thu Sep 15 21:25:11 2011 +0900 | |
@@ -0,0 +1,1198 @@ | |
+/* | |
+ * TODO: | |
+ * document | |
+ * | |
+ * error message | |
+ * | |
+ * Use inet socket? | |
+ * | |
+ * test | |
+ * | |
+ * unix: How to automatically remove socket file which is not listened | |
+ * (i.e. vim crashed)? To test with connect() makes many connections | |
+ * and it may cause EAGAIN error with following connect(). No need to | |
+ * do it? | |
+ * | |
+ * | |
+ * Changed: | |
+ * | |
+ * - unix: use unix domain socket. | |
+ * /tmp/vim-cmdsrv-<uid>/<serverid> | |
+ * | |
+ * - win32: use named pipe. | |
+ * \\.\pipe\vim-cmdsrv-<serverid> | |
+ * | |
+ * - expand('<client>') returns server name instead of number. | |
+ * | |
+ */ | |
+ | |
+/* for struct ucred */ | |
+#ifdef __linux__ | |
+# define _GNU_SOURCE | |
+#endif | |
+ | |
+#include "vim.h" | |
+#include "version.h" | |
+ | |
+#if defined(FEAT_CLIENTSERVER) || defined(PROTO) | |
+ | |
+#ifdef WIN3264 | |
+# include "if_cmdsrv_win32.c" | |
+#else | |
+# include "if_cmdsrv_unix.c" | |
+#endif | |
+ | |
+typedef struct | |
+{ | |
+ char_u *type; | |
+ char_u *sender; | |
+ char_u *script; | |
+ char_u *reply; | |
+ char_u *code; | |
+ char_u *encoding; | |
+ int ncode; | |
+} cmdsrv_message_T; | |
+ | |
+typedef struct | |
+{ | |
+ char_u *serverid; | |
+ garray_T strings; | |
+} cmdsrv_server_reply_T; | |
+ | |
+static garray_T cmdsrv_reply = GA_EMPTY; | |
+ | |
+static int cmdsrv_is_serial_name(char_u *name); | |
+static cmdsrv_server_reply_T *cmdsrv_reply_find(char_u *serverid); | |
+static int cmdsrv_reply_add(char_u *serverid, char_u *str); | |
+static int cmdsrv_reply_delete(char_u *serverid); | |
+static int cmdsrv_msg_keys(char_u *keys, char_u **abuf, size_t *abufsize); | |
+static int cmdsrv_msg_expr(char_u *expr, char_u **abuf, size_t *abufsize); | |
+static int cmdsrv_msg_reply(char_u *reply, int code, char_u **abuf, size_t *abufsize); | |
+static int cmdsrv_msg_notification(char_u *reply, char_u **abuf, size_t *abufsize); | |
+static int cmdsrv_parse_message(char_u *msg, cmdsrv_message_T *result); | |
+static int cmdsrv_receive_keys(cmdsrv_handle_t conn, cmdsrv_message_T *pmsg); | |
+static int cmdsrv_receive_expr(cmdsrv_handle_t conn, cmdsrv_message_T *pmsg); | |
+static int cmdsrv_receive_notification(cmdsrv_handle_t conn, cmdsrv_message_T *pmsg); | |
+static char_u *cmdsrv_convert(char_u *client_enc, char_u *data); | |
+ | |
+static cmdsrv_handle_t cmdsrv_server = NULL; | |
+ | |
+#ifdef FEAT_GUI | |
+# define CMDSRV_HAS_GUI (gui.in_use || gui.starting) | |
+# if defined(FEAT_GUI_X11) | |
+# define CMDSRV_OPEN (cmdsrv_listenfd != -1) | |
+static void cmdsrv_message_from_client(XtPointer clientData, int *unused1, XtInputId *unused2); | |
+static XtInputId inputHandler = (XtInputId)NULL; | |
+# elif defined(FEAT_GUI_GTK) | |
+# define CMDSRV_OPEN (cmdsrv_listenfd != -1) | |
+static void cmdsrv_message_from_client(gpointer clientData, gint unused1, GdkInputCondition unused2); | |
+static gint inputHandler = 0; | |
+# elif defined(FEAT_GUI_W32) | |
+# define CMDSRV_OPEN (cmdsrv_events != NULL) | |
+# endif | |
+#endif | |
+ | |
+#define CMDSRV_SEND_MSEC_POLL 50 | |
+ | |
+int | |
+cmdsrv_init(void) | |
+{ | |
+ ga_init2(&cmdsrv_reply, sizeof(cmdsrv_server_reply_T), 1); | |
+ | |
+ return 0; | |
+} | |
+ | |
+int | |
+cmdsrv_uninit(void) | |
+{ | |
+ cmdsrv_server_reply_T *p; | |
+ int i; | |
+ | |
+ if (cmdsrv_server == NULL) | |
+ return 0; | |
+ | |
+ vim_free(serverName); | |
+ serverName = NULL; | |
+ | |
+ vim_free(cmdsrv_clientid); | |
+ cmdsrv_clientid = NULL; | |
+ | |
+ if (cmdsrv_serv_close(cmdsrv_server) != 0) | |
+ return -1; | |
+ | |
+ cmdsrv_server = NULL; | |
+ | |
+ p = (cmdsrv_server_reply_T *)cmdsrv_reply.ga_data; | |
+ for (i = 0; i < cmdsrv_reply.ga_len; ++i) | |
+ { | |
+ vim_free(p[i].serverid); | |
+ ga_clear(&p[i].strings); | |
+ } | |
+ ga_clear(&cmdsrv_reply); | |
+ | |
+ return 0; | |
+} | |
+ | |
+#ifdef FEAT_GUI | |
+int | |
+cmdsrv_gui_register(void) | |
+{ | |
+ if (!CMDSRV_HAS_GUI || !CMDSRV_OPEN) | |
+ return 0; | |
+ | |
+#if defined(FEAT_GUI_X11) | |
+ if (inputHandler == (XtInputId)NULL) | |
+ { | |
+ inputHandler = XtAppAddInput( | |
+ (XtAppContext)app_context, | |
+ cmdsrv_listenfd, | |
+ (XtPointer)(XtInputReadMask + XtInputExceptMask), | |
+ cmdsrv_message_from_client, | |
+ NULL); | |
+ } | |
+#elif defined(FEAT_GUI_GTK) | |
+ if (inputHandler == 0) | |
+ { | |
+ inputHandler = gdk_input_add( | |
+ (gint)cmdsrv_listenfd, | |
+ (GdkInputCondition)((int)GDK_INPUT_READ | |
+ + (int)GDK_INPUT_EXCEPTION), | |
+ cmdsrv_message_from_client, | |
+ NULL); | |
+ } | |
+#endif | |
+ | |
+ return 0; | |
+} | |
+ | |
+int | |
+cmdsrv_gui_unregister(void) | |
+{ | |
+#if defined(FEAT_GUI_X11) | |
+ if (inputHandler != (XtInputId)NULL) | |
+ { | |
+ XtRemoveInput(inputHandler); | |
+ inputHandler = (XtInputId)NULL; | |
+ } | |
+#elif defined(FEAT_GUI_GTK) | |
+ if (inputHandler != 0) | |
+ { | |
+ gdk_input_remove(inputHandler); | |
+ inputHandler = 0; | |
+ } | |
+#endif | |
+ | |
+ return 0; | |
+} | |
+#endif | |
+ | |
+int | |
+cmdsrv_register_name(char_u *name) | |
+{ | |
+ cmdsrv_handle_t server = NULL; | |
+ char_u *serverid = NULL; | |
+ int i; | |
+ | |
+ serverid = alloc(STRLEN(name) + 3 + 1); /* name + 1-999 + NUL */ | |
+ if (serverid == NULL) | |
+ return -1; | |
+ | |
+ STRCPY(serverid, name); | |
+ | |
+ if (cmdsrv_serv_listen(serverid, &server) != 0) | |
+ { | |
+ /* Specified name is not available. Try to register with postfix. */ | |
+ /* XXX: For backward compatibility, try loosename even if the | |
+ * specified name is serial name (name[0-999]). */ | |
+ for (i = 1; i < 1000; ++i) | |
+ { | |
+ sprintf((char *)serverid, "%s%d", name, i); | |
+ if (cmdsrv_serv_listen(serverid, &server) == 0) | |
+ break; | |
+ } | |
+ } | |
+ | |
+ if (server == NULL) | |
+ { | |
+ MSG_ATTR(_("Unable to register a command server name"), | |
+ hl_attr(HLF_W)); | |
+ vim_free(serverid); | |
+ return -1; | |
+ } | |
+ | |
+ serverName = strup_save(serverid); | |
+ if (serverName == NULL) | |
+ { | |
+ vim_free(serverid); | |
+ cmdsrv_serv_close(server); | |
+ return -1; | |
+ } | |
+ | |
+#ifdef FEAT_EVAL | |
+ set_vim_var_string(VV_SEND_SERVER, serverName, -1); | |
+#endif | |
+ | |
+#ifdef FEAT_TITLE | |
+ need_maketitle = TRUE; | |
+#endif | |
+ | |
+ vim_free(serverid); | |
+ | |
+ cmdsrv_server = server; | |
+ | |
+ return 0; | |
+} | |
+ | |
+int | |
+cmdsrv_server_list(char_u **result) | |
+{ | |
+ garray_T ga; | |
+ char_u *list; | |
+ char_u *p; | |
+ | |
+ list = cmdsrv_list(NUL); | |
+ if (list == NULL) | |
+ return -1; | |
+ | |
+ ga_init2(&ga, (int)sizeof(char), 100); | |
+ | |
+ /* filter temporary server name */ | |
+ for (p = list; *p != NUL; p += STRLEN(p) + 1) | |
+ { | |
+ if (STRNICMP(p, CMDSRV_TMPNAME, STRLEN(CMDSRV_TMPNAME)) != 0) | |
+ { | |
+ ga_concat(&ga, p); | |
+ ga_append(&ga, '\n'); | |
+ } | |
+ } | |
+ | |
+ ga_append(&ga, NUL); | |
+ | |
+ vim_free(list); | |
+ | |
+ *result = ga.ga_data; | |
+ | |
+ return 0; | |
+} | |
+ | |
+/* | |
+ * @param timeoutmsec -1 infinit, 0 nowait, >0 millisecond | |
+ * @return -1 error, 0 timeout, 1 received (not handled yet) | |
+ */ | |
+int | |
+cmdsrv_wait_request(int timeoutmsec) | |
+{ | |
+ if (cmdsrv_server == NULL) | |
+ return -1; | |
+ return cmdsrv_serv_wait(cmdsrv_server, timeoutmsec); | |
+} | |
+ | |
+int | |
+cmdsrv_handle_requests() | |
+{ | |
+ cmdsrv_handle_t conn; | |
+ char_u *abuf; | |
+ size_t abufsize; | |
+ cmdsrv_message_T msg; | |
+ int n; | |
+ | |
+ if (cmdsrv_server == NULL) | |
+ return -1; | |
+ | |
+ for (;;) | |
+ { | |
+ if (got_int) | |
+ return -1; | |
+ | |
+ n = cmdsrv_serv_wait(cmdsrv_server, 0); | |
+ if (n < 0) | |
+ return -1; | |
+ if (n == 0) | |
+ return 0; | |
+ | |
+ if (cmdsrv_serv_accept(cmdsrv_server, &conn) != 0) | |
+ continue; | |
+ | |
+ if (cmdsrv_read_message(conn, (void **)&abuf, &abufsize) != 0) | |
+ { | |
+ cmdsrv_conn_close(conn); | |
+ continue; | |
+ } | |
+ | |
+ if (cmdsrv_parse_message(abuf, &msg) != 0) | |
+ { | |
+ cmdsrv_conn_close(conn); | |
+ continue; | |
+ } | |
+ | |
+ if (STRICMP(msg.type, "keys") == 0) | |
+ { | |
+ cmdsrv_receive_keys(conn, &msg); | |
+ } | |
+ else if (STRICMP(msg.type, "expr") == 0) | |
+ { | |
+ cmdsrv_receive_expr(conn, &msg); | |
+ } | |
+ else if (STRICMP(msg.type, "notification") == 0) | |
+ { | |
+ cmdsrv_receive_notification(conn, &msg); | |
+ } | |
+ | |
+ vim_free(abuf); | |
+ | |
+ cmdsrv_conn_close(conn); | |
+ } | |
+} | |
+ | |
+int | |
+cmdsrv_send_keys(char_u *serverid, char_u *keys) | |
+{ | |
+ cmdsrv_handle_t conn; | |
+ char_u *abuf; | |
+ size_t abufsize; | |
+ | |
+ if (serverName != NULL && STRICMP(serverid, serverName) == 0) | |
+ { | |
+ server_to_input_buf(keys); | |
+ return 0; | |
+ } | |
+ | |
+ if (cmdsrv_cli_conn(serverid, &conn) != 0) | |
+ { | |
+ EMSG(_("E248: Failed to send command to the destination program")); | |
+ return -1; | |
+ } | |
+ | |
+ if (cmdsrv_msg_keys(keys, &abuf, &abufsize) != 0) | |
+ { | |
+ cmdsrv_conn_close(conn); | |
+ return -1; | |
+ } | |
+ | |
+ if (cmdsrv_write_message(conn, abuf, abufsize) != 0) | |
+ { | |
+ EMSG(_("E248: Failed to send command to the destination program")); | |
+ vim_free(abuf); | |
+ cmdsrv_conn_close(conn); | |
+ return -1; | |
+ } | |
+ | |
+ vim_free(abuf); | |
+ | |
+ if (cmdsrv_conn_close(conn) != 0) | |
+ { | |
+ EMSG(_("Exxx: close error")); | |
+ return -1; | |
+ } | |
+ | |
+ return 0; | |
+} | |
+ | |
+int | |
+cmdsrv_send_expr(char_u *serverid, char_u *expr, char_u **result) | |
+{ | |
+ cmdsrv_handle_t conn; | |
+ char_u *abuf; | |
+ size_t abufsize; | |
+ cmdsrv_message_T msg; | |
+ char_u *reply; | |
+ char_u *exprbuf; | |
+ | |
+ if (serverName != NULL && STRICMP(serverid, serverName) == 0) | |
+ { | |
+ /* Use allocated buffer for string literal because eval may | |
+ * modify expr temporarily. */ | |
+ exprbuf = vim_strsave(expr); | |
+ if (exprbuf == NULL) | |
+ return -1; | |
+ reply = eval_client_expr_to_string(exprbuf); | |
+ vim_free(exprbuf); | |
+ if (result != NULL) | |
+ { | |
+ if (reply == NULL) | |
+ *result = vim_strsave((char_u *)_(e_invexprmsg)); | |
+ else | |
+ *result = reply; | |
+ } | |
+ else | |
+ vim_free(reply); | |
+ return reply == NULL ? -1 : 0; | |
+ } | |
+ | |
+ if (cmdsrv_cli_conn(serverid, &conn) != 0) | |
+ { | |
+ EMSG(_("E248: Failed to send command to the destination program")); | |
+ return -1; | |
+ } | |
+ | |
+ if (cmdsrv_msg_expr(expr, &abuf, &abufsize) != 0) | |
+ { | |
+ cmdsrv_conn_close(conn); | |
+ return -1; | |
+ } | |
+ | |
+ if (cmdsrv_write_message(conn, abuf, abufsize) != 0) | |
+ { | |
+ EMSG(_("E248: Failed to send command to the destination program")); | |
+ vim_free(abuf); | |
+ cmdsrv_conn_close(conn); | |
+ return -1; | |
+ } | |
+ | |
+ vim_free(abuf); | |
+ | |
+ if (cmdsrv_read_message(conn, (void **)&abuf, &abufsize) != 0) | |
+ { | |
+ EMSG(_("Exxx: Failed to receive result")); | |
+ cmdsrv_conn_close(conn); | |
+ return -1; | |
+ } | |
+ | |
+ if (cmdsrv_parse_message(abuf, &msg) != 0 | |
+ || STRICMP(msg.type, "reply") != 0) | |
+ { | |
+ EMSG(_("Exxx: Failed to parse result")); | |
+ vim_free(abuf); | |
+ cmdsrv_conn_close(conn); | |
+ return -1; | |
+ } | |
+ | |
+ if (cmdsrv_conn_close(conn) != 0) | |
+ { | |
+ EMSG(_("Exxx: close error")); | |
+ vim_free(abuf); | |
+ return -1; | |
+ } | |
+ | |
+ if (result != NULL) | |
+ *result = cmdsrv_convert(msg.encoding, msg.reply); | |
+ | |
+ vim_free(abuf); | |
+ | |
+ return msg.ncode == 0 ? 0 : -1; | |
+} | |
+ | |
+int | |
+cmdsrv_send_notification(char_u *serverid, char_u *reply) | |
+{ | |
+ cmdsrv_handle_t conn; | |
+ char_u *abuf; | |
+ size_t abufsize; | |
+ | |
+ if (cmdsrv_cli_conn(serverid, &conn) != 0) | |
+ { | |
+ EMSG(_("E248: Failed to send command to the destination program")); | |
+ return -1; | |
+ } | |
+ | |
+ if (cmdsrv_msg_notification(reply, &abuf, &abufsize) != 0) | |
+ { | |
+ cmdsrv_conn_close(conn); | |
+ return -1; | |
+ } | |
+ | |
+ if (cmdsrv_write_message(conn, abuf, abufsize) != 0) | |
+ { | |
+ EMSG(_("E248: Failed to send command to the destination program")); | |
+ vim_free(abuf); | |
+ cmdsrv_conn_close(conn); | |
+ return -1; | |
+ } | |
+ | |
+ vim_free(abuf); | |
+ | |
+ if (cmdsrv_conn_close(conn) != 0) | |
+ { | |
+ EMSG(_("Exxx: close error")); | |
+ return -1; | |
+ } | |
+ | |
+ return 0; | |
+} | |
+ | |
+int | |
+cmdsrv_foreground(char_u *serverid) | |
+{ | |
+#ifdef WIN3264 | |
+ HWND hWnd; | |
+ char_u *windowidstr; | |
+ long windowid; | |
+ char_u *endp; | |
+ | |
+ if (cmdsrv_send_expr(serverid, "v:windowid", &windowidstr) != 0) | |
+ return -1; | |
+ | |
+ if (windowidstr == NULL) | |
+ return -1; | |
+ | |
+ errno = 0; | |
+ windowid = strtol(windowidstr, (char **)&endp, 10); | |
+ if (errno != 0 || *endp != NUL || endp == windowidstr) | |
+ { | |
+ vim_free(windowidstr); | |
+ return -1; | |
+ } | |
+ | |
+ if (windowid == 0) | |
+ return -1; | |
+ | |
+ hWnd = (HWND)LongToHandle(windowid); | |
+ | |
+ SetForegroundWindow(hWnd); | |
+ | |
+ return 0; | |
+#else | |
+ return cmdsrv_send_expr(serverid, "foreground()", NULL); | |
+#endif | |
+} | |
+ | |
+int | |
+cmdsrv_peek_reply(char_u *serverid, char_u **pstr) | |
+{ | |
+ cmdsrv_server_reply_T *p; | |
+ | |
+ p = cmdsrv_reply_find(serverid); | |
+ if (p != NULL && p->strings.ga_len > 0) | |
+ *pstr = (char_u *)p->strings.ga_data; | |
+ else | |
+ *pstr = NULL; | |
+ | |
+ return 0; | |
+} | |
+ | |
+int | |
+cmdsrv_read_reply(char_u *serverid, char_u **pstr) | |
+{ | |
+ cmdsrv_server_reply_T *p; | |
+ char_u *s; | |
+ int len; | |
+ | |
+ p = cmdsrv_reply_find(serverid); | |
+ if (p != NULL && p->strings.ga_len > 0) | |
+ { | |
+ *pstr = vim_strsave(p->strings.ga_data); | |
+ len = STRLEN(*pstr) + 1; | |
+ if (len < p->strings.ga_len) | |
+ { | |
+ s = (char_u *)p->strings.ga_data; | |
+ mch_memmove(s, s + len, p->strings.ga_len - len); | |
+ p->strings.ga_len -= len; | |
+ } | |
+ else | |
+ { | |
+ cmdsrv_reply_delete(serverid); | |
+ } | |
+ return 0; | |
+ } | |
+ | |
+ return -1; | |
+} | |
+ | |
+int | |
+cmdsrv_wait_reply(char_u *serverid, char_u **pstr) | |
+{ | |
+ char_u *exists; | |
+ | |
+ while (!got_int) | |
+ { | |
+ exists = cmdsrv_find_server(serverid, FALSE); | |
+ if (exists == NULL) | |
+ return -1; | |
+ vim_free(exists); | |
+ | |
+ if (cmdsrv_peek_reply(serverid, pstr) != 0) | |
+ return -1; | |
+ | |
+ if (*pstr != NULL) | |
+ { | |
+ if (cmdsrv_read_reply(serverid, pstr) != 0) | |
+ return -1; | |
+ return 0; | |
+ } | |
+ | |
+ if (cmdsrv_wait_request(CMDSRV_SEND_MSEC_POLL) < 0) | |
+ return -1; | |
+ | |
+ if (cmdsrv_handle_requests() != 0) | |
+ return -1; | |
+ } | |
+ | |
+ return -1; | |
+} | |
+ | |
+char_u * | |
+cmdsrv_find_server(char_u *name, int loose) | |
+{ | |
+ char_u *list; | |
+ char_u *res; | |
+ char_u *p; | |
+ char_u *e; | |
+ size_t len; | |
+ | |
+ list = cmdsrv_list(NUL); | |
+ if (list == NULL) | |
+ return NULL; | |
+ | |
+ for (p = list; *p != NUL; p += STRLEN(p) + 1) | |
+ { | |
+ if (STRICMP(p, name) == 0) | |
+ { | |
+ res = vim_strsave(p); | |
+ vim_free(list); | |
+ return res; | |
+ } | |
+ } | |
+ | |
+ if (!loose || cmdsrv_is_serial_name(name)) | |
+ return NULL; | |
+ | |
+ len = STRLEN(name); | |
+ | |
+ for (p = list; *p != NUL; p += STRLEN(p) + 1) | |
+ { | |
+ if (STRNICMP(p, name, len) == 0) | |
+ { | |
+ e = skipdigits(p + len); | |
+ if (*e == NUL) | |
+ { | |
+ res = vim_strsave(p); | |
+ vim_free(list); | |
+ return res; | |
+ } | |
+ } | |
+ } | |
+ | |
+ return NULL; | |
+} | |
+ | |
+/* | |
+ * @return percent-encoded string in allocated memory, NULL for error. | |
+ */ | |
+char_u * | |
+cmdsrv_urlencode(char_u *str) | |
+{ | |
+ char_u *buf; | |
+ char_u *s; | |
+ char_u *d; | |
+ | |
+ buf = (char_u *)lalloc_clear((STRLEN(str) * 3) + 1, TRUE); | |
+ if (buf == NULL) | |
+ return NULL; | |
+ | |
+ d = buf; | |
+ s = str; | |
+ while (*s != NUL) | |
+ { | |
+ if (ASCII_ISALPHA(*s) || VIM_ISDIGIT(*s) | |
+ || *s == '-' || *s == '.' || *s == '_' || *s == '~') | |
+ *d++ = *s++; | |
+ else | |
+ d += sprintf(d, "%%%02x", *s++); | |
+ } | |
+ *d = NUL; | |
+ | |
+ return buf; | |
+} | |
+ | |
+ | |
+/* | |
+ * @return percent-decoded string in allocated memory, NULL for error. | |
+ */ | |
+char_u * | |
+cmdsrv_urldecode(char_u *str) | |
+{ | |
+ char_u *buf; | |
+ char_u *s; | |
+ char_u *d; | |
+ int c; | |
+ | |
+ buf = (char_u *)lalloc_clear(STRLEN(str) + 1, TRUE); | |
+ if (buf == NULL) | |
+ return NULL; | |
+ | |
+ d = buf; | |
+ s = str; | |
+ while (*s != NUL) | |
+ { | |
+ if (*s == '%') | |
+ { | |
+ c = hexhex2nr(&s[1]); | |
+ if (c == -1) | |
+ { | |
+ vim_free(buf); | |
+ return NULL; | |
+ } | |
+ *d++ = c; | |
+ s += 3; | |
+ } | |
+ else | |
+ { | |
+ *d++ = *s++; | |
+ } | |
+ } | |
+ *d = NUL; | |
+ | |
+ return buf; | |
+} | |
+ | |
+static int | |
+cmdsrv_is_serial_name(char_u *name) | |
+{ | |
+ size_t len; | |
+ | |
+ len = STRLEN(name); | |
+ if (len > 1 && vim_isdigit(name[len - 1])) | |
+ return TRUE; | |
+ return FALSE; | |
+} | |
+ | |
+static cmdsrv_server_reply_T * | |
+cmdsrv_reply_find(char_u *serverid) | |
+{ | |
+ cmdsrv_server_reply_T *p; | |
+ int i; | |
+ | |
+ p = (cmdsrv_server_reply_T *)cmdsrv_reply.ga_data; | |
+ for (i = 0; i < cmdsrv_reply.ga_len; ++i) | |
+ { | |
+ if (STRICMP(p[i].serverid, serverid) == 0) | |
+ return &p[i]; | |
+ } | |
+ | |
+ return NULL; | |
+} | |
+ | |
+static int | |
+cmdsrv_reply_add(char_u *serverid, char_u *str) | |
+{ | |
+ cmdsrv_server_reply_T *p; | |
+ | |
+ p = cmdsrv_reply_find(serverid); | |
+ | |
+ if (p == NULL) | |
+ { | |
+ if (ga_grow(&cmdsrv_reply, 1) != OK) | |
+ return -1; | |
+ p = ((cmdsrv_server_reply_T *)cmdsrv_reply.ga_data) | |
+ + cmdsrv_reply.ga_len; | |
+ p->serverid = vim_strsave(serverid); | |
+ if (p->serverid == NULL) | |
+ return -1; | |
+ ga_init2(&p->strings, 1, 100); | |
+ cmdsrv_reply.ga_len++; | |
+ } | |
+ | |
+ ga_concat(&p->strings, str); | |
+ ga_append(&p->strings, NUL); | |
+ | |
+ return 0; | |
+} | |
+ | |
+static int | |
+cmdsrv_reply_delete(char_u *serverid) | |
+{ | |
+ cmdsrv_server_reply_T *p; | |
+ cmdsrv_server_reply_T *e; | |
+ | |
+ p = cmdsrv_reply_find(serverid); | |
+ | |
+ if (p != NULL) | |
+ { | |
+ e = ((cmdsrv_server_reply_T *)cmdsrv_reply.ga_data) | |
+ + cmdsrv_reply.ga_len; | |
+ ga_clear(&p->strings); | |
+ mch_memmove(p, p + 1, (e - (p + 1)) * sizeof(*p)); | |
+ cmdsrv_reply.ga_len--; | |
+ } | |
+ | |
+ return 0; | |
+} | |
+ | |
+static int | |
+cmdsrv_msg_keys(char_u *keys, char_u **abuf, size_t *abufsize) | |
+{ | |
+ garray_T ga; | |
+ char_u *sender; | |
+ | |
+ sender = serverName; | |
+ if (sender == NULL) | |
+ sender = (char_u *)""; | |
+ | |
+ ga_init2(&ga, (int)sizeof(char), 100); | |
+ | |
+ ga_concat(&ga, "type"); | |
+ ga_append(&ga, NUL); | |
+ ga_concat(&ga, "keys"); | |
+ ga_append(&ga, NUL); | |
+ | |
+ ga_concat(&ga, "sender"); | |
+ ga_append(&ga, NUL); | |
+ ga_concat(&ga, sender); | |
+ ga_append(&ga, NUL); | |
+ | |
+ ga_concat(&ga, "script"); | |
+ ga_append(&ga, NUL); | |
+ ga_concat(&ga, keys); | |
+ ga_append(&ga, NUL); | |
+ | |
+#ifdef FEAT_MBYTE | |
+ ga_concat(&ga, "encoding"); | |
+ ga_append(&ga, NUL); | |
+ ga_concat(&ga, p_enc); | |
+ ga_append(&ga, NUL); | |
+#endif | |
+ | |
+ ga_append(&ga, NUL); | |
+ | |
+ *abuf = ga.ga_data; | |
+ *abufsize = ga.ga_len; | |
+ | |
+ return 0; | |
+} | |
+ | |
+static int | |
+cmdsrv_msg_expr(char_u *expr, char_u **abuf, size_t *abufsize) | |
+{ | |
+ garray_T ga; | |
+ char_u *sender; | |
+ | |
+ sender = serverName; | |
+ if (sender == NULL) | |
+ sender = (char_u *)""; | |
+ | |
+ ga_init2(&ga, (int)sizeof(char), 100); | |
+ | |
+ ga_concat(&ga, "type"); | |
+ ga_append(&ga, NUL); | |
+ ga_concat(&ga, "expr"); | |
+ ga_append(&ga, NUL); | |
+ | |
+ ga_concat(&ga, "sender"); | |
+ ga_append(&ga, NUL); | |
+ ga_concat(&ga, sender); | |
+ ga_append(&ga, NUL); | |
+ | |
+ ga_concat(&ga, "script"); | |
+ ga_append(&ga, NUL); | |
+ ga_concat(&ga, expr); | |
+ ga_append(&ga, NUL); | |
+ | |
+#ifdef FEAT_MBYTE | |
+ ga_concat(&ga, "encoding"); | |
+ ga_append(&ga, NUL); | |
+ ga_concat(&ga, p_enc); | |
+ ga_append(&ga, NUL); | |
+#endif | |
+ | |
+ ga_append(&ga, NUL); | |
+ | |
+ *abuf = ga.ga_data; | |
+ *abufsize = ga.ga_len; | |
+ | |
+ return 0; | |
+} | |
+ | |
+static int | |
+cmdsrv_msg_reply(char_u *reply, int code, char_u **abuf, size_t *abufsize) | |
+{ | |
+ garray_T ga; | |
+ char_u *sender; | |
+ char codestr[NUMBUFLEN]; | |
+ | |
+ sender = serverName; | |
+ if (sender == NULL) | |
+ sender = (char_u *)""; | |
+ | |
+ vim_snprintf(codestr, sizeof(codestr), "%d", code); | |
+ | |
+ ga_init2(&ga, (int)sizeof(char), 100); | |
+ | |
+ ga_concat(&ga, "type"); | |
+ ga_append(&ga, NUL); | |
+ ga_concat(&ga, "reply"); | |
+ ga_append(&ga, NUL); | |
+ | |
+ ga_concat(&ga, "sender"); | |
+ ga_append(&ga, NUL); | |
+ ga_concat(&ga, sender); | |
+ ga_append(&ga, NUL); | |
+ | |
+ ga_concat(&ga, "reply"); | |
+ ga_append(&ga, NUL); | |
+ ga_concat(&ga, reply); | |
+ ga_append(&ga, NUL); | |
+ | |
+ ga_concat(&ga, "code"); | |
+ ga_append(&ga, NUL); | |
+ ga_concat(&ga, codestr); | |
+ ga_append(&ga, NUL); | |
+ | |
+#ifdef FEAT_MBYTE | |
+ ga_concat(&ga, "encoding"); | |
+ ga_append(&ga, NUL); | |
+ ga_concat(&ga, p_enc); | |
+ ga_append(&ga, NUL); | |
+#endif | |
+ | |
+ ga_append(&ga, NUL); | |
+ | |
+ *abuf = ga.ga_data; | |
+ *abufsize = ga.ga_len; | |
+ | |
+ return 0; | |
+} | |
+ | |
+static int | |
+cmdsrv_msg_notification(char_u *reply, char_u **abuf, size_t *abufsize) | |
+{ | |
+ char_u *sender; | |
+ garray_T ga; | |
+ | |
+ sender = serverName; | |
+ if (sender == NULL) | |
+ sender = (char_u *)""; | |
+ | |
+ ga_init2(&ga, (int)sizeof(char), 100); | |
+ | |
+ ga_concat(&ga, "type"); | |
+ ga_append(&ga, NUL); | |
+ ga_concat(&ga, "notification"); | |
+ ga_append(&ga, NUL); | |
+ | |
+ ga_concat(&ga, "sender"); | |
+ ga_append(&ga, NUL); | |
+ ga_concat(&ga, sender); | |
+ ga_append(&ga, NUL); | |
+ | |
+ ga_concat(&ga, "reply"); | |
+ ga_append(&ga, NUL); | |
+ ga_concat(&ga, reply); | |
+ ga_append(&ga, NUL); | |
+ | |
+#ifdef FEAT_MBYTE | |
+ ga_concat(&ga, "encoding"); | |
+ ga_append(&ga, NUL); | |
+ ga_concat(&ga, p_enc); | |
+ ga_append(&ga, NUL); | |
+#endif | |
+ | |
+ ga_append(&ga, NUL); | |
+ | |
+ *abuf = ga.ga_data; | |
+ *abufsize = ga.ga_len; | |
+ | |
+ return 0; | |
+} | |
+ | |
+static int | |
+cmdsrv_parse_message(char_u *msg, cmdsrv_message_T *result) | |
+{ | |
+ char_u *key; | |
+ char_u *value; | |
+ char_u *p; | |
+ char_u *endp; | |
+ | |
+ vim_memset(result, 0, sizeof(*result)); | |
+ | |
+ p = msg; | |
+ while (*p != NUL) | |
+ { | |
+ key = p; | |
+ p += STRLEN(p) + 1; | |
+ value = p; | |
+ p += STRLEN(p) + 1; | |
+ if (STRICMP(key, "type") == 0) | |
+ result->type = value; | |
+ else if (STRICMP(key, "sender") == 0) | |
+ result->sender = value; | |
+ else if (STRICMP(key, "script") == 0) | |
+ result->script = value; | |
+ else if (STRICMP(key, "reply") == 0) | |
+ result->reply = value; | |
+ else if (STRICMP(key, "code") == 0) | |
+ result->code = value; | |
+ else if (STRICMP(key, "encoding") == 0) | |
+ result->encoding = value; | |
+ else | |
+ return -1; | |
+ } | |
+ | |
+ if (result->code != NULL) | |
+ { | |
+ errno = 0; | |
+ result->ncode = strtol(result->code, (char **)&endp, 10); | |
+ if (errno != 0 || *endp != NUL || endp == result->code) | |
+ return -1; | |
+ } | |
+ | |
+ if (result->type == NULL) | |
+ { | |
+ return -1; | |
+ } | |
+ else if (STRICMP(result->type, "keys") == 0) | |
+ { | |
+ if (result->sender == NULL || result->script == NULL) | |
+ return -1; | |
+ } | |
+ else if (STRICMP(result->type, "expr") == 0) | |
+ { | |
+ if (result->sender == NULL || result->script == NULL) | |
+ return -1; | |
+ } | |
+ else if (STRICMP(result->type, "reply") == 0) | |
+ { | |
+ if (result->sender == NULL || result->reply == NULL | |
+ || result->code == NULL) | |
+ return -1; | |
+ } | |
+ else if (STRICMP(result->type, "notification") == 0) | |
+ { | |
+ if (result->sender == NULL || result->reply == NULL) | |
+ return -1; | |
+ } | |
+ else | |
+ { | |
+ return -1; | |
+ } | |
+ | |
+ return 0; | |
+} | |
+ | |
+static int | |
+cmdsrv_receive_keys(cmdsrv_handle_t conn, cmdsrv_message_T *pmsg) | |
+{ | |
+ char_u *script; | |
+ | |
+ /* Remember in global */ | |
+ vim_free(cmdsrv_clientid); | |
+ cmdsrv_clientid = vim_strsave(pmsg->sender); | |
+ | |
+ script = cmdsrv_convert(pmsg->encoding, pmsg->script); | |
+ if (script == NULL) | |
+ return -1; | |
+ | |
+ server_to_input_buf(script); | |
+ | |
+ vim_free(script); | |
+ | |
+ return 0; | |
+} | |
+ | |
+static int | |
+cmdsrv_receive_expr(cmdsrv_handle_t conn, cmdsrv_message_T *pmsg) | |
+{ | |
+ char_u *script; | |
+ char_u *result; | |
+ char_u *reply; | |
+ char_u *abuf; | |
+ size_t abufsize; | |
+ int code; | |
+ | |
+ /* Remember in global */ | |
+ vim_free(cmdsrv_clientid); | |
+ cmdsrv_clientid = vim_strsave(pmsg->sender); | |
+ | |
+ script = cmdsrv_convert(pmsg->encoding, pmsg->script); | |
+ if (script == NULL) | |
+ return -1; | |
+ | |
+ result = eval_client_expr_to_string(script); | |
+ | |
+ vim_free(script); | |
+ | |
+ if (result != NULL) | |
+ { | |
+ reply = result; | |
+ code = 0; | |
+ } | |
+ else | |
+ { | |
+ reply = (char_u *)_(e_invexprmsg); | |
+ code = 1; | |
+ } | |
+ | |
+ if (cmdsrv_msg_reply(reply, code, &abuf, &abufsize) != 0) | |
+ { | |
+ vim_free(result); | |
+ return -1; | |
+ } | |
+ | |
+ vim_free(result); | |
+ | |
+ if (cmdsrv_write_message(conn, abuf, abufsize) != 0) | |
+ { | |
+ vim_free(abuf); | |
+ return -1; | |
+ } | |
+ | |
+ vim_free(abuf); | |
+ | |
+ return 0; | |
+} | |
+ | |
+static int | |
+cmdsrv_receive_notification(cmdsrv_handle_t conn, cmdsrv_message_T *pmsg) | |
+{ | |
+ char_u *reply; | |
+ | |
+ reply = cmdsrv_convert(pmsg->encoding, pmsg->reply); | |
+ if (reply == NULL) | |
+ return -1; | |
+ | |
+ if (cmdsrv_reply_add(pmsg->sender, reply) != 0) | |
+ { | |
+ vim_free(reply); | |
+ return -1; | |
+ } | |
+ | |
+#ifdef FEAT_AUTOCMD | |
+ apply_autocmds(EVENT_REMOTEREPLY, pmsg->sender, reply, TRUE, curbuf); | |
+#endif | |
+ | |
+ vim_free(reply); | |
+ | |
+ return 0; | |
+} | |
+ | |
+static char_u * | |
+cmdsrv_convert(char_u *client_enc, char_u *data) | |
+{ | |
+#ifdef FEAT_MBYTE | |
+ if (client_enc != NULL && p_enc != NULL) | |
+ { | |
+ vimconv_T vimconv; | |
+ char_u *res; | |
+ | |
+ vimconv.vc_type = CONV_NONE; | |
+ if (convert_setup(&vimconv, client_enc, p_enc) != FAIL | |
+ && vimconv.vc_type != CONV_NONE) | |
+ res = string_convert(&vimconv, data, NULL); | |
+ else | |
+ res = vim_strsave(data); | |
+ convert_setup(&vimconv, NULL, NULL); | |
+ | |
+ return res; | |
+ } | |
+#endif | |
+ return vim_strsave(data); | |
+} | |
+ | |
+#if defined(FEAT_GUI_X11) | |
+static void | |
+cmdsrv_message_from_client(XtPointer clientData UNUSED, | |
+ int *unused1 UNUSED, | |
+ XtInputId *unused2 UNUSED) | |
+{ | |
+ cmdsrv_handle_requests(); | |
+} | |
+#elif defined(FEAT_GUI_GTK) | |
+static void | |
+cmdsrv_message_from_client(gpointer clientData UNUSED, | |
+ gint unused1 UNUSED, | |
+ GdkInputCondition unused2 UNUSED) | |
+{ | |
+ cmdsrv_handle_requests(); | |
+} | |
+#endif | |
+ | |
+#endif /* FEAT_CLIENTSERVER */ | |
diff -r 15b934a16641 -r 2082fc32d223 src/if_cmdsrv_unix.c | |
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 | |
+++ b/src/if_cmdsrv_unix.c Thu Sep 15 21:25:11 2011 +0900 | |
@@ -0,0 +1,805 @@ | |
+ | |
+#include "vim.h" | |
+#include "version.h" | |
+ | |
+#include <sys/types.h> | |
+#include <sys/socket.h> | |
+#include <sys/un.h> | |
+ | |
+#ifdef HAVE_GETPEERUCRED | |
+# include <ucred.h> | |
+#endif | |
+ | |
+/* for struct xucred */ | |
+#ifdef LOCAL_PEERCRED | |
+# include <sys/ucred.h> | |
+#endif | |
+ | |
+#define CMDSRV_POLL_INTERVAL 1000 | |
+ | |
+struct cmdsrv_server { | |
+ int handle; | |
+ char_u *path; | |
+}; | |
+ | |
+struct cmdsrv_connection { | |
+ int handle; | |
+}; | |
+ | |
+typedef void *cmdsrv_handle_t; | |
+ | |
+static char_u *cmdsrv_dir(void); | |
+static char_u *cmdsrv_prefix(void); | |
+static char_u *cmdsrv_make_address(char_u *serverid); | |
+static char_u *cmdsrv_list(int sep); | |
+static int cmdsrv_is_alive(char_u *serverid); | |
+static uid_t cmdsrv_getpeeruid(int fd); | |
+static int cmdsrv_serv_listen(char_u *serverid, cmdsrv_handle_t *pserver); | |
+static int cmdsrv_serv_accept(cmdsrv_handle_t server, cmdsrv_handle_t *pconn); | |
+static int cmdsrv_serv_wait(cmdsrv_handle_t server, int timeoutmsec); | |
+static int cmdsrv_serv_close(cmdsrv_handle_t server); | |
+static int cmdsrv_cli_conn(char_u *serverid, cmdsrv_handle_t *pconn); | |
+static int cmdsrv_conn_close(cmdsrv_handle_t conn); | |
+static int cmdsrv_read_message(cmdsrv_handle_t conn, void **pabuf, size_t *pabufsize); | |
+static int cmdsrv_write_message(cmdsrv_handle_t conn, void *buf, size_t bufsize); | |
+static int cmdsrv_wait(int rfd, int wfd, int timeoutmsec); | |
+static int _ga_concatmemory(garray_T *gap, void *buf, size_t bufsize); | |
+static long_u _GetTickCount(void); | |
+ | |
+/* | |
+ * @return directory path without last separator | |
+ */ | |
+static char_u * | |
+cmdsrv_dir(void) | |
+{ | |
+ static char_u buf[256]; | |
+ | |
+ vim_snprintf(buf, sizeof(buf), "/tmp/vim-cmdsrv-%d", (int)getuid()); | |
+ | |
+ return buf; | |
+} | |
+ | |
+static char_u * | |
+cmdsrv_prefix(void) | |
+{ | |
+ static char_u buf[256]; | |
+ | |
+ vim_snprintf(buf, sizeof(buf), ""); | |
+ | |
+ return buf; | |
+} | |
+ | |
+static char_u * | |
+cmdsrv_make_address(char_u *serverid) | |
+{ | |
+ garray_T ga; | |
+ char_u *upname; | |
+ char_u *encoded; | |
+ | |
+ upname = strup_save(serverid); | |
+ if (upname == NULL) | |
+ return NULL; | |
+ | |
+ encoded = cmdsrv_urlencode(upname); | |
+ if (encoded == NULL) | |
+ { | |
+ vim_free(upname); | |
+ return NULL; | |
+ } | |
+ | |
+ vim_free(upname); | |
+ | |
+ ga_init2(&ga, (int)sizeof(char), 100); | |
+ | |
+ ga_concat(&ga, cmdsrv_dir()); | |
+ ga_concat(&ga, "/"); | |
+ ga_concat(&ga, cmdsrv_prefix()); | |
+ ga_concat(&ga, encoded); | |
+ | |
+ vim_free(encoded); | |
+ | |
+ return (char_u *)ga.ga_data; | |
+} | |
+ | |
+static char_u * | |
+cmdsrv_list(int sep) | |
+{ | |
+ garray_T ga; | |
+ char_u buf[256]; | |
+ char_u *pat[1]; | |
+ int num_files; | |
+ char_u **files; | |
+ int flags = EW_FILE | EW_ICASE | EW_SILENT; | |
+ char_u *serverid; | |
+ char_u *tail; | |
+ char_u *path; | |
+ char_u *prefix; | |
+ int i; | |
+ | |
+ prefix = cmdsrv_prefix(); | |
+ | |
+ vim_snprintf(buf, sizeof(buf), "%s/%s*", cmdsrv_dir(), prefix); | |
+ | |
+ pat[0] = buf; | |
+ | |
+ if (gen_expand_wildcards(1, pat, &num_files, &files, flags) == FAIL) | |
+ { | |
+ return vim_strsave((char_u *)""); | |
+ } | |
+ | |
+ ga_init2(&ga, (int)sizeof(char), 100); | |
+ | |
+ for (i = 0; i < num_files; ++i) | |
+ { | |
+ path = files[i]; | |
+ tail = gettail(path); | |
+ if (STRNICMP(tail, prefix, STRLEN(prefix)) == 0) | |
+ { | |
+ serverid = cmdsrv_urldecode(tail + STRLEN(prefix)); | |
+ if (serverid != NULL) | |
+ { | |
+#if 0 | |
+ if (!cmdsrv_is_alive(serverid) | |
+ && (errno == ENOENT || errno == ECONNREFUSED)) | |
+ { | |
+ /* server might down with trouble. */ | |
+ mch_remove(path); | |
+ } | |
+ else | |
+#endif | |
+ { | |
+ ga_concat(&ga, serverid); | |
+ ga_append(&ga, sep); | |
+ } | |
+ vim_free(serverid); | |
+ } | |
+ } | |
+ } | |
+ | |
+ ga_append(&ga, NUL); | |
+ | |
+ FreeWild(num_files, files); | |
+ | |
+ return (char_u *)ga.ga_data; | |
+} | |
+ | |
+static int | |
+cmdsrv_is_alive(char_u *serverid) | |
+{ | |
+ cmdsrv_handle_t conn; | |
+ | |
+ if (cmdsrv_cli_conn(serverid, &conn) == 0) | |
+ { | |
+ cmdsrv_conn_close(conn); | |
+ return 1; | |
+ } | |
+ | |
+ return 0; | |
+} | |
+ | |
+/* | |
+ * @return peer uid. -1 for error. | |
+ * TODO: more variant? | |
+ */ | |
+static uid_t | |
+cmdsrv_getpeeruid(int fd) | |
+{ | |
+#if defined(HAVE_GETPEERUCRED) | |
+ /* Solaris */ | |
+ { | |
+ ucred_t *cred = NULL; | |
+ uid_t uid; | |
+ | |
+ if (getpeerucred(fd, &cred) != 0) | |
+ return -1; | |
+ uid = ucred_geteuid(cred); | |
+ ucred_free(cred); | |
+ return uid; | |
+ } | |
+#elif defined(HAVE_GETPEEREID) | |
+ /* FreeBSD */ | |
+ { | |
+ uid_t uid; | |
+ gid_t gid; | |
+ | |
+ if (getpeereid(fd, &uid, &gid) != 0) | |
+ return -1; | |
+ return uid; | |
+ } | |
+#elif defined(LOCAL_PEERCRED) | |
+ /* FreeBSD */ | |
+ { | |
+ struct xucred cred; | |
+ socklen_t len; | |
+ | |
+ len = sizeof(cred); | |
+ if (getsockopt(fd, 0, LOCAL_PEERCRED, &cred, &len) != 0) | |
+ return -1; | |
+ return cred.cr_uid; | |
+ } | |
+#elif defined(SO_PEERCRED) | |
+ /* Linux */ | |
+ { | |
+ struct ucred cred; | |
+ socklen_t len; | |
+ | |
+ len = sizeof(cred); | |
+ if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cred, &len) != 0) | |
+ return -1; | |
+ return cred.uid; | |
+ } | |
+#else | |
+ { | |
+ /* No method available. Rely on directory permission. */ | |
+ return getuid(); | |
+ } | |
+#endif | |
+} | |
+ | |
+static int | |
+cmdsrv_serv_listen(char_u *serverid, cmdsrv_handle_t *pserver) | |
+{ | |
+ int fd; | |
+ socklen_t len; | |
+ struct sockaddr_un unix_addr; | |
+ char_u *path; | |
+ struct stat st; | |
+ struct cmdsrv_server *server; | |
+ | |
+ if (!mch_isdir(cmdsrv_dir())) | |
+ { | |
+ if (vim_mkdir(cmdsrv_dir(), 0700) != 0) | |
+ return -1; | |
+ } | |
+ | |
+ if (mch_stat((char *)cmdsrv_dir(), &st) != 0 | |
+ || st.st_uid != getuid() | |
+ || (st.st_mode & 0777) != 0700) | |
+ { | |
+ return -1; | |
+ } | |
+ | |
+ path = cmdsrv_make_address(serverid); | |
+ if (path == NULL) | |
+ return -1; | |
+ | |
+ if (STRLEN(path) + 1 > sizeof(unix_addr.sun_path)) | |
+ { | |
+ vim_free(path); | |
+ return -1; | |
+ } | |
+ | |
+ fd = socket(AF_UNIX, SOCK_STREAM, 0); | |
+ if (fd < 0) | |
+ { | |
+ vim_free(path); | |
+ return -1; | |
+ } | |
+ | |
+ memset(&unix_addr, 0, sizeof(unix_addr)); | |
+ unix_addr.sun_family = AF_UNIX; | |
+ strcpy(unix_addr.sun_path, path); | |
+ len = SUN_LEN(&unix_addr); | |
+ | |
+ if (bind(fd, (struct sockaddr*)&unix_addr, len) != 0) | |
+ { | |
+ close(fd); | |
+ vim_free(path); | |
+ return -1; | |
+ } | |
+ | |
+ if (listen(fd, CMDSRV_INSTANCES) != 0) | |
+ { | |
+ mch_remove(path); | |
+ close(fd); | |
+ vim_free(path); | |
+ return -1; | |
+ } | |
+ | |
+ server = (struct cmdsrv_server *)lalloc_clear( | |
+ sizeof(struct cmdsrv_server), TRUE); | |
+ if (server == NULL) | |
+ { | |
+ mch_remove(path); | |
+ close(fd); | |
+ vim_free(path); | |
+ return -1; | |
+ } | |
+ | |
+ server->handle = fd; | |
+ server->path = path; | |
+ | |
+ *pserver = (cmdsrv_handle_t)server; | |
+ | |
+ cmdsrv_listenfd = fd; | |
+#ifdef FEAT_GUI | |
+ cmdsrv_gui_register(); | |
+#endif | |
+ | |
+ return 0; | |
+} | |
+ | |
+static int | |
+cmdsrv_serv_accept(cmdsrv_handle_t _server, cmdsrv_handle_t *pconn) | |
+{ | |
+ struct cmdsrv_server *server = (struct cmdsrv_server *)_server; | |
+ int listenfd; | |
+ int fd; | |
+ socklen_t len; | |
+ struct sockaddr_un unix_addr; | |
+ struct cmdsrv_connection *conn; | |
+ | |
+ listenfd = server->handle; | |
+ | |
+ len = sizeof(unix_addr); | |
+ fd = accept(listenfd, (struct sockaddr *)&unix_addr, &len); | |
+ if (fd < 0) | |
+ return -1; | |
+ | |
+ if (cmdsrv_getpeeruid(fd) != getuid()) | |
+ { | |
+ close(fd); | |
+ return -1; | |
+ } | |
+ | |
+ conn = (struct cmdsrv_connection *)lalloc_clear( | |
+ sizeof(struct cmdsrv_connection), TRUE); | |
+ if (conn == NULL) | |
+ { | |
+ close(fd); | |
+ return -1; | |
+ } | |
+ | |
+ conn->handle = fd; | |
+ | |
+ *pconn = (cmdsrv_handle_t)conn; | |
+ | |
+ return 0; | |
+} | |
+ | |
+static int | |
+cmdsrv_serv_wait(cmdsrv_handle_t _server, int timeoutmsec) | |
+{ | |
+ struct cmdsrv_server *server = (struct cmdsrv_server *)_server; | |
+ static int lock = 0; | |
+ int ret; | |
+ | |
+ /* Don't allow accept request while waiting request */ | |
+ if (lock != 0) | |
+ return 0; | |
+ | |
+ /* When watching object is signaled while polling gui event, it is | |
+ * never handled because waiting session is locked. Then, the | |
+ * signal is not reset and gui event loop never finish. | |
+ * Unregister watching object temporarily. */ | |
+#ifdef FEAT_GUI | |
+ if (timeoutmsec != 0) | |
+ cmdsrv_gui_unregister(); | |
+#endif | |
+ | |
+ ++lock; | |
+ | |
+ ret = cmdsrv_wait(server->handle, -1, timeoutmsec); | |
+ | |
+ --lock; | |
+ | |
+#ifdef FEAT_GUI | |
+ if (timeoutmsec != 0) | |
+ cmdsrv_gui_register(); | |
+#endif | |
+ | |
+ return ret; | |
+} | |
+ | |
+static int | |
+cmdsrv_serv_close(cmdsrv_handle_t _server) | |
+{ | |
+ struct cmdsrv_server *server = (struct cmdsrv_server *)_server; | |
+ | |
+#ifdef FEAT_GUI | |
+ cmdsrv_gui_unregister(); | |
+#endif | |
+ cmdsrv_listenfd = -1; | |
+ | |
+ if (close(server->handle) != 0) | |
+ return -1; | |
+ | |
+ mch_remove(server->path); | |
+ | |
+ vim_free(server->path); | |
+ vim_free(server); | |
+ | |
+ return 0; | |
+} | |
+ | |
+static int | |
+cmdsrv_cli_conn(char_u *serverid, cmdsrv_handle_t *pconn) | |
+{ | |
+ int fd; | |
+ socklen_t len; | |
+ struct sockaddr_un unix_addr; | |
+ int flag; | |
+ char_u *path; | |
+ struct cmdsrv_connection *conn; | |
+ socklen_t errlen; | |
+ int err; | |
+ int ret; | |
+ | |
+ path = cmdsrv_make_address(serverid); | |
+ if (path == NULL) | |
+ return -1; | |
+ | |
+ if (STRLEN(path) + 1 > sizeof(unix_addr.sun_path)) | |
+ { | |
+ vim_free(path); | |
+ return -1; | |
+ } | |
+ | |
+ fd = socket(AF_UNIX, SOCK_STREAM, 0); | |
+ if (fd < 0) | |
+ { | |
+ vim_free(path); | |
+ return -1; | |
+ } | |
+ | |
+ /* Set NONBLOCK mode to prevent to block with connect(). */ | |
+ flag = fcntl(fd, F_GETFL, 0); | |
+ if (flag == -1) | |
+ { | |
+ close(fd); | |
+ vim_free(path); | |
+ return -1; | |
+ } | |
+ | |
+ if (fcntl(fd, F_SETFL, flag | O_NONBLOCK) == -1) | |
+ { | |
+ close(fd); | |
+ vim_free(path); | |
+ return -1; | |
+ } | |
+ | |
+ memset(&unix_addr, 0, sizeof(unix_addr)); | |
+ unix_addr.sun_family = AF_UNIX; | |
+ strcpy(unix_addr.sun_path, path); | |
+ len = SUN_LEN(&unix_addr); | |
+ | |
+ ret = connect(fd, (struct sockaddr *)&unix_addr, len); | |
+ if (ret < 0) | |
+ { | |
+ if (errno == EINPROGRESS) | |
+ { | |
+ if (cmdsrv_wait(-1, fd, -1) > 0) | |
+ { | |
+ errlen = sizeof(err); | |
+ if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &errlen) == 0 | |
+ && err == 0) | |
+ ret = 0; | |
+ } | |
+ } | |
+ } | |
+ if (ret < 0) | |
+ { | |
+ close(fd); | |
+ vim_free(path); | |
+ return -1; | |
+ } | |
+ | |
+ conn = (struct cmdsrv_connection *)lalloc_clear( | |
+ sizeof(struct cmdsrv_connection), TRUE); | |
+ if (conn == NULL) | |
+ { | |
+ close(fd); | |
+ vim_free(path); | |
+ return -1; | |
+ } | |
+ | |
+ conn->handle = fd; | |
+ | |
+ *pconn = (cmdsrv_handle_t)conn; | |
+ | |
+ return 0; | |
+} | |
+ | |
+static int | |
+cmdsrv_conn_close(cmdsrv_handle_t _conn) | |
+{ | |
+ struct cmdsrv_connection *conn = (struct cmdsrv_connection *)_conn; | |
+ | |
+ if (close(conn->handle) != 0) | |
+ return -1; | |
+ | |
+ vim_free(conn); | |
+ | |
+ return 0; | |
+} | |
+ | |
+static int | |
+cmdsrv_read_message(cmdsrv_handle_t _conn, void **pabuf, size_t *pabufsize) | |
+{ | |
+ struct cmdsrv_connection *conn = (struct cmdsrv_connection *)_conn; | |
+ int fd; | |
+ char buf[BUFSIZ]; | |
+ ssize_t nread; | |
+ garray_T ga; | |
+ | |
+ fd = conn->handle; | |
+ | |
+ ga_init2(&ga, (int)sizeof(char), 100); | |
+ | |
+ for (;;) | |
+ { | |
+ if (cmdsrv_wait(fd, -1, -1) <= 0) | |
+ { | |
+ ga_clear(&ga); | |
+ return -1; | |
+ } | |
+ | |
+ nread = read(fd, buf, sizeof(buf)); | |
+ if (nread < 0) | |
+ { | |
+ if (errno == EINTR) | |
+ continue; | |
+ ga_clear(&ga); | |
+ return -1; | |
+ } | |
+ else if (nread == 0) | |
+ break; | |
+ if (_ga_concatmemory(&ga, buf, nread) != OK) | |
+ { | |
+ ga_clear(&ga); | |
+ return -1; | |
+ } | |
+ } | |
+ | |
+ /* On MacOSX, shutdown() may fail when the connection is already | |
+ * closed by peer. */ | |
+ if (shutdown(fd, SHUT_RD) < 0 && errno != ENOTCONN) | |
+ { | |
+ ga_clear(&ga); | |
+ return -1; | |
+ } | |
+ | |
+ *pabufsize = ga.ga_len; | |
+ | |
+ /* ensure NUL terminated to ease to parse */ | |
+ ga_append(&ga, NUL); | |
+ ga_append(&ga, NUL); | |
+ ga_append(&ga, NUL); | |
+ | |
+ *pabuf = ga.ga_data; | |
+ | |
+ return 0; | |
+} | |
+ | |
+static int | |
+cmdsrv_write_message(cmdsrv_handle_t _conn, void *buf, size_t bufsize) | |
+{ | |
+ struct cmdsrv_connection *conn = (struct cmdsrv_connection *)_conn; | |
+ int fd; | |
+ ssize_t nwrite; | |
+ size_t nwritten; | |
+ | |
+ fd = conn->handle; | |
+ | |
+ nwritten = 0; | |
+ | |
+ while (nwritten < bufsize) | |
+ { | |
+ if (cmdsrv_wait(-1, fd, -1) <= 0) | |
+ { | |
+ return -1; | |
+ } | |
+ | |
+ nwrite = write(fd, ((char*)buf) + nwritten, bufsize - nwritten); | |
+ if (nwrite < 0) | |
+ { | |
+ if (errno == EINTR) | |
+ continue; | |
+ return -1; | |
+ } | |
+ nwritten += nwrite; | |
+ } | |
+ | |
+ /* On MacOSX, shutdown() may fail when the connection is already | |
+ * closed by peer. */ | |
+ if (shutdown(fd, SHUT_WR) < 0 && errno != ENOTCONN) | |
+ return -1; | |
+ | |
+ return 0; | |
+} | |
+ | |
+/* | |
+ * Wait single object, rfd for read, wfd for write. | |
+ * @param timeoutmsec -1 infinit, 0 nowait, >0 millisecond | |
+ * @return -1 error, 0 timeout, 1 received (not handled yet) | |
+ */ | |
+#ifdef HAVE_SELECT | |
+static int | |
+cmdsrv_wait(int rfd, int wfd, int timeoutmsec) | |
+{ | |
+ int maxfd; | |
+ struct timeval tv; | |
+ fd_set rset; | |
+ fd_set wset; | |
+ int n; | |
+ long_u starttime; | |
+ long_u lefttime; | |
+ long_u waittime; | |
+ | |
+ starttime = _GetTickCount(); | |
+ | |
+ while (!got_int) | |
+ { | |
+ maxfd = 0; | |
+ | |
+ FD_ZERO(&rset); | |
+ FD_ZERO(&wset); | |
+ | |
+ if (rfd != -1) | |
+ { | |
+ FD_SET(rfd, &rset); | |
+ if (maxfd < rfd) | |
+ maxfd = rfd; | |
+ } | |
+ | |
+ if (wfd != -1) | |
+ { | |
+ FD_SET(wfd, &wset); | |
+ if (maxfd < wfd) | |
+ maxfd = wfd; | |
+ } | |
+ | |
+ if (cmdsrv_listenfd != -1 | |
+ && cmdsrv_listenfd != rfd && cmdsrv_listenfd != wfd) | |
+ { | |
+ FD_SET(cmdsrv_listenfd, &rset); | |
+ if (maxfd < cmdsrv_listenfd) | |
+ maxfd = cmdsrv_listenfd; | |
+ } | |
+ | |
+ lefttime = _GetTickCount() - starttime; | |
+ if (timeoutmsec < 0) | |
+ waittime = CMDSRV_POLL_INTERVAL; | |
+ else if (timeoutmsec == 0) | |
+ waittime = 0; | |
+ else if ((long_u)timeoutmsec <= lefttime) | |
+ waittime = 0; | |
+ else if ((long_u)timeoutmsec - lefttime > CMDSRV_POLL_INTERVAL) | |
+ waittime = CMDSRV_POLL_INTERVAL; | |
+ else | |
+ waittime = (long_u)timeoutmsec - lefttime; | |
+ | |
+ tv.tv_sec = waittime / 1000; | |
+ tv.tv_usec = waittime % 1000 * 1000; | |
+ | |
+ n = select(maxfd + 1, &rset, &wset, NULL, &tv); | |
+ if (n < 0) | |
+ return -1; | |
+ | |
+ if (rfd != -1 && FD_ISSET(rfd, &rset)) | |
+ return 1; | |
+ | |
+ if (wfd != -1 && FD_ISSET(wfd, &wset)) | |
+ return 1; | |
+ | |
+ lefttime = _GetTickCount() - starttime; | |
+ if (timeoutmsec == 0) | |
+ return 0; | |
+ else if (timeoutmsec > 0 && (long_u)timeoutmsec <= lefttime) | |
+ return 0; | |
+ | |
+ cmdsrv_handle_requests(); | |
+ | |
+ ui_breakcheck(); | |
+ } | |
+ | |
+ return -1; | |
+} | |
+#else | |
+static int | |
+cmdsrv_wait(int rfd, int wfd, int timeoutmsec) | |
+{ | |
+ struct pollfd fds[2]; | |
+ int nfds; | |
+ int n; | |
+ long_u starttime; | |
+ long_u lefttime; | |
+ long_u waittime; | |
+ | |
+ starttime = _GetTickCount(); | |
+ | |
+ while (!got_int) | |
+ { | |
+ nfds = 0; | |
+ | |
+ if (rfd != -1) | |
+ { | |
+ fds[0].fd = rfd; | |
+ fds[0].events = POLLIN; | |
+ nfds = 1; | |
+ } | |
+ | |
+ if (wfd != -1) | |
+ { | |
+ fds[0].fd = wfd; | |
+ fds[0].events = POLLOUT; | |
+ nfds = 1; | |
+ } | |
+ | |
+ if (cmdsrv_listenfd != -1 | |
+ && cmdsrv_listenfd != rfd && cmdsrv_listenfd != wfd) | |
+ { | |
+ fds[nfds].fd = cmdsrv_listenfd; | |
+ fds[nfds].events = POLLIN; | |
+ nfds++; | |
+ } | |
+ | |
+ lefttime = _GetTickCount() - starttime; | |
+ | |
+ if (timeoutmsec < 0) | |
+ waittime = CMDSRV_POLL_INTERVAL; | |
+ else if (timeoutmsec == 0) | |
+ waittime = 0; | |
+ else if ((long_u)timeoutmsec <= lefttime) | |
+ waittime = 0; | |
+ else if ((long_u)timeoutmsec - lefttime > CMDSRV_POLL_INTERVAL) | |
+ waittime = CMDSRV_POLL_INTERVAL; | |
+ else | |
+ waittime = (long_u)timeoutmsec - lefttime; | |
+ | |
+ n = poll(fds, nfds, (int)waittime); | |
+ if (n < 0) | |
+ return -1; | |
+ | |
+ if (rfd != -1) | |
+ { | |
+ if (fds[0].revents & POLLIN) | |
+ return 1; | |
+ else if (fds[0].revents & (POLLERR | POLLHUP)) | |
+ return -1; | |
+ } | |
+ | |
+ if (wfd != -1) | |
+ { | |
+ if (fds[0].revents & POLLOUT) | |
+ return 1; | |
+ else if (fds[0].revents & (POLLERR | POLLHUP)) | |
+ return -1; | |
+ } | |
+ | |
+ lefttime = _GetTickCount() - starttime; | |
+ if (timeoutmsec == 0) | |
+ return 0; | |
+ else if (timeoutmsec > 0 && (long_u)timeoutmsec <= lefttime) | |
+ return 0; | |
+ | |
+ cmdsrv_handle_requests(); | |
+ | |
+ ui_breakcheck(); | |
+ } | |
+ | |
+ return -1; | |
+} | |
+#endif | |
+ | |
+static int | |
+_ga_concatmemory(garray_T *gap, void *buf, size_t bufsize) | |
+{ | |
+ if (ga_grow(gap, bufsize) != OK) | |
+ return FAIL; | |
+ mch_memmove(((char *)gap->ga_data) + gap->ga_len, buf, bufsize); | |
+ gap->ga_len += bufsize; | |
+ return OK; | |
+} | |
+ | |
+static long_u | |
+_GetTickCount(void) | |
+{ | |
+ struct timeval tv; | |
+ | |
+ if (gettimeofday(&tv, NULL) != 0) | |
+ return (long_u)-1; | |
+ | |
+ return (tv.tv_sec * 1000) + (tv.tv_usec / 1000); | |
+} | |
+ | |
diff -r 15b934a16641 -r 2082fc32d223 src/if_cmdsrv_win32.c | |
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 | |
+++ b/src/if_cmdsrv_win32.c Thu Sep 15 21:25:11 2011 +0900 | |
@@ -0,0 +1,639 @@ | |
+ | |
+#include "vim.h" | |
+#include "version.h" | |
+ | |
+#include <stddef.h> | |
+ | |
+#define CMDSRV_POLL_INTERVAL 1000 | |
+ | |
+typedef struct { | |
+ OVERLAPPED oOverlap; | |
+ HANDLE hPipeInst; | |
+ BOOL iopending; | |
+} PIPEINST, *LPPIPEINST; | |
+ | |
+struct cmdsrv_server { | |
+ PIPEINST pipes[CMDSRV_INSTANCES]; | |
+ HANDLE hEvents[CMDSRV_INSTANCES]; | |
+}; | |
+ | |
+struct cmdsrv_connection { | |
+ PIPEINST Pipe; | |
+ LPPIPEINST Server; | |
+}; | |
+ | |
+typedef void *cmdsrv_handle_t; | |
+ | |
+static char_u *cmdsrv_dir(void); | |
+static char_u *cmdsrv_prefix(void); | |
+static char_u *cmdsrv_make_address(char_u *serverid); | |
+static char_u *cmdsrv_list(int sep); | |
+static int cmdsrv_serv_listen(char_u *serverid, cmdsrv_handle_t *pserver); | |
+static int cmdsrv_serv_accept(cmdsrv_handle_t server, cmdsrv_handle_t *pconn); | |
+static int cmdsrv_serv_wait(cmdsrv_handle_t server, int timeoutmsec); | |
+static int cmdsrv_serv_close(cmdsrv_handle_t server); | |
+static int cmdsrv_cli_conn(char_u *serverid, cmdsrv_handle_t *pconn); | |
+static int cmdsrv_conn_close(cmdsrv_handle_t conn); | |
+static int cmdsrv_read_message(cmdsrv_handle_t conn, void **pabuf, size_t *pabufsize); | |
+static int cmdsrv_write_message(cmdsrv_handle_t conn, void *buf, size_t bufsize); | |
+static int cmdsrv_wait(int nitems, HANDLE *objects, int *pidx, int timeoutmsec); | |
+static int _ga_concatmemory(garray_T *gap, void *buf, size_t bufsize); | |
+static BOOL DisconnectAndReconnect(LPPIPEINST lpPipe); | |
+static BOOL ConnectToNewClient(LPPIPEINST lpPipe); | |
+ | |
+/* | |
+ * @return directory path without last separator | |
+ */ | |
+static char_u * | |
+cmdsrv_dir(void) | |
+{ | |
+ static char_u buf[256]; | |
+ | |
+ vim_snprintf(buf, sizeof(buf), "\\\\.\\pipe"); | |
+ | |
+ return buf; | |
+} | |
+ | |
+static char_u * | |
+cmdsrv_prefix(void) | |
+{ | |
+ static char_u buf[256]; | |
+ | |
+ vim_snprintf(buf, sizeof(buf), "vim-cmdsrv-"); | |
+ | |
+ return buf; | |
+} | |
+ | |
+static char_u * | |
+cmdsrv_make_address(char_u *serverid) | |
+{ | |
+ garray_T ga; | |
+ char_u *upname; | |
+ char_u *encoded; | |
+ | |
+ upname = strup_save(serverid); | |
+ if (upname == NULL) | |
+ return NULL; | |
+ | |
+ encoded = cmdsrv_urlencode(upname); | |
+ if (encoded == NULL) | |
+ { | |
+ vim_free(upname); | |
+ return NULL; | |
+ } | |
+ | |
+ vim_free(upname); | |
+ | |
+ ga_init2(&ga, (int)sizeof(char), 100); | |
+ | |
+ ga_concat(&ga, cmdsrv_dir()); | |
+ ga_concat(&ga, "\\"); | |
+ ga_concat(&ga, cmdsrv_prefix()); | |
+ ga_concat(&ga, encoded); | |
+ | |
+ vim_free(encoded); | |
+ | |
+ return (char_u *)ga.ga_data; | |
+} | |
+ | |
+static char_u * | |
+cmdsrv_list(int sep) | |
+{ | |
+ garray_T ga; | |
+ char_u buf[256]; | |
+ char_u *serverid; | |
+ char_u *tail; | |
+ char_u *prefix; | |
+ WIN32_FIND_DATA finddata; | |
+ HANDLE h; | |
+ | |
+ prefix = cmdsrv_prefix(); | |
+ | |
+ vim_snprintf(buf, sizeof(buf), "%s\\*", cmdsrv_dir()); | |
+ | |
+ ga_init2(&ga, (int)sizeof(char), 100); | |
+ | |
+ h = FindFirstFile(buf, &finddata); | |
+ if (h == INVALID_HANDLE_VALUE) | |
+ { | |
+ ga_clear(&ga); | |
+ return NULL; | |
+ } | |
+ | |
+ do | |
+ { | |
+ tail = gettail((char_u *)finddata.cFileName); | |
+ if (STRNICMP(tail, prefix, STRLEN(prefix)) == 0) | |
+ { | |
+ serverid = cmdsrv_urldecode(tail + STRLEN(prefix)); | |
+ if (serverid != NULL) | |
+ { | |
+ ga_concat(&ga, serverid); | |
+ ga_append(&ga, sep); | |
+ vim_free(serverid); | |
+ } | |
+ } | |
+ } while (FindNextFile(h, &finddata)); | |
+ | |
+ ga_append(&ga, NUL); | |
+ | |
+ FindClose(h); | |
+ | |
+ return (char_u *)ga.ga_data; | |
+} | |
+ | |
+static int | |
+cmdsrv_serv_listen(char_u *serverid, cmdsrv_handle_t *pserver) | |
+{ | |
+ char_u *path; | |
+ int i; | |
+ struct cmdsrv_server *server; | |
+ | |
+ path = cmdsrv_make_address(serverid); | |
+ if (path == NULL) | |
+ return -1; | |
+ | |
+ server = (struct cmdsrv_server *)lalloc_clear( | |
+ sizeof(struct cmdsrv_server), TRUE); | |
+ if (server == NULL) | |
+ { | |
+ vim_free(path); | |
+ return -1; | |
+ } | |
+ | |
+ for (i = 0; i < CMDSRV_INSTANCES; ++i) | |
+ { | |
+ server->pipes[i].iopending = FALSE; | |
+ server->pipes[i].oOverlap.hEvent = NULL; | |
+ server->pipes[i].hPipeInst = INVALID_HANDLE_VALUE; | |
+ } | |
+ | |
+ for (i = 0; i < CMDSRV_INSTANCES; ++i) | |
+ { | |
+ server->pipes[i].oOverlap.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL); | |
+ if (server->pipes[i].oOverlap.hEvent == NULL) | |
+ break; | |
+ | |
+ server->hEvents[i] = server->pipes[i].oOverlap.hEvent; | |
+ | |
+ server->pipes[i].hPipeInst = CreateNamedPipe( | |
+ path, | |
+ PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, | |
+ PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, | |
+ CMDSRV_INSTANCES, | |
+ 0, | |
+ 0, | |
+ 0, | |
+ NULL); | |
+ if (server->pipes[i].hPipeInst == INVALID_HANDLE_VALUE) | |
+ break; | |
+ | |
+ if (!ConnectToNewClient(&server->pipes[i])) | |
+ break; | |
+ } | |
+ | |
+ if (i < CMDSRV_INSTANCES) | |
+ { | |
+ cmdsrv_serv_close(server); | |
+ vim_free(path); | |
+ return -1; | |
+ } | |
+ | |
+ vim_free(path); | |
+ | |
+ *pserver = (cmdsrv_handle_t)server; | |
+ | |
+ cmdsrv_events = server->hEvents; | |
+ | |
+ return 0; | |
+} | |
+ | |
+static int | |
+cmdsrv_serv_accept(cmdsrv_handle_t _server, cmdsrv_handle_t *pconn) | |
+{ | |
+ struct cmdsrv_server *server = (struct cmdsrv_server *)_server; | |
+ int idx; | |
+ struct cmdsrv_connection *conn; | |
+ | |
+ if (cmdsrv_wait(CMDSRV_INSTANCES, server->hEvents, &idx, -1) <= 0) | |
+ return -1; | |
+ | |
+ server->pipes[idx].iopending = FALSE; | |
+ | |
+ /* Event object will be re-checked while processing connection. | |
+ * Turn off to prevent to be misinterpreted as new request came. */ | |
+ if (!ResetEvent(server->hEvents[idx])) | |
+ { | |
+ DisconnectAndReconnect(&server->pipes[idx]); | |
+ return -1; | |
+ } | |
+ | |
+ conn = (struct cmdsrv_connection *)lalloc_clear( | |
+ sizeof(struct cmdsrv_connection), TRUE); | |
+ if (conn == NULL) | |
+ { | |
+ DisconnectAndReconnect(&server->pipes[idx]); | |
+ return -1; | |
+ } | |
+ | |
+ conn->Pipe.oOverlap.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL); | |
+ if (conn->Pipe.oOverlap.hEvent == NULL) | |
+ { | |
+ vim_free(conn); | |
+ DisconnectAndReconnect(&server->pipes[idx]); | |
+ return -1; | |
+ } | |
+ | |
+ conn->Pipe.hPipeInst = server->pipes[idx].hPipeInst; | |
+ conn->Server = &server->pipes[idx]; | |
+ | |
+ *pconn = (cmdsrv_handle_t)conn; | |
+ | |
+ return 0; | |
+} | |
+ | |
+static int | |
+cmdsrv_serv_wait(cmdsrv_handle_t _server, int timeoutmsec) | |
+{ | |
+ struct cmdsrv_server *server = (struct cmdsrv_server *)_server; | |
+ static int lock = 0; | |
+ int ret; | |
+ | |
+ /* Don't allow accept request while waiting request */ | |
+ if (lock != 0) | |
+ return 0; | |
+ | |
+ /* When watching object is signaled while polling gui event, it is | |
+ * never handled because waiting session is locked. Then, the | |
+ * signal is not reset and gui event loop never finish. | |
+ * Unregister watching object temporarily. */ | |
+#ifdef FEAT_GUI | |
+ if (timeoutmsec != 0) | |
+ cmdsrv_gui_unregister(); | |
+#endif | |
+ | |
+ ++lock; | |
+ | |
+ ret = cmdsrv_wait(CMDSRV_INSTANCES, server->hEvents, NULL, timeoutmsec); | |
+ | |
+ --lock; | |
+ | |
+#ifdef FEAT_GUI | |
+ if (timeoutmsec != 0) | |
+ cmdsrv_gui_register(); | |
+#endif | |
+ | |
+ return ret; | |
+} | |
+ | |
+static int | |
+cmdsrv_serv_close(cmdsrv_handle_t _server) | |
+{ | |
+ struct cmdsrv_server *server = (struct cmdsrv_server *)_server; | |
+ int i; | |
+ | |
+ cmdsrv_events = NULL; | |
+ | |
+ for (i = 0; i < CMDSRV_INSTANCES; ++i) | |
+ { | |
+ if (server->pipes[i].iopending) | |
+ { | |
+ if (CancelIo(server->pipes[i].hPipeInst)) | |
+ WaitForSingleObject(server->pipes[i].oOverlap.hEvent, INFINITE); | |
+ } | |
+ if (server->pipes[i].hPipeInst != INVALID_HANDLE_VALUE) | |
+ { | |
+ DisconnectNamedPipe(server->pipes[i].hPipeInst); | |
+ CloseHandle(server->pipes[i].hPipeInst); | |
+ } | |
+ if (server->pipes[i].oOverlap.hEvent != NULL) | |
+ CloseHandle(server->pipes[i].oOverlap.hEvent); | |
+ } | |
+ | |
+ vim_free(server); | |
+ | |
+ return 0; | |
+} | |
+ | |
+static int | |
+cmdsrv_cli_conn(char_u *serverid, cmdsrv_handle_t *pconn) | |
+{ | |
+ char_u *path; | |
+ struct cmdsrv_connection *conn; | |
+ | |
+ path = cmdsrv_make_address(serverid); | |
+ if (path == NULL) | |
+ return -1; | |
+ | |
+ conn = (struct cmdsrv_connection *)lalloc_clear( | |
+ sizeof(struct cmdsrv_connection), TRUE); | |
+ if (conn == NULL) | |
+ { | |
+ vim_free(path); | |
+ return -1; | |
+ } | |
+ | |
+ conn->Pipe.oOverlap.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL); | |
+ if (conn->Pipe.oOverlap.hEvent == NULL) | |
+ { | |
+ vim_free(conn); | |
+ vim_free(path); | |
+ return -1; | |
+ } | |
+ | |
+ conn->Pipe.hPipeInst = CreateFile( | |
+ path, | |
+ GENERIC_READ | GENERIC_WRITE, | |
+ 0, | |
+ NULL, | |
+ OPEN_EXISTING, | |
+ FILE_FLAG_OVERLAPPED, | |
+ NULL); | |
+ if (conn->Pipe.hPipeInst == INVALID_HANDLE_VALUE) | |
+ { | |
+ CloseHandle(conn->Pipe.oOverlap.hEvent); | |
+ vim_free(conn); | |
+ vim_free(path); | |
+ return -1; | |
+ } | |
+ | |
+ conn->Server = NULL; | |
+ | |
+ *pconn = (cmdsrv_handle_t)conn; | |
+ | |
+ return 0; | |
+} | |
+ | |
+static int | |
+cmdsrv_conn_close(cmdsrv_handle_t _conn) | |
+{ | |
+ struct cmdsrv_connection *conn = (struct cmdsrv_connection *)_conn; | |
+ | |
+ if (conn->Server != NULL) | |
+ { | |
+ CloseHandle(conn->Pipe.oOverlap.hEvent); | |
+ DisconnectAndReconnect(conn->Server); | |
+ } | |
+ else | |
+ { | |
+ CloseHandle(conn->Pipe.oOverlap.hEvent); | |
+ CloseHandle(conn->Pipe.hPipeInst); | |
+ } | |
+ | |
+ vim_free(conn); | |
+ | |
+ return 0; | |
+} | |
+ | |
+static int | |
+cmdsrv_read_message(cmdsrv_handle_t _conn, void **pabuf, size_t *pabufsize) | |
+{ | |
+ struct cmdsrv_connection *conn = (struct cmdsrv_connection *)_conn; | |
+ char buf[BUFSIZ]; | |
+ DWORD nread; | |
+ garray_T ga; | |
+ | |
+ ga_init2(&ga, (int)sizeof(char), 100); | |
+ | |
+ for (;;) | |
+ { | |
+ if (ReadFile(conn->Pipe.hPipeInst, buf, sizeof(buf), &nread, | |
+ &conn->Pipe.oOverlap)) | |
+ { | |
+ if (_ga_concatmemory(&ga, buf, nread) != OK) | |
+ { | |
+ ga_clear(&ga); | |
+ return -1; | |
+ } | |
+ break; | |
+ } | |
+ else if (GetLastError() == ERROR_MORE_DATA) | |
+ { | |
+ if (_ga_concatmemory(&ga, buf, nread) != OK) | |
+ { | |
+ ga_clear(&ga); | |
+ return -1; | |
+ } | |
+ continue; | |
+ } | |
+ else if (GetLastError() == ERROR_IO_PENDING) | |
+ { | |
+ if (cmdsrv_wait(1, &conn->Pipe.oOverlap.hEvent, NULL, -1) <= 0) | |
+ { | |
+ if (CancelIo(conn->Pipe.hPipeInst)) | |
+ WaitForSingleObject(conn->Pipe.oOverlap.hEvent, INFINITE); | |
+ ga_clear(&ga); | |
+ return -1; | |
+ } | |
+ if (GetOverlappedResult(conn->Pipe.hPipeInst, | |
+ &conn->Pipe.oOverlap, &nread, FALSE)) | |
+ { | |
+ if (_ga_concatmemory(&ga, buf, nread) != OK) | |
+ { | |
+ ga_clear(&ga); | |
+ return -1; | |
+ } | |
+ break; | |
+ } | |
+ else if (GetLastError() == ERROR_MORE_DATA) | |
+ { | |
+ if (_ga_concatmemory(&ga, buf, nread) != OK) | |
+ { | |
+ ga_clear(&ga); | |
+ return -1; | |
+ } | |
+ continue; | |
+ } | |
+ else | |
+ { | |
+ ga_clear(&ga); | |
+ return -1; | |
+ } | |
+ } | |
+ else | |
+ { | |
+ ga_clear(&ga); | |
+ return -1; | |
+ } | |
+ } | |
+ | |
+ *pabufsize = ga.ga_len; | |
+ | |
+ /* ensure NUL terminated to ease to parse */ | |
+ ga_append(&ga, NUL); | |
+ ga_append(&ga, NUL); | |
+ ga_append(&ga, NUL); | |
+ | |
+ *pabuf = ga.ga_data; | |
+ | |
+ return 0; | |
+} | |
+ | |
+static int | |
+cmdsrv_write_message(cmdsrv_handle_t _conn, void *buf, size_t bufsize) | |
+{ | |
+ struct cmdsrv_connection *conn = (struct cmdsrv_connection *)_conn; | |
+ DWORD nwrite; | |
+ | |
+ /* message pipe is atomic for each WriteFile() call. */ | |
+ if (WriteFile(conn->Pipe.hPipeInst, buf, bufsize, &nwrite, | |
+ &conn->Pipe.oOverlap)) | |
+ { | |
+ return 0; | |
+ } | |
+ else if (GetLastError() == ERROR_IO_PENDING) | |
+ { | |
+ if (cmdsrv_wait(1, &conn->Pipe.oOverlap.hEvent, NULL, -1) <= 0) | |
+ { | |
+ if (CancelIo(conn->Pipe.hPipeInst)) | |
+ WaitForSingleObject(conn->Pipe.oOverlap.hEvent, INFINITE); | |
+ return -1; | |
+ } | |
+ if (GetOverlappedResult(conn->Pipe.hPipeInst, | |
+ &conn->Pipe.oOverlap, &nwrite, FALSE)) | |
+ { | |
+ return 0; | |
+ } | |
+ else | |
+ { | |
+ return -1; | |
+ } | |
+ } | |
+ else | |
+ { | |
+ return -1; | |
+ } | |
+} | |
+ | |
+/* | |
+ * @param timeoutmsec -1 infinit, 0 nowait, >0 millisecond | |
+ * @return -1 error, 0 timeout, 1 received (not handled yet) | |
+ */ | |
+static int | |
+cmdsrv_wait(int nitems, HANDLE *objects, int *pidx, int timeoutmsec) | |
+{ | |
+ HANDLE wait_objects[1 + CMDSRV_INSTANCES]; | |
+ int wait_nitems = 0; | |
+ DWORD dwWait; | |
+ DWORD starttime; | |
+ DWORD lefttime; | |
+ DWORD waittime; | |
+ | |
+ /* assert nitems == 1 || objects == cmdsrv_events */ | |
+ | |
+ while (wait_nitems < nitems) | |
+ { | |
+ wait_objects[wait_nitems] = objects[wait_nitems]; | |
+ wait_nitems++; | |
+ } | |
+ | |
+ if (objects != cmdsrv_events) | |
+ { | |
+ mch_memmove(wait_objects + wait_nitems, cmdsrv_events, | |
+ sizeof(HANDLE) * CMDSRV_INSTANCES); | |
+ wait_nitems += CMDSRV_INSTANCES; | |
+ } | |
+ | |
+ starttime = GetTickCount(); | |
+ | |
+ while (!got_int) | |
+ { | |
+ lefttime = GetTickCount() - starttime; | |
+ | |
+ if (timeoutmsec < 0) | |
+ waittime = CMDSRV_POLL_INTERVAL; | |
+ else if (timeoutmsec == 0) | |
+ waittime = 0; | |
+ else if ((DWORD)timeoutmsec <= lefttime) | |
+ waittime = 0; | |
+ else if ((DWORD)timeoutmsec - lefttime > CMDSRV_POLL_INTERVAL) | |
+ waittime = CMDSRV_POLL_INTERVAL; | |
+ else | |
+ waittime = (DWORD)timeoutmsec - lefttime; | |
+ | |
+ dwWait = WaitForMultipleObjects(wait_nitems, wait_objects, FALSE, | |
+ waittime); | |
+ if (dwWait == WAIT_FAILED) | |
+ { | |
+ return -1; | |
+ } | |
+ else if (dwWait == WAIT_TIMEOUT) | |
+ { | |
+ /* continue */ | |
+ } | |
+ else if (WAIT_OBJECT_0 <= dwWait && dwWait < WAIT_OBJECT_0 + nitems) | |
+ { | |
+ if (pidx != NULL) | |
+ *pidx = dwWait - WAIT_OBJECT_0; | |
+ return 1; | |
+ } | |
+ else if (WAIT_OBJECT_0 + nitems <= dwWait | |
+ && dwWait < WAIT_OBJECT_0 + wait_nitems) | |
+ { | |
+ /* request received */ | |
+ } | |
+ else | |
+ { | |
+ return -1; | |
+ } | |
+ | |
+ lefttime = GetTickCount() - starttime; | |
+ if (timeoutmsec == 0) | |
+ return 0; | |
+ else if (timeoutmsec > 0 && (DWORD)timeoutmsec <= lefttime) | |
+ return 0; | |
+ | |
+ cmdsrv_handle_requests(); | |
+ | |
+ ui_breakcheck(); | |
+ } | |
+ | |
+ return -1; | |
+} | |
+ | |
+static int | |
+_ga_concatmemory(garray_T *gap, void *buf, size_t bufsize) | |
+{ | |
+ if (ga_grow(gap, bufsize) != OK) | |
+ return FAIL; | |
+ mch_memmove(((char *)gap->ga_data) + gap->ga_len, buf, bufsize); | |
+ gap->ga_len += bufsize; | |
+ return OK; | |
+} | |
+ | |
+static BOOL | |
+DisconnectAndReconnect(LPPIPEINST lpPipe) | |
+{ | |
+ if (!DisconnectNamedPipe(lpPipe->hPipeInst)) | |
+ return FALSE; | |
+ return ConnectToNewClient(lpPipe); | |
+} | |
+ | |
+static BOOL | |
+ConnectToNewClient(LPPIPEINST lpPipe) | |
+{ | |
+ BOOL fConnected; | |
+ | |
+ lpPipe->iopending = FALSE; | |
+ | |
+ fConnected = ConnectNamedPipe(lpPipe->hPipeInst, &lpPipe->oOverlap); | |
+ /* Overlapped ConnectNamedPipe should return zero. */ | |
+ if (fConnected) | |
+ { | |
+ return FALSE; | |
+ } | |
+ | |
+ switch (GetLastError()) | |
+ { | |
+ case ERROR_IO_PENDING: | |
+ lpPipe->iopending = TRUE; | |
+ return TRUE; | |
+ case ERROR_PIPE_CONNECTED: | |
+ if (SetEvent(lpPipe->oOverlap.hEvent)) | |
+ return TRUE; | |
+ /* FALLTHROUGH */ | |
+ default: | |
+ return FALSE; | |
+ } | |
+} | |
+ | |
diff -r 15b934a16641 -r 2082fc32d223 src/if_xcmdsrv.c | |
--- a/src/if_xcmdsrv.c Wed Sep 14 19:04:40 2011 +0200 | |
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000 | |
@@ -1,1498 +0,0 @@ | |
-/* vi:set ts=8 sts=4 sw=4: | |
- * | |
- * VIM - Vi IMproved by Bram Moolenaar | |
- * X command server by Flemming Madsen | |
- * | |
- * 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. | |
- * | |
- * if_xcmdsrv.c: Functions for passing commands through an X11 display. | |
- * | |
- */ | |
- | |
-#include "vim.h" | |
-#include "version.h" | |
- | |
-#if defined(FEAT_CLIENTSERVER) || defined(PROTO) | |
- | |
-# ifdef FEAT_X11 | |
-# include <X11/Intrinsic.h> | |
-# include <X11/Xatom.h> | |
-# endif | |
- | |
-/* | |
- * This file provides procedures that implement the command server | |
- * functionality of Vim when in contact with an X11 server. | |
- * | |
- * Adapted from TCL/TK's send command in tkSend.c of the tk 3.6 distribution. | |
- * Adapted for use in Vim by Flemming Madsen. Protocol changed to that of tk 4 | |
- */ | |
- | |
-/* | |
- * Copyright (c) 1989-1993 The Regents of the University of California. | |
- * All rights reserved. | |
- * | |
- * Permission is hereby granted, without written agreement and without | |
- * license or royalty fees, to use, copy, modify, and distribute this | |
- * software and its documentation for any purpose, provided that the | |
- * above copyright notice and the following two paragraphs appear in | |
- * all copies of this software. | |
- * | |
- * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR | |
- * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT | |
- * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF | |
- * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
- * | |
- * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, | |
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY | |
- * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS | |
- * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO | |
- * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. | |
- */ | |
- | |
- | |
-/* | |
- * When a result is being awaited from a sent command, one of | |
- * the following structures is present on a list of all outstanding | |
- * sent commands. The information in the structure is used to | |
- * process the result when it arrives. You're probably wondering | |
- * how there could ever be multiple outstanding sent commands. | |
- * This could happen if Vim instances invoke each other recursively. | |
- * It's unlikely, but possible. | |
- */ | |
- | |
-typedef struct PendingCommand | |
-{ | |
- int serial; /* Serial number expected in result. */ | |
- int code; /* Result Code. 0 is OK */ | |
- char_u *result; /* String result for command (malloc'ed). | |
- * NULL means command still pending. */ | |
- struct PendingCommand *nextPtr; | |
- /* Next in list of all outstanding commands. | |
- * NULL means end of list. */ | |
-} PendingCommand; | |
- | |
-static PendingCommand *pendingCommands = NULL; | |
- /* List of all commands currently | |
- * being waited for. */ | |
- | |
-/* | |
- * The information below is used for communication between processes | |
- * during "send" commands. Each process keeps a private window, never | |
- * even mapped, with one property, "Comm". When a command is sent to | |
- * an interpreter, the command is appended to the comm property of the | |
- * communication window associated with the interp's process. Similarly, | |
- * when a result is returned from a sent command, it is also appended | |
- * to the comm property. | |
- * | |
- * Each command and each result takes the form of ASCII text. For a | |
- * command, the text consists of a nul character followed by several | |
- * nul-terminated ASCII strings. The first string consists of a | |
- * single letter: | |
- * "c" for an expression | |
- * "k" for keystrokes | |
- * "r" for reply | |
- * "n" for notification. | |
- * Subsequent strings have the form "option value" where the following options | |
- * are supported: | |
- * | |
- * -r commWindow serial | |
- * | |
- * This option means that a response should be sent to the window | |
- * whose X identifier is "commWindow" (in hex), and the response should | |
- * be identified with the serial number given by "serial" (in decimal). | |
- * If this option isn't specified then the send is asynchronous and | |
- * no response is sent. | |
- * | |
- * -n name | |
- * "Name" gives the name of the application for which the command is | |
- * intended. This option must be present. | |
- * | |
- * -E encoding | |
- * Encoding name used for the text. This is the 'encoding' of the | |
- * sender. The receiver may want to do conversion to his 'encoding'. | |
- * | |
- * -s script | |
- * "Script" is the script to be executed. This option must be | |
- * present. Taken as a series of keystrokes in a "k" command where | |
- * <Key>'s are expanded | |
- * | |
- * The options may appear in any order. The -n and -s options must be | |
- * present, but -r may be omitted for asynchronous RPCs. For compatibility | |
- * with future releases that may add new features, there may be additional | |
- * options present; as long as they start with a "-" character, they will | |
- * be ignored. | |
- * | |
- * A result also consists of a zero character followed by several null- | |
- * terminated ASCII strings. The first string consists of the single | |
- * letter "r". Subsequent strings have the form "option value" where | |
- * the following options are supported: | |
- * | |
- * -s serial | |
- * Identifies the command for which this is the result. It is the | |
- * same as the "serial" field from the -s option in the command. This | |
- * option must be present. | |
- * | |
- * -r result | |
- * "Result" is the result string for the script, which may be either | |
- * a result or an error message. If this field is omitted then it | |
- * defaults to an empty string. | |
- * | |
- * -c code | |
- * 0: for OK. This is the default. | |
- * 1: for error: Result is the last error | |
- * | |
- * -i errorInfo | |
- * -e errorCode | |
- * Not applicable for Vim | |
- * | |
- * Options may appear in any order, and only the -s option must be | |
- * present. As with commands, there may be additional options besides | |
- * these; unknown options are ignored. | |
- */ | |
- | |
-/* | |
- * Maximum size property that can be read at one time by | |
- * this module: | |
- */ | |
- | |
-#define MAX_PROP_WORDS 100000 | |
- | |
-struct ServerReply | |
-{ | |
- Window id; | |
- garray_T strings; | |
-}; | |
-static garray_T serverReply = { 0, 0, 0, 0, 0 }; | |
-enum ServerReplyOp { SROP_Find, SROP_Add, SROP_Delete }; | |
- | |
-typedef int (*EndCond) __ARGS((void *)); | |
- | |
-/* | |
- * Forward declarations for procedures defined later in this file: | |
- */ | |
- | |
-static Window LookupName __ARGS((Display *dpy, char_u *name, int delete, char_u **loose)); | |
-static int SendInit __ARGS((Display *dpy)); | |
-static int DoRegisterName __ARGS((Display *dpy, char_u *name)); | |
-static void DeleteAnyLingerer __ARGS((Display *dpy, Window w)); | |
-static int GetRegProp __ARGS((Display *dpy, char_u **regPropp, long_u *numItemsp, int domsg)); | |
-static int WaitForPend __ARGS((void *p)); | |
-static int WaitForReply __ARGS((void *p)); | |
-static int WindowValid __ARGS((Display *dpy, Window w)); | |
-static void ServerWait __ARGS((Display *dpy, Window w, EndCond endCond, void *endData, int localLoop, int seconds)); | |
-static struct ServerReply *ServerReplyFind __ARGS((Window w, enum ServerReplyOp op)); | |
-static int AppendPropCarefully __ARGS((Display *display, Window window, Atom property, char_u *value, int length)); | |
-static int x_error_check __ARGS((Display *dpy, XErrorEvent *error_event)); | |
-static int IsSerialName __ARGS((char_u *name)); | |
- | |
-/* Private variables for the "server" functionality */ | |
-static Atom registryProperty = None; | |
-static Atom vimProperty = None; | |
-static int got_x_error = FALSE; | |
- | |
-static char_u *empty_prop = (char_u *)""; /* empty GetRegProp() result */ | |
- | |
-/* | |
- * Associate an ASCII name with Vim. Try real hard to get a unique one. | |
- * Returns FAIL or OK. | |
- */ | |
- int | |
-serverRegisterName(dpy, name) | |
- Display *dpy; /* display to register with */ | |
- char_u *name; /* the name that will be used as a base */ | |
-{ | |
- int i; | |
- int res; | |
- char_u *p = NULL; | |
- | |
- res = DoRegisterName(dpy, name); | |
- if (res < 0) | |
- { | |
- i = 1; | |
- do | |
- { | |
- if (res < -1 || i >= 1000) | |
- { | |
- MSG_ATTR(_("Unable to register a command server name"), | |
- hl_attr(HLF_W)); | |
- return FAIL; | |
- } | |
- if (p == NULL) | |
- p = alloc(STRLEN(name) + 10); | |
- if (p == NULL) | |
- { | |
- res = -10; | |
- continue; | |
- } | |
- sprintf((char *)p, "%s%d", name, i++); | |
- res = DoRegisterName(dpy, p); | |
- } | |
- while (res < 0) | |
- ; | |
- vim_free(p); | |
- } | |
- return OK; | |
-} | |
- | |
- static int | |
-DoRegisterName(dpy, name) | |
- Display *dpy; | |
- char_u *name; | |
-{ | |
- Window w; | |
- XErrorHandler old_handler; | |
-#define MAX_NAME_LENGTH 100 | |
- char_u propInfo[MAX_NAME_LENGTH + 20]; | |
- | |
- if (commProperty == None) | |
- { | |
- if (SendInit(dpy) < 0) | |
- return -2; | |
- } | |
- | |
- /* | |
- * Make sure the name is unique, and append info about it to | |
- * the registry property. It's important to lock the server | |
- * here to prevent conflicting changes to the registry property. | |
- * WARNING: Do not step through this while debugging, it will hangup the X | |
- * server! | |
- */ | |
- XGrabServer(dpy); | |
- w = LookupName(dpy, name, FALSE, NULL); | |
- if (w != (Window)0) | |
- { | |
- Status status; | |
- int dummyInt; | |
- unsigned int dummyUns; | |
- Window dummyWin; | |
- | |
- /* | |
- * The name is currently registered. See if the commWindow | |
- * associated with the name exists. If not, or if the commWindow | |
- * is *our* commWindow, then just unregister the old name (this | |
- * could happen if an application dies without cleaning up the | |
- * registry). | |
- */ | |
- old_handler = XSetErrorHandler(x_error_check); | |
- status = XGetGeometry(dpy, w, &dummyWin, &dummyInt, &dummyInt, | |
- &dummyUns, &dummyUns, &dummyUns, &dummyUns); | |
- (void)XSetErrorHandler(old_handler); | |
- if (status != Success && w != commWindow) | |
- { | |
- XUngrabServer(dpy); | |
- XFlush(dpy); | |
- return -1; | |
- } | |
- (void)LookupName(dpy, name, /*delete=*/TRUE, NULL); | |
- } | |
- sprintf((char *)propInfo, "%x %.*s", (int_u)commWindow, | |
- MAX_NAME_LENGTH, name); | |
- old_handler = XSetErrorHandler(x_error_check); | |
- got_x_error = FALSE; | |
- XChangeProperty(dpy, RootWindow(dpy, 0), registryProperty, XA_STRING, 8, | |
- PropModeAppend, propInfo, STRLEN(propInfo) + 1); | |
- XUngrabServer(dpy); | |
- XSync(dpy, False); | |
- (void)XSetErrorHandler(old_handler); | |
- | |
- if (!got_x_error) | |
- { | |
-#ifdef FEAT_EVAL | |
- set_vim_var_string(VV_SEND_SERVER, name, -1); | |
-#endif | |
- serverName = vim_strsave(name); | |
-#ifdef FEAT_TITLE | |
- need_maketitle = TRUE; | |
-#endif | |
- return 0; | |
- } | |
- return -2; | |
-} | |
- | |
-#if defined(FEAT_GUI) || defined(PROTO) | |
-/* | |
- * Clean out new ID from registry and set it as comm win. | |
- * Change any registered window ID. | |
- */ | |
- void | |
-serverChangeRegisteredWindow(dpy, newwin) | |
- Display *dpy; /* Display to register with */ | |
- Window newwin; /* Re-register to this ID */ | |
-{ | |
- char_u propInfo[MAX_NAME_LENGTH + 20]; | |
- | |
- commWindow = newwin; | |
- | |
- /* Always call SendInit() here, to make sure commWindow is marked as a Vim | |
- * window. */ | |
- if (SendInit(dpy) < 0) | |
- return; | |
- | |
- /* WARNING: Do not step through this while debugging, it will hangup the X | |
- * server! */ | |
- XGrabServer(dpy); | |
- DeleteAnyLingerer(dpy, newwin); | |
- if (serverName != NULL) | |
- { | |
- /* Reinsert name if we was already registered */ | |
- (void)LookupName(dpy, serverName, /*delete=*/TRUE, NULL); | |
- sprintf((char *)propInfo, "%x %.*s", | |
- (int_u)newwin, MAX_NAME_LENGTH, serverName); | |
- XChangeProperty(dpy, RootWindow(dpy, 0), registryProperty, XA_STRING, 8, | |
- PropModeAppend, (char_u *)propInfo, | |
- STRLEN(propInfo) + 1); | |
- } | |
- XUngrabServer(dpy); | |
-} | |
-#endif | |
- | |
-/* | |
- * Send to an instance of Vim via the X display. | |
- * Returns 0 for OK, negative for an error. | |
- */ | |
- int | |
-serverSendToVim(dpy, name, cmd, result, server, asExpr, localLoop, silent) | |
- Display *dpy; /* Where to send. */ | |
- char_u *name; /* Where to send. */ | |
- char_u *cmd; /* What to send. */ | |
- char_u **result; /* Result of eval'ed expression */ | |
- Window *server; /* Actual ID of receiving app */ | |
- Bool asExpr; /* Interpret as keystrokes or expr ? */ | |
- Bool localLoop; /* Throw away everything but result */ | |
- int silent; /* don't complain about no server */ | |
-{ | |
- Window w; | |
- char_u *property; | |
- int length; | |
- int res; | |
- static int serial = 0; /* Running count of sent commands. | |
- * Used to give each command a | |
- * different serial number. */ | |
- PendingCommand pending; | |
- char_u *loosename = NULL; | |
- | |
- if (result != NULL) | |
- *result = NULL; | |
- if (name == NULL || *name == NUL) | |
- name = (char_u *)"GVIM"; /* use a default name */ | |
- | |
- if (commProperty == None && dpy != NULL) | |
- { | |
- if (SendInit(dpy) < 0) | |
- return -1; | |
- } | |
- | |
- /* Execute locally if no display or target is ourselves */ | |
- if (dpy == NULL || (serverName != NULL && STRICMP(name, serverName) == 0)) | |
- { | |
- if (asExpr) | |
- { | |
- char_u *ret; | |
- | |
- ret = eval_client_expr_to_string(cmd); | |
- if (result != NULL) | |
- { | |
- if (ret == NULL) | |
- *result = vim_strsave((char_u *)_(e_invexprmsg)); | |
- else | |
- *result = ret; | |
- } | |
- else | |
- vim_free(ret); | |
- return ret == NULL ? -1 : 0; | |
- } | |
- else | |
- server_to_input_buf(cmd); | |
- return 0; | |
- } | |
- | |
- /* | |
- * Bind the server name to a communication window. | |
- * | |
- * Find any survivor with a serialno attached to the name if the | |
- * original registrant of the wanted name is no longer present. | |
- * | |
- * Delete any lingering names from dead editors. | |
- */ | |
- while (TRUE) | |
- { | |
- w = LookupName(dpy, name, FALSE, &loosename); | |
- /* Check that the window is hot */ | |
- if (w != None) | |
- { | |
- if (!WindowValid(dpy, w)) | |
- { | |
- LookupName(dpy, loosename ? loosename : name, | |
- /*DELETE=*/TRUE, NULL); | |
- continue; | |
- } | |
- } | |
- break; | |
- } | |
- if (w == None) | |
- { | |
- if (!silent) | |
- EMSG2(_(e_noserver), name); | |
- return -1; | |
- } | |
- else if (loosename != NULL) | |
- name = loosename; | |
- if (server != NULL) | |
- *server = w; | |
- | |
- /* | |
- * Send the command to target interpreter by appending it to the | |
- * comm window in the communication window. | |
- * Length must be computed exactly! | |
- */ | |
-#ifdef FEAT_MBYTE | |
- length = STRLEN(name) + STRLEN(p_enc) + STRLEN(cmd) + 14; | |
-#else | |
- length = STRLEN(name) + STRLEN(cmd) + 10; | |
-#endif | |
- property = (char_u *)alloc((unsigned)length + 30); | |
- | |
-#ifdef FEAT_MBYTE | |
- sprintf((char *)property, "%c%c%c-n %s%c-E %s%c-s %s", | |
- 0, asExpr ? 'c' : 'k', 0, name, 0, p_enc, 0, cmd); | |
-#else | |
- sprintf((char *)property, "%c%c%c-n %s%c-s %s", | |
- 0, asExpr ? 'c' : 'k', 0, name, 0, cmd); | |
-#endif | |
- if (name == loosename) | |
- vim_free(loosename); | |
- /* Add a back reference to our comm window */ | |
- serial++; | |
- sprintf((char *)property + length, "%c-r %x %d", | |
- 0, (int_u)commWindow, serial); | |
- /* Add length of what "-r %x %d" resulted in, skipping the NUL. */ | |
- length += STRLEN(property + length + 1) + 1; | |
- | |
- res = AppendPropCarefully(dpy, w, commProperty, property, length + 1); | |
- vim_free(property); | |
- if (res < 0) | |
- { | |
- EMSG(_("E248: Failed to send command to the destination program")); | |
- return -1; | |
- } | |
- | |
- if (!asExpr) /* There is no answer for this - Keys are sent async */ | |
- return 0; | |
- | |
- /* | |
- * Register the fact that we're waiting for a command to | |
- * complete (this is needed by SendEventProc and by | |
- * AppendErrorProc to pass back the command's results). | |
- */ | |
- pending.serial = serial; | |
- pending.code = 0; | |
- pending.result = NULL; | |
- pending.nextPtr = pendingCommands; | |
- pendingCommands = &pending; | |
- | |
- ServerWait(dpy, w, WaitForPend, &pending, localLoop, 600); | |
- | |
- /* | |
- * Unregister the information about the pending command | |
- * and return the result. | |
- */ | |
- if (pendingCommands == &pending) | |
- pendingCommands = pending.nextPtr; | |
- else | |
- { | |
- PendingCommand *pcPtr; | |
- | |
- for (pcPtr = pendingCommands; pcPtr != NULL; pcPtr = pcPtr->nextPtr) | |
- if (pcPtr->nextPtr == &pending) | |
- { | |
- pcPtr->nextPtr = pending.nextPtr; | |
- break; | |
- } | |
- } | |
- if (result != NULL) | |
- *result = pending.result; | |
- else | |
- vim_free(pending.result); | |
- | |
- return pending.code == 0 ? 0 : -1; | |
-} | |
- | |
- static int | |
-WaitForPend(p) | |
- void *p; | |
-{ | |
- PendingCommand *pending = (PendingCommand *) p; | |
- return pending->result != NULL; | |
-} | |
- | |
-/* | |
- * Return TRUE if window "w" exists and has a "Vim" property on it. | |
- */ | |
- static int | |
-WindowValid(dpy, w) | |
- Display *dpy; | |
- Window w; | |
-{ | |
- XErrorHandler old_handler; | |
- Atom *plist; | |
- int numProp; | |
- int i; | |
- | |
- old_handler = XSetErrorHandler(x_error_check); | |
- got_x_error = 0; | |
- plist = XListProperties(dpy, w, &numProp); | |
- XSync(dpy, False); | |
- XSetErrorHandler(old_handler); | |
- if (plist == NULL || got_x_error) | |
- return FALSE; | |
- | |
- for (i = 0; i < numProp; i++) | |
- if (plist[i] == vimProperty) | |
- { | |
- XFree(plist); | |
- return TRUE; | |
- } | |
- XFree(plist); | |
- return FALSE; | |
-} | |
- | |
-/* | |
- * Enter a loop processing X events & polling chars until we see a result | |
- */ | |
- static void | |
-ServerWait(dpy, w, endCond, endData, localLoop, seconds) | |
- Display *dpy; | |
- Window w; | |
- EndCond endCond; | |
- void *endData; | |
- int localLoop; | |
- int seconds; | |
-{ | |
- time_t start; | |
- time_t now; | |
- time_t lastChk = 0; | |
- XEvent event; | |
- XPropertyEvent *e = (XPropertyEvent *)&event; | |
-# define SEND_MSEC_POLL 50 | |
- | |
- time(&start); | |
- while (endCond(endData) == 0) | |
- { | |
- time(&now); | |
- if (seconds >= 0 && (now - start) >= seconds) | |
- break; | |
- if (now != lastChk) | |
- { | |
- lastChk = now; | |
- if (!WindowValid(dpy, w)) | |
- break; | |
- /* | |
- * Sometimes the PropertyChange event doesn't come. | |
- * This can be seen in eg: vim -c 'echo remote_expr("gvim", "3+2")' | |
- */ | |
- serverEventProc(dpy, NULL); | |
- } | |
- if (localLoop) | |
- { | |
- /* Just look out for the answer without calling back into Vim */ | |
-#ifndef HAVE_SELECT | |
- struct pollfd fds; | |
- | |
- fds.fd = ConnectionNumber(dpy); | |
- fds.events = POLLIN; | |
- if (poll(&fds, 1, SEND_MSEC_POLL) < 0) | |
- break; | |
-#else | |
- fd_set fds; | |
- struct timeval tv; | |
- | |
- tv.tv_sec = 0; | |
- tv.tv_usec = SEND_MSEC_POLL * 1000; | |
- FD_ZERO(&fds); | |
- FD_SET(ConnectionNumber(dpy), &fds); | |
- if (select(ConnectionNumber(dpy) + 1, &fds, NULL, NULL, &tv) < 0) | |
- break; | |
-#endif | |
- while (XEventsQueued(dpy, QueuedAfterReading) > 0) | |
- { | |
- XNextEvent(dpy, &event); | |
- if (event.type == PropertyNotify && e->window == commWindow) | |
- serverEventProc(dpy, &event); | |
- } | |
- } | |
- else | |
- { | |
- if (got_int) | |
- break; | |
- ui_delay((long)SEND_MSEC_POLL, TRUE); | |
- ui_breakcheck(); | |
- } | |
- } | |
-} | |
- | |
- | |
-/* | |
- * Fetch a list of all the Vim instance names currently registered for the | |
- * display. | |
- * | |
- * Returns a newline separated list in allocated memory or NULL. | |
- */ | |
- char_u * | |
-serverGetVimNames(dpy) | |
- Display *dpy; | |
-{ | |
- char_u *regProp; | |
- char_u *entry; | |
- char_u *p; | |
- long_u numItems; | |
- int_u w; | |
- garray_T ga; | |
- | |
- if (registryProperty == None) | |
- { | |
- if (SendInit(dpy) < 0) | |
- return NULL; | |
- } | |
- ga_init2(&ga, 1, 100); | |
- | |
- /* | |
- * Read the registry property. | |
- */ | |
- if (GetRegProp(dpy, ®Prop, &numItems, TRUE) == FAIL) | |
- return NULL; | |
- | |
- /* | |
- * Scan all of the names out of the property. | |
- */ | |
- ga_init2(&ga, 1, 100); | |
- for (p = regProp; (long_u)(p - regProp) < numItems; p++) | |
- { | |
- entry = p; | |
- while (*p != 0 && !isspace(*p)) | |
- p++; | |
- if (*p != 0) | |
- { | |
- w = None; | |
- sscanf((char *)entry, "%x", &w); | |
- if (WindowValid(dpy, (Window)w)) | |
- { | |
- ga_concat(&ga, p + 1); | |
- ga_concat(&ga, (char_u *)"\n"); | |
- } | |
- while (*p != 0) | |
- p++; | |
- } | |
- } | |
- if (regProp != empty_prop) | |
- XFree(regProp); | |
- ga_append(&ga, NUL); | |
- return ga.ga_data; | |
-} | |
- | |
-/* ---------------------------------------------------------- | |
- * Reply stuff | |
- */ | |
- | |
- static struct ServerReply * | |
-ServerReplyFind(w, op) | |
- Window w; | |
- enum ServerReplyOp op; | |
-{ | |
- struct ServerReply *p; | |
- struct ServerReply e; | |
- int i; | |
- | |
- p = (struct ServerReply *) serverReply.ga_data; | |
- for (i = 0; i < serverReply.ga_len; i++, p++) | |
- if (p->id == w) | |
- break; | |
- if (i >= serverReply.ga_len) | |
- p = NULL; | |
- | |
- if (p == NULL && op == SROP_Add) | |
- { | |
- if (serverReply.ga_growsize == 0) | |
- ga_init2(&serverReply, sizeof(struct ServerReply), 1); | |
- if (ga_grow(&serverReply, 1) == OK) | |
- { | |
- p = ((struct ServerReply *) serverReply.ga_data) | |
- + serverReply.ga_len; | |
- e.id = w; | |
- ga_init2(&e.strings, 1, 100); | |
- mch_memmove(p, &e, sizeof(e)); | |
- serverReply.ga_len++; | |
- } | |
- } | |
- else if (p != NULL && op == SROP_Delete) | |
- { | |
- ga_clear(&p->strings); | |
- mch_memmove(p, p + 1, (serverReply.ga_len - i - 1) * sizeof(*p)); | |
- serverReply.ga_len--; | |
- } | |
- | |
- return p; | |
-} | |
- | |
-/* | |
- * Convert string to windowid. | |
- * Issue an error if the id is invalid. | |
- */ | |
- Window | |
-serverStrToWin(str) | |
- char_u *str; | |
-{ | |
- unsigned id = None; | |
- | |
- sscanf((char *)str, "0x%x", &id); | |
- if (id == None) | |
- EMSG2(_("E573: Invalid server id used: %s"), str); | |
- | |
- return (Window)id; | |
-} | |
- | |
-/* | |
- * Send a reply string (notification) to client with id "name". | |
- * Return -1 if the window is invalid. | |
- */ | |
- int | |
-serverSendReply(name, str) | |
- char_u *name; | |
- char_u *str; | |
-{ | |
- char_u *property; | |
- int length; | |
- int res; | |
- Display *dpy = X_DISPLAY; | |
- Window win = serverStrToWin(name); | |
- | |
- if (commProperty == None) | |
- { | |
- if (SendInit(dpy) < 0) | |
- return -2; | |
- } | |
- if (!WindowValid(dpy, win)) | |
- return -1; | |
- | |
-#ifdef FEAT_MBYTE | |
- length = STRLEN(p_enc) + STRLEN(str) + 14; | |
-#else | |
- length = STRLEN(str) + 10; | |
-#endif | |
- if ((property = (char_u *)alloc((unsigned)length + 30)) != NULL) | |
- { | |
-#ifdef FEAT_MBYTE | |
- sprintf((char *)property, "%cn%c-E %s%c-n %s%c-w %x", | |
- 0, 0, p_enc, 0, str, 0, (unsigned int)commWindow); | |
-#else | |
- sprintf((char *)property, "%cn%c-n %s%c-w %x", | |
- 0, 0, str, 0, (unsigned int)commWindow); | |
-#endif | |
- /* Add length of what "%x" resulted in. */ | |
- length += STRLEN(property + length); | |
- res = AppendPropCarefully(dpy, win, commProperty, property, length + 1); | |
- vim_free(property); | |
- return res; | |
- } | |
- return -1; | |
-} | |
- | |
- static int | |
-WaitForReply(p) | |
- void *p; | |
-{ | |
- Window *w = (Window *) p; | |
- return ServerReplyFind(*w, SROP_Find) != NULL; | |
-} | |
- | |
-/* | |
- * Wait for replies from id (win) | |
- * Return 0 and the malloc'ed string when a reply is available. | |
- * Return -1 if the window becomes invalid while waiting. | |
- */ | |
- int | |
-serverReadReply(dpy, win, str, localLoop) | |
- Display *dpy; | |
- Window win; | |
- char_u **str; | |
- int localLoop; | |
-{ | |
- int len; | |
- char_u *s; | |
- struct ServerReply *p; | |
- | |
- ServerWait(dpy, win, WaitForReply, &win, localLoop, -1); | |
- | |
- if ((p = ServerReplyFind(win, SROP_Find)) != NULL && p->strings.ga_len > 0) | |
- { | |
- *str = vim_strsave(p->strings.ga_data); | |
- len = STRLEN(*str) + 1; | |
- if (len < p->strings.ga_len) | |
- { | |
- s = (char_u *) p->strings.ga_data; | |
- mch_memmove(s, s + len, p->strings.ga_len - len); | |
- p->strings.ga_len -= len; | |
- } | |
- else | |
- { | |
- /* Last string read. Remove from list */ | |
- ga_clear(&p->strings); | |
- ServerReplyFind(win, SROP_Delete); | |
- } | |
- return 0; | |
- } | |
- return -1; | |
-} | |
- | |
-/* | |
- * Check for replies from id (win). | |
- * Return TRUE and a non-malloc'ed string if there is. Else return FALSE. | |
- */ | |
- int | |
-serverPeekReply(dpy, win, str) | |
- Display *dpy; | |
- Window win; | |
- char_u **str; | |
-{ | |
- struct ServerReply *p; | |
- | |
- if ((p = ServerReplyFind(win, SROP_Find)) != NULL && p->strings.ga_len > 0) | |
- { | |
- if (str != NULL) | |
- *str = p->strings.ga_data; | |
- return 1; | |
- } | |
- if (!WindowValid(dpy, win)) | |
- return -1; | |
- return 0; | |
-} | |
- | |
- | |
-/* | |
- * Initialize the communication channels for sending commands and receiving | |
- * results. | |
- */ | |
- static int | |
-SendInit(dpy) | |
- Display *dpy; | |
-{ | |
- XErrorHandler old_handler; | |
- | |
- /* | |
- * Create the window used for communication, and set up an | |
- * event handler for it. | |
- */ | |
- old_handler = XSetErrorHandler(x_error_check); | |
- got_x_error = FALSE; | |
- | |
- if (commProperty == None) | |
- commProperty = XInternAtom(dpy, "Comm", False); | |
- if (vimProperty == None) | |
- vimProperty = XInternAtom(dpy, "Vim", False); | |
- if (registryProperty == None) | |
- registryProperty = XInternAtom(dpy, "VimRegistry", False); | |
- | |
- if (commWindow == None) | |
- { | |
- commWindow = XCreateSimpleWindow(dpy, XDefaultRootWindow(dpy), | |
- getpid(), 0, 10, 10, 0, | |
- WhitePixel(dpy, DefaultScreen(dpy)), | |
- WhitePixel(dpy, DefaultScreen(dpy))); | |
- XSelectInput(dpy, commWindow, PropertyChangeMask); | |
- /* WARNING: Do not step through this while debugging, it will hangup | |
- * the X server! */ | |
- XGrabServer(dpy); | |
- DeleteAnyLingerer(dpy, commWindow); | |
- XUngrabServer(dpy); | |
- } | |
- | |
- /* Make window recognizable as a vim window */ | |
- XChangeProperty(dpy, commWindow, vimProperty, XA_STRING, | |
- 8, PropModeReplace, (char_u *)VIM_VERSION_SHORT, | |
- (int)STRLEN(VIM_VERSION_SHORT) + 1); | |
- | |
- XSync(dpy, False); | |
- (void)XSetErrorHandler(old_handler); | |
- | |
- return got_x_error ? -1 : 0; | |
-} | |
- | |
-/* | |
- * Given a server name, see if the name exists in the registry for a | |
- * particular display. | |
- * | |
- * If the given name is registered, return the ID of the window associated | |
- * with the name. If the name isn't registered, then return 0. | |
- * | |
- * Side effects: | |
- * If the registry property is improperly formed, then it is deleted. | |
- * If "delete" is non-zero, then if the named server is found it is | |
- * removed from the registry property. | |
- */ | |
- static Window | |
-LookupName(dpy, name, delete, loose) | |
- Display *dpy; /* Display whose registry to check. */ | |
- char_u *name; /* Name of a server. */ | |
- int delete; /* If non-zero, delete info about name. */ | |
- char_u **loose; /* Do another search matching -999 if not found | |
- Return result here if a match is found */ | |
-{ | |
- char_u *regProp, *entry; | |
- char_u *p; | |
- long_u numItems; | |
- int_u returnValue; | |
- | |
- /* | |
- * Read the registry property. | |
- */ | |
- if (GetRegProp(dpy, ®Prop, &numItems, FALSE) == FAIL) | |
- return 0; | |
- | |
- /* | |
- * Scan the property for the desired name. | |
- */ | |
- returnValue = (int_u)None; | |
- entry = NULL; /* Not needed, but eliminates compiler warning. */ | |
- for (p = regProp; (long_u)(p - regProp) < numItems; ) | |
- { | |
- entry = p; | |
- while (*p != 0 && !isspace(*p)) | |
- p++; | |
- if (*p != 0 && STRICMP(name, p + 1) == 0) | |
- { | |
- sscanf((char *)entry, "%x", &returnValue); | |
- break; | |
- } | |
- while (*p != 0) | |
- p++; | |
- p++; | |
- } | |
- | |
- if (loose != NULL && returnValue == (int_u)None && !IsSerialName(name)) | |
- { | |
- for (p = regProp; (long_u)(p - regProp) < numItems; ) | |
- { | |
- entry = p; | |
- while (*p != 0 && !isspace(*p)) | |
- p++; | |
- if (*p != 0 && IsSerialName(p + 1) | |
- && STRNICMP(name, p + 1, STRLEN(name)) == 0) | |
- { | |
- sscanf((char *)entry, "%x", &returnValue); | |
- *loose = vim_strsave(p + 1); | |
- break; | |
- } | |
- while (*p != 0) | |
- p++; | |
- p++; | |
- } | |
- } | |
- | |
- /* | |
- * Delete the property, if that is desired (copy down the | |
- * remainder of the registry property to overlay the deleted | |
- * info, then rewrite the property). | |
- */ | |
- if (delete && returnValue != (int_u)None) | |
- { | |
- int count; | |
- | |
- while (*p != 0) | |
- p++; | |
- p++; | |
- count = numItems - (p - regProp); | |
- if (count > 0) | |
- mch_memmove(entry, p, count); | |
- XChangeProperty(dpy, RootWindow(dpy, 0), registryProperty, XA_STRING, | |
- 8, PropModeReplace, regProp, | |
- (int)(numItems - (p - entry))); | |
- XSync(dpy, False); | |
- } | |
- | |
- if (regProp != empty_prop) | |
- XFree(regProp); | |
- return (Window)returnValue; | |
-} | |
- | |
-/* | |
- * Delete any lingering occurrence of window id. We promise that any | |
- * occurrence is not ours since it is not yet put into the registry (by us) | |
- * | |
- * This is necessary in the following scenario: | |
- * 1. There is an old windowid for an exit'ed vim in the registry | |
- * 2. We get that id for our commWindow but only want to send, not register. | |
- * 3. The window will mistakenly be regarded valid because of own commWindow | |
- */ | |
- static void | |
-DeleteAnyLingerer(dpy, win) | |
- Display *dpy; /* Display whose registry to check. */ | |
- Window win; /* Window to remove */ | |
-{ | |
- char_u *regProp, *entry = NULL; | |
- char_u *p; | |
- long_u numItems; | |
- int_u wwin; | |
- | |
- /* | |
- * Read the registry property. | |
- */ | |
- if (GetRegProp(dpy, ®Prop, &numItems, FALSE) == FAIL) | |
- return; | |
- | |
- /* Scan the property for the window id. */ | |
- for (p = regProp; (long_u)(p - regProp) < numItems; ) | |
- { | |
- if (*p != 0) | |
- { | |
- sscanf((char *)p, "%x", &wwin); | |
- if ((Window)wwin == win) | |
- { | |
- int lastHalf; | |
- | |
- /* Copy down the remainder to delete entry */ | |
- entry = p; | |
- while (*p != 0) | |
- p++; | |
- p++; | |
- lastHalf = numItems - (p - regProp); | |
- if (lastHalf > 0) | |
- mch_memmove(entry, p, lastHalf); | |
- numItems = (entry - regProp) + lastHalf; | |
- p = entry; | |
- continue; | |
- } | |
- } | |
- while (*p != 0) | |
- p++; | |
- p++; | |
- } | |
- | |
- if (entry != NULL) | |
- { | |
- XChangeProperty(dpy, RootWindow(dpy, 0), registryProperty, | |
- XA_STRING, 8, PropModeReplace, regProp, | |
- (int)(p - regProp)); | |
- XSync(dpy, False); | |
- } | |
- | |
- if (regProp != empty_prop) | |
- XFree(regProp); | |
-} | |
- | |
-/* | |
- * Read the registry property. Delete it when it's formatted wrong. | |
- * Return the property in "regPropp". "empty_prop" is used when it doesn't | |
- * exist yet. | |
- * Return OK when successful. | |
- */ | |
- static int | |
-GetRegProp(dpy, regPropp, numItemsp, domsg) | |
- Display *dpy; | |
- char_u **regPropp; | |
- long_u *numItemsp; | |
- int domsg; /* When TRUE give error message. */ | |
-{ | |
- int result, actualFormat; | |
- long_u bytesAfter; | |
- Atom actualType; | |
- XErrorHandler old_handler; | |
- | |
- *regPropp = NULL; | |
- old_handler = XSetErrorHandler(x_error_check); | |
- got_x_error = FALSE; | |
- | |
- result = XGetWindowProperty(dpy, RootWindow(dpy, 0), registryProperty, 0L, | |
- (long)MAX_PROP_WORDS, False, | |
- XA_STRING, &actualType, | |
- &actualFormat, numItemsp, &bytesAfter, | |
- regPropp); | |
- | |
- XSync(dpy, FALSE); | |
- (void)XSetErrorHandler(old_handler); | |
- if (got_x_error) | |
- return FAIL; | |
- | |
- if (actualType == None) | |
- { | |
- /* No prop yet. Logically equal to the empty list */ | |
- *numItemsp = 0; | |
- *regPropp = empty_prop; | |
- return OK; | |
- } | |
- | |
- /* If the property is improperly formed, then delete it. */ | |
- if (result != Success || actualFormat != 8 || actualType != XA_STRING) | |
- { | |
- if (*regPropp != NULL) | |
- XFree(*regPropp); | |
- XDeleteProperty(dpy, RootWindow(dpy, 0), registryProperty); | |
- if (domsg) | |
- EMSG(_("E251: VIM instance registry property is badly formed. Deleted!")); | |
- return FAIL; | |
- } | |
- return OK; | |
-} | |
- | |
-/* | |
- * This procedure is invoked by the various X event loops throughout Vims when | |
- * a property changes on the communication window. This procedure reads the | |
- * property and handles command requests and responses. | |
- */ | |
- void | |
-serverEventProc(dpy, eventPtr) | |
- Display *dpy; | |
- XEvent *eventPtr; /* Information about event. */ | |
-{ | |
- char_u *propInfo; | |
- char_u *p; | |
- int result, actualFormat, code; | |
- long_u numItems, bytesAfter; | |
- Atom actualType; | |
- char_u *tofree; | |
- | |
- if (eventPtr != NULL) | |
- { | |
- if (eventPtr->xproperty.atom != commProperty | |
- || eventPtr->xproperty.state != PropertyNewValue) | |
- return; | |
- } | |
- | |
- /* | |
- * Read the comm property and delete it. | |
- */ | |
- propInfo = NULL; | |
- result = XGetWindowProperty(dpy, commWindow, commProperty, 0L, | |
- (long)MAX_PROP_WORDS, True, | |
- XA_STRING, &actualType, | |
- &actualFormat, &numItems, &bytesAfter, | |
- &propInfo); | |
- | |
- /* If the property doesn't exist or is improperly formed then ignore it. */ | |
- if (result != Success || actualType != XA_STRING || actualFormat != 8) | |
- { | |
- if (propInfo != NULL) | |
- XFree(propInfo); | |
- return; | |
- } | |
- | |
- /* | |
- * Several commands and results could arrive in the property at | |
- * one time; each iteration through the outer loop handles a | |
- * single command or result. | |
- */ | |
- for (p = propInfo; (long_u)(p - propInfo) < numItems; ) | |
- { | |
- /* | |
- * Ignore leading NULs; each command or result starts with a | |
- * NUL so that no matter how badly formed a preceding command | |
- * is, we'll be able to tell that a new command/result is | |
- * starting. | |
- */ | |
- if (*p == 0) | |
- { | |
- p++; | |
- continue; | |
- } | |
- | |
- if ((*p == 'c' || *p == 'k') && (p[1] == 0)) | |
- { | |
- Window resWindow; | |
- char_u *name, *script, *serial, *end, *res; | |
- Bool asKeys = *p == 'k'; | |
- garray_T reply; | |
- char_u *enc; | |
- | |
- /* | |
- * This is an incoming command from some other application. | |
- * Iterate over all of its options. Stop when we reach | |
- * the end of the property or something that doesn't look | |
- * like an option. | |
- */ | |
- p += 2; | |
- name = NULL; | |
- resWindow = None; | |
- serial = (char_u *)""; | |
- script = NULL; | |
- enc = NULL; | |
- while ((long_u)(p - propInfo) < numItems && *p == '-') | |
- { | |
- switch (p[1]) | |
- { | |
- case 'r': | |
- end = skipwhite(p + 2); | |
- resWindow = 0; | |
- while (vim_isxdigit(*end)) | |
- { | |
- resWindow = 16 * resWindow + (long_u)hex2nr(*end); | |
- ++end; | |
- } | |
- if (end == p + 2 || *end != ' ') | |
- resWindow = None; | |
- else | |
- { | |
- p = serial = end + 1; | |
- clientWindow = resWindow; /* Remember in global */ | |
- } | |
- break; | |
- case 'n': | |
- if (p[2] == ' ') | |
- name = p + 3; | |
- break; | |
- case 's': | |
- if (p[2] == ' ') | |
- script = p + 3; | |
- break; | |
- case 'E': | |
- if (p[2] == ' ') | |
- enc = p + 3; | |
- break; | |
- } | |
- while (*p != 0) | |
- p++; | |
- p++; | |
- } | |
- | |
- if (script == NULL || name == NULL) | |
- continue; | |
- | |
- /* | |
- * Initialize the result property, so that we're ready at any | |
- * time if we need to return an error. | |
- */ | |
- if (resWindow != None) | |
- { | |
- ga_init2(&reply, 1, 100); | |
-#ifdef FEAT_MBYTE | |
- ga_grow(&reply, 50 + STRLEN(p_enc)); | |
- sprintf(reply.ga_data, "%cr%c-E %s%c-s %s%c-r ", | |
- 0, 0, p_enc, 0, serial, 0); | |
- reply.ga_len = 14 + STRLEN(p_enc) + STRLEN(serial); | |
-#else | |
- ga_grow(&reply, 50); | |
- sprintf(reply.ga_data, "%cr%c-s %s%c-r ", 0, 0, serial, 0); | |
- reply.ga_len = 10 + STRLEN(serial); | |
-#endif | |
- } | |
- res = NULL; | |
- if (serverName != NULL && STRICMP(name, serverName) == 0) | |
- { | |
- script = serverConvert(enc, script, &tofree); | |
- if (asKeys) | |
- server_to_input_buf(script); | |
- else | |
- res = eval_client_expr_to_string(script); | |
- vim_free(tofree); | |
- } | |
- if (resWindow != None) | |
- { | |
- if (res != NULL) | |
- ga_concat(&reply, res); | |
- else if (asKeys == 0) | |
- { | |
- ga_concat(&reply, (char_u *)_(e_invexprmsg)); | |
- ga_append(&reply, 0); | |
- ga_concat(&reply, (char_u *)"-c 1"); | |
- } | |
- ga_append(&reply, NUL); | |
- (void)AppendPropCarefully(dpy, resWindow, commProperty, | |
- reply.ga_data, reply.ga_len); | |
- ga_clear(&reply); | |
- } | |
- vim_free(res); | |
- } | |
- else if (*p == 'r' && p[1] == 0) | |
- { | |
- int serial, gotSerial; | |
- char_u *res; | |
- PendingCommand *pcPtr; | |
- char_u *enc; | |
- | |
- /* | |
- * This is a reply to some command that we sent out. Iterate | |
- * over all of its options. Stop when we reach the end of the | |
- * property or something that doesn't look like an option. | |
- */ | |
- p += 2; | |
- gotSerial = 0; | |
- res = (char_u *)""; | |
- code = 0; | |
- enc = NULL; | |
- while ((long_u)(p - propInfo) < numItems && *p == '-') | |
- { | |
- switch (p[1]) | |
- { | |
- case 'r': | |
- if (p[2] == ' ') | |
- res = p + 3; | |
- break; | |
- case 'E': | |
- if (p[2] == ' ') | |
- enc = p + 3; | |
- break; | |
- case 's': | |
- if (sscanf((char *)p + 2, " %d", &serial) == 1) | |
- gotSerial = 1; | |
- break; | |
- case 'c': | |
- if (sscanf((char *)p + 2, " %d", &code) != 1) | |
- code = 0; | |
- break; | |
- } | |
- while (*p != 0) | |
- p++; | |
- p++; | |
- } | |
- | |
- if (!gotSerial) | |
- continue; | |
- | |
- /* | |
- * Give the result information to anyone who's | |
- * waiting for it. | |
- */ | |
- for (pcPtr = pendingCommands; pcPtr != NULL; pcPtr = pcPtr->nextPtr) | |
- { | |
- if (serial != pcPtr->serial || pcPtr->result != NULL) | |
- continue; | |
- | |
- pcPtr->code = code; | |
- if (res != NULL) | |
- { | |
- res = serverConvert(enc, res, &tofree); | |
- if (tofree == NULL) | |
- res = vim_strsave(res); | |
- pcPtr->result = res; | |
- } | |
- else | |
- pcPtr->result = vim_strsave((char_u *)""); | |
- break; | |
- } | |
- } | |
- else if (*p == 'n' && p[1] == 0) | |
- { | |
- Window win = 0; | |
- unsigned int u; | |
- int gotWindow; | |
- char_u *str; | |
- struct ServerReply *r; | |
- char_u *enc; | |
- | |
- /* | |
- * This is a (n)otification. Sent with serverreply_send in VimL. | |
- * Execute any autocommand and save it for later retrieval | |
- */ | |
- p += 2; | |
- gotWindow = 0; | |
- str = (char_u *)""; | |
- enc = NULL; | |
- while ((long_u)(p - propInfo) < numItems && *p == '-') | |
- { | |
- switch (p[1]) | |
- { | |
- case 'n': | |
- if (p[2] == ' ') | |
- str = p + 3; | |
- break; | |
- case 'E': | |
- if (p[2] == ' ') | |
- enc = p + 3; | |
- break; | |
- case 'w': | |
- if (sscanf((char *)p + 2, " %x", &u) == 1) | |
- { | |
- win = u; | |
- gotWindow = 1; | |
- } | |
- break; | |
- } | |
- while (*p != 0) | |
- p++; | |
- p++; | |
- } | |
- | |
- if (!gotWindow) | |
- continue; | |
- str = serverConvert(enc, str, &tofree); | |
- if ((r = ServerReplyFind(win, SROP_Add)) != NULL) | |
- { | |
- ga_concat(&(r->strings), str); | |
- ga_append(&(r->strings), NUL); | |
- } | |
-#ifdef FEAT_AUTOCMD | |
- { | |
- char_u winstr[30]; | |
- | |
- sprintf((char *)winstr, "0x%x", (unsigned int)win); | |
- apply_autocmds(EVENT_REMOTEREPLY, winstr, str, TRUE, curbuf); | |
- } | |
-#endif | |
- vim_free(tofree); | |
- } | |
- else | |
- { | |
- /* | |
- * Didn't recognize this thing. Just skip through the next | |
- * null character and try again. | |
- * Even if we get an 'r'(eply) we will throw it away as we | |
- * never specify (and thus expect) one | |
- */ | |
- while (*p != 0) | |
- p++; | |
- p++; | |
- } | |
- } | |
- XFree(propInfo); | |
-} | |
- | |
-/* | |
- * Append a given property to a given window, but set up an X error handler so | |
- * that if the append fails this procedure can return an error code rather | |
- * than having Xlib panic. | |
- * Return: 0 for OK, -1 for error | |
- */ | |
- static int | |
-AppendPropCarefully(dpy, window, property, value, length) | |
- Display *dpy; /* Display on which to operate. */ | |
- Window window; /* Window whose property is to be modified. */ | |
- Atom property; /* Name of property. */ | |
- char_u *value; /* Characters to append to property. */ | |
- int length; /* How much to append */ | |
-{ | |
- XErrorHandler old_handler; | |
- | |
- old_handler = XSetErrorHandler(x_error_check); | |
- got_x_error = FALSE; | |
- XChangeProperty(dpy, window, property, XA_STRING, 8, | |
- PropModeAppend, value, length); | |
- XSync(dpy, False); | |
- (void) XSetErrorHandler(old_handler); | |
- return got_x_error ? -1 : 0; | |
-} | |
- | |
- | |
-/* | |
- * Another X Error handler, just used to check for errors. | |
- */ | |
- static int | |
-x_error_check(dpy, error_event) | |
- Display *dpy UNUSED; | |
- XErrorEvent *error_event UNUSED; | |
-{ | |
- got_x_error = TRUE; | |
- return 0; | |
-} | |
- | |
-/* | |
- * Check if "str" looks like it had a serial number appended. | |
- * Actually just checks if the name ends in a digit. | |
- */ | |
- static int | |
-IsSerialName(str) | |
- char_u *str; | |
-{ | |
- int len = STRLEN(str); | |
- | |
- return (len > 1 && vim_isdigit(str[len - 1])); | |
-} | |
-#endif /* FEAT_CLIENTSERVER */ | |
diff -r 15b934a16641 -r 2082fc32d223 src/main.c | |
--- a/src/main.c Wed Sep 14 19:04:40 2011 +0200 | |
+++ b/src/main.c Thu Sep 15 21:25:11 2011 +0900 | |
@@ -72,7 +72,6 @@ | |
char_u *serverName_arg; /* cmdline arg for server name */ | |
char_u *serverStr; /* remote server command */ | |
char_u *serverStrEnc; /* encoding of serverStr */ | |
- char_u *servername; /* allocated name for our server */ | |
#endif | |
#if (!defined(UNIX) && !defined(__EMX__)) || defined(ARCHIE) | |
int literal; /* don't expand file names */ | |
@@ -3440,13 +3439,10 @@ | |
exec_on_server(parmp) | |
mparm_T *parmp; | |
{ | |
+ char_u *servername; | |
+ | |
if (parmp->serverName_arg == NULL || *parmp->serverName_arg != NUL) | |
{ | |
-# ifdef WIN32 | |
- /* Initialise the client/server messaging infrastructure. */ | |
- serverInitMessaging(); | |
-# endif | |
- | |
/* | |
* When a command server argument was found, execute it. This may | |
* exit Vim when it was successful. Otherwise it's executed further | |
@@ -3461,18 +3457,17 @@ | |
# endif | |
} | |
+ cmdsrv_init(); | |
+ | |
/* If we're still running, get the name to register ourselves. | |
- * On Win32 can register right now, for X11 need to setup the | |
- * clipboard first, it's further down. */ | |
- parmp->servername = serverMakeName(parmp->serverName_arg, | |
- parmp->argv[0]); | |
-# ifdef WIN32 | |
- if (parmp->servername != NULL) | |
+ * And register server */ | |
+ servername = serverMakeName(parmp->serverName_arg, parmp->argv[0]); | |
+ if (servername != NULL) | |
{ | |
- serverSetName(parmp->servername); | |
- vim_free(parmp->servername); | |
+ cmdsrv_register_name(servername); | |
+ vim_free(servername); | |
+ TIME_MSG("register server name"); | |
} | |
-# endif | |
} | |
} | |
@@ -3483,31 +3478,6 @@ | |
prepare_server(parmp) | |
mparm_T *parmp; | |
{ | |
-# if defined(FEAT_X11) | |
- /* | |
- * Register for remote command execution with :serversend and --remote | |
- * unless there was a -X or a --servername '' on the command line. | |
- * Only register nongui-vim's with an explicit --servername argument. | |
- * When running as root --servername is also required. | |
- */ | |
- if (X_DISPLAY != NULL && parmp->servername != NULL && ( | |
-# ifdef FEAT_GUI | |
- (gui.in_use | |
-# ifdef UNIX | |
- && getuid() != ROOT_UID | |
-# endif | |
- ) || | |
-# endif | |
- parmp->serverName_arg != NULL)) | |
- { | |
- (void)serverRegisterName(X_DISPLAY, parmp->servername); | |
- vim_free(parmp->servername); | |
- TIME_MSG("register server name"); | |
- } | |
- else | |
- serverDelayedStartName = parmp->servername; | |
-# endif | |
- | |
/* | |
* Execute command ourselves if we're here because the send failed (or | |
* else we would have exited above). | |
@@ -3532,6 +3502,7 @@ | |
char_u *res; | |
int i; | |
char_u *sname; | |
+ char_u *serverid; | |
int ret; | |
int didone = FALSE; | |
int exiterr = 0; | |
@@ -3545,18 +3516,21 @@ | |
#define ARGTYPE_SEND 3 | |
int silent = FALSE; | |
int tabs = FALSE; | |
-# ifndef FEAT_X11 | |
- HWND srv; | |
-# else | |
- Window srv; | |
- | |
- setup_term_clip(); | |
-# endif | |
+ | |
+ /* start temporary server to receive reply */ | |
+ if (cmdsrv_init() != 0) | |
+ return; | |
+ | |
+ if (cmdsrv_register_name((char_u *)CMDSRV_TMPNAME) != 0) | |
+ return; | |
sname = serverMakeName(serverName_arg, argv[0]); | |
if (sname == NULL) | |
return; | |
+ /* If server is not found, show error later. */ | |
+ serverid = cmdsrv_find_server(sname, TRUE); | |
+ | |
/* | |
* Execute the command server related arguments and remove them | |
* from the argc/argv array; We may have to return into main() | |
@@ -3630,20 +3604,17 @@ | |
} | |
Argc = i; | |
} | |
-# ifdef FEAT_X11 | |
- if (xterm_dpy == NULL) | |
+ if (serverid == NULL) | |
{ | |
- mch_errmsg(_("No display")); | |
+ if (!silent) | |
+ EMSG2(_(e_noserver), sname); | |
ret = -1; | |
} | |
else | |
- ret = serverSendToVim(xterm_dpy, sname, *serverStr, | |
- NULL, &srv, 0, 0, silent); | |
-# else | |
- /* Win32 always works? */ | |
- ret = serverSendToVim(sname, *serverStr, NULL, &srv, 0, silent); | |
-# endif | |
- if (ret < 0) | |
+ { | |
+ ret = cmdsrv_send_keys(serverid, *serverStr); | |
+ } | |
+ if (ret != 0) | |
{ | |
if (argtype == ARGTYPE_SEND) | |
{ | |
@@ -3660,85 +3631,56 @@ | |
# ifdef FEAT_GUI_W32 | |
/* Guess that when the server name starts with "g" it's a GUI | |
- * server, which we can bring to the foreground here. | |
- * Foreground() in the server doesn't work very well. */ | |
- if (argtype != ARGTYPE_SEND && TOUPPER_ASC(*sname) == 'G') | |
- SetForegroundWindow(srv); | |
+ * server, which we can bring to the foreground here. */ | |
+ if (argtype != ARGTYPE_SEND && TOUPPER_ASC(*serverid) == 'G') | |
+ cmdsrv_foreground(serverid); | |
# endif | |
/* | |
* For --remote-wait: Wait until the server did edit each | |
* file. Also detect that the server no longer runs. | |
*/ | |
- if (ret >= 0 && argtype == ARGTYPE_EDIT_WAIT) | |
+ if (argtype == ARGTYPE_EDIT_WAIT) | |
{ | |
int numFiles = *argc - i - 1; | |
int j; | |
char_u *done = alloc(numFiles); | |
char_u *p; | |
-# ifdef FEAT_GUI_W32 | |
- NOTIFYICONDATA ni; | |
- int count = 0; | |
- extern HWND message_window; | |
-# endif | |
if (numFiles > 0 && argv[i + 1][0] == '+') | |
/* Skip "+cmd" argument, don't wait for it to be edited. */ | |
--numFiles; | |
-# ifdef FEAT_GUI_W32 | |
- ni.cbSize = sizeof(ni); | |
- ni.hWnd = message_window; | |
- ni.uID = 0; | |
- ni.uFlags = NIF_ICON|NIF_TIP; | |
- ni.hIcon = LoadIcon((HINSTANCE)GetModuleHandle(0), "IDR_VIM"); | |
- sprintf(ni.szTip, _("%d of %d edited"), count, numFiles); | |
- Shell_NotifyIcon(NIM_ADD, &ni); | |
-# endif | |
- | |
/* Wait for all files to unload in remote */ | |
vim_memset(done, 0, numFiles); | |
while (memchr(done, 0, numFiles) != NULL) | |
{ | |
-# ifdef WIN32 | |
- p = serverGetReply(srv, NULL, TRUE, TRUE); | |
+ if (cmdsrv_wait_reply(serverid, &p) != 0) | |
+ break; | |
if (p == NULL) | |
break; | |
-# else | |
- if (serverReadReply(xterm_dpy, srv, &p, TRUE) < 0) | |
- break; | |
-# endif | |
j = atoi((char *)p); | |
if (j >= 0 && j < numFiles) | |
{ | |
-# ifdef FEAT_GUI_W32 | |
- ++count; | |
- sprintf(ni.szTip, _("%d of %d edited"), | |
- count, numFiles); | |
- Shell_NotifyIcon(NIM_MODIFY, &ni); | |
-# endif | |
done[j] = 1; | |
} | |
} | |
-# ifdef FEAT_GUI_W32 | |
- Shell_NotifyIcon(NIM_DELETE, &ni); | |
-# endif | |
} | |
} | |
else if (STRICMP(argv[i], "--remote-expr") == 0) | |
{ | |
if (i == *argc - 1) | |
mainerr_arg_missing((char_u *)argv[i]); | |
-# ifdef WIN32 | |
- /* Win32 always works? */ | |
- if (serverSendToVim(sname, (char_u *)argv[i + 1], | |
- &res, NULL, 1, FALSE) < 0) | |
-# else | |
- if (xterm_dpy == NULL) | |
- mch_errmsg(_("No display: Send expression failed.\n")); | |
- else if (serverSendToVim(xterm_dpy, sname, (char_u *)argv[i + 1], | |
- &res, NULL, 1, 1, FALSE) < 0) | |
-# endif | |
+ if (serverid == NULL) | |
+ { | |
+ EMSG2(_(e_noserver), sname); | |
+ ret = -1; | |
+ } | |
+ else | |
+ { | |
+ ret = cmdsrv_send_expr(serverid, (char_u *)argv[i + 1], &res); | |
+ } | |
+ if (ret != 0) | |
{ | |
if (res != NULL && *res != NUL) | |
{ | |
@@ -3752,13 +3694,7 @@ | |
} | |
else if (STRICMP(argv[i], "--serverlist") == 0) | |
{ | |
-# ifdef WIN32 | |
- /* Win32 always works? */ | |
- res = serverGetVimNames(); | |
-# else | |
- if (xterm_dpy != NULL) | |
- res = serverGetVimNames(xterm_dpy); | |
-# endif | |
+ cmdsrv_server_list(&res); | |
if (called_emsg) | |
mch_errmsg("\n"); | |
} | |
@@ -3784,6 +3720,8 @@ | |
vim_free(res); | |
} | |
+ cmdsrv_uninit(); | |
+ | |
if (didone) | |
{ | |
display_errors(); /* display any collected messages */ | |
@@ -3793,6 +3731,7 @@ | |
/* Return back into main() */ | |
*argc = newArgC; | |
vim_free(sname); | |
+ vim_free(serverid); | |
} | |
/* | |
diff -r 15b934a16641 -r 2082fc32d223 src/misc2.c | |
--- a/src/misc2.c Wed Sep 14 19:04:40 2011 +0200 | |
+++ b/src/misc2.c Thu Sep 15 21:25:11 2011 +0900 | |
@@ -1185,10 +1185,6 @@ | |
ResetRedobuff(); | |
ResetRedobuff(); | |
-#if defined(FEAT_CLIENTSERVER) && defined(FEAT_X11) | |
- vim_free(serverDelayedStartName); | |
-#endif | |
- | |
/* highlight info */ | |
free_highlight(); | |
diff -r 15b934a16641 -r 2082fc32d223 src/os_mswin.c | |
--- a/src/os_mswin.c Wed Sep 14 19:04:40 2011 +0200 | |
+++ b/src/os_mswin.c Thu Sep 15 21:25:11 2011 +0900 | |
@@ -218,6 +218,9 @@ | |
ml_close_all(TRUE); /* remove all memfiles */ | |
+# ifdef FEAT_CLIENTSERVER | |
+ cmdsrv_uninit(); | |
+# endif | |
# ifdef FEAT_OLE | |
UninitOLE(); | |
# endif | |
@@ -2498,649 +2501,6 @@ | |
} | |
#endif | |
-#if defined(FEAT_CLIENTSERVER) || defined(PROTO) | |
-/* | |
- * Client-server code for Vim | |
- * | |
- * Originally written by Paul Moore | |
- */ | |
- | |
-/* In order to handle inter-process messages, we need to have a window. But | |
- * the functions in this module can be called before the main GUI window is | |
- * created (and may also be called in the console version, where there is no | |
- * GUI window at all). | |
- * | |
- * So we create a hidden window, and arrange to destroy it on exit. | |
- */ | |
-HWND message_window = 0; /* window that's handling messsages */ | |
- | |
-#define VIM_CLASSNAME "VIM_MESSAGES" | |
-#define VIM_CLASSNAME_LEN (sizeof(VIM_CLASSNAME) - 1) | |
- | |
-/* Communication is via WM_COPYDATA messages. The message type is send in | |
- * the dwData parameter. Types are defined here. */ | |
-#define COPYDATA_KEYS 0 | |
-#define COPYDATA_REPLY 1 | |
-#define COPYDATA_EXPR 10 | |
-#define COPYDATA_RESULT 11 | |
-#define COPYDATA_ERROR_RESULT 12 | |
-#define COPYDATA_ENCODING 20 | |
- | |
-/* This is a structure containing a server HWND and its name. */ | |
-struct server_id | |
-{ | |
- HWND hwnd; | |
- char_u *name; | |
-}; | |
- | |
-/* Last received 'encoding' that the client uses. */ | |
-static char_u *client_enc = NULL; | |
- | |
-/* | |
- * Tell the other side what encoding we are using. | |
- * Errors are ignored. | |
- */ | |
- static void | |
-serverSendEnc(HWND target) | |
-{ | |
- COPYDATASTRUCT data; | |
- | |
- data.dwData = COPYDATA_ENCODING; | |
-#ifdef FEAT_MBYTE | |
- data.cbData = (DWORD)STRLEN(p_enc) + 1; | |
- data.lpData = p_enc; | |
-#else | |
- data.cbData = (DWORD)STRLEN("latin1") + 1; | |
- data.lpData = "latin1"; | |
-#endif | |
- (void)SendMessage(target, WM_COPYDATA, (WPARAM)message_window, | |
- (LPARAM)(&data)); | |
-} | |
- | |
-/* | |
- * Clean up on exit. This destroys the hidden message window. | |
- */ | |
- static void | |
-#ifdef __BORLANDC__ | |
- _RTLENTRYF | |
-#endif | |
-CleanUpMessaging(void) | |
-{ | |
- if (message_window != 0) | |
- { | |
- DestroyWindow(message_window); | |
- message_window = 0; | |
- } | |
-} | |
- | |
-static int save_reply(HWND server, char_u *reply, int expr); | |
- | |
-/*s | |
- * The window procedure for the hidden message window. | |
- * It handles callback messages and notifications from servers. | |
- * In order to process these messages, it is necessary to run a | |
- * message loop. Code which may run before the main message loop | |
- * is started (in the GUI) is careful to pump messages when it needs | |
- * to. Features which require message delivery during normal use will | |
- * not work in the console version - this basically means those | |
- * features which allow Vim to act as a server, rather than a client. | |
- */ | |
- static LRESULT CALLBACK | |
-Messaging_WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) | |
-{ | |
- if (msg == WM_COPYDATA) | |
- { | |
- /* This is a message from another Vim. The dwData member of the | |
- * COPYDATASTRUCT determines the type of message: | |
- * COPYDATA_ENCODING: | |
- * The encoding that the client uses. Following messages will | |
- * use this encoding, convert if needed. | |
- * COPYDATA_KEYS: | |
- * A key sequence. We are a server, and a client wants these keys | |
- * adding to the input queue. | |
- * COPYDATA_REPLY: | |
- * A reply. We are a client, and a server has sent this message | |
- * in response to a request. (server2client()) | |
- * COPYDATA_EXPR: | |
- * An expression. We are a server, and a client wants us to | |
- * evaluate this expression. | |
- * COPYDATA_RESULT: | |
- * A reply. We are a client, and a server has sent this message | |
- * in response to a COPYDATA_EXPR. | |
- * COPYDATA_ERROR_RESULT: | |
- * A reply. We are a client, and a server has sent this message | |
- * in response to a COPYDATA_EXPR that failed to evaluate. | |
- */ | |
- COPYDATASTRUCT *data = (COPYDATASTRUCT*)lParam; | |
- HWND sender = (HWND)wParam; | |
- COPYDATASTRUCT reply; | |
- char_u *res; | |
- char_u winstr[30]; | |
- int retval; | |
- char_u *str; | |
- char_u *tofree; | |
- | |
- switch (data->dwData) | |
- { | |
- case COPYDATA_ENCODING: | |
-# ifdef FEAT_MBYTE | |
- /* Remember the encoding that the client uses. */ | |
- vim_free(client_enc); | |
- client_enc = enc_canonize((char_u *)data->lpData); | |
-# endif | |
- return 1; | |
- | |
- case COPYDATA_KEYS: | |
- /* Remember who sent this, for <client> */ | |
- clientWindow = sender; | |
- | |
- /* Add the received keys to the input buffer. The loop waiting | |
- * for the user to do something should check the input buffer. */ | |
- str = serverConvert(client_enc, (char_u *)data->lpData, &tofree); | |
- server_to_input_buf(str); | |
- vim_free(tofree); | |
- | |
-# ifdef FEAT_GUI | |
- /* Wake up the main GUI loop. */ | |
- if (s_hwnd != 0) | |
- PostMessage(s_hwnd, WM_NULL, 0, 0); | |
-# endif | |
- return 1; | |
- | |
- case COPYDATA_EXPR: | |
- /* Remember who sent this, for <client> */ | |
- clientWindow = sender; | |
- | |
- str = serverConvert(client_enc, (char_u *)data->lpData, &tofree); | |
- res = eval_client_expr_to_string(str); | |
- vim_free(tofree); | |
- | |
- if (res == NULL) | |
- { | |
- res = vim_strsave(_(e_invexprmsg)); | |
- reply.dwData = COPYDATA_ERROR_RESULT; | |
- } | |
- else | |
- reply.dwData = COPYDATA_RESULT; | |
- reply.lpData = res; | |
- reply.cbData = (DWORD)STRLEN(res) + 1; | |
- | |
- serverSendEnc(sender); | |
- retval = (int)SendMessage(sender, WM_COPYDATA, (WPARAM)message_window, | |
- (LPARAM)(&reply)); | |
- vim_free(res); | |
- return retval; | |
- | |
- case COPYDATA_REPLY: | |
- case COPYDATA_RESULT: | |
- case COPYDATA_ERROR_RESULT: | |
- if (data->lpData != NULL) | |
- { | |
- str = serverConvert(client_enc, (char_u *)data->lpData, | |
- &tofree); | |
- if (tofree == NULL) | |
- str = vim_strsave(str); | |
- if (save_reply(sender, str, | |
- (data->dwData == COPYDATA_REPLY ? 0 : | |
- (data->dwData == COPYDATA_RESULT ? 1 : | |
- 2))) == FAIL) | |
- vim_free(str); | |
-#ifdef FEAT_AUTOCMD | |
- else if (data->dwData == COPYDATA_REPLY) | |
- { | |
- sprintf((char *)winstr, PRINTF_HEX_LONG_U, (long_u)sender); | |
- apply_autocmds(EVENT_REMOTEREPLY, winstr, str, | |
- TRUE, curbuf); | |
- } | |
-#endif | |
- } | |
- return 1; | |
- } | |
- | |
- return 0; | |
- } | |
- | |
- else if (msg == WM_ACTIVATE && wParam == WA_ACTIVE) | |
- { | |
- /* When the message window is activated (brought to the foreground), | |
- * this actually applies to the text window. */ | |
-#ifndef FEAT_GUI | |
- GetConsoleHwnd(); /* get value of s_hwnd */ | |
-#endif | |
- if (s_hwnd != 0) | |
- { | |
- SetForegroundWindow(s_hwnd); | |
- return 0; | |
- } | |
- } | |
- | |
- return DefWindowProc(hwnd, msg, wParam, lParam); | |
-} | |
- | |
-/* | |
- * Initialise the message handling process. This involves creating a window | |
- * to handle messages - the window will not be visible. | |
- */ | |
- void | |
-serverInitMessaging(void) | |
-{ | |
- WNDCLASS wndclass; | |
- HINSTANCE s_hinst; | |
- | |
- /* Clean up on exit */ | |
- atexit(CleanUpMessaging); | |
- | |
- /* Register a window class - we only really care | |
- * about the window procedure | |
- */ | |
- s_hinst = (HINSTANCE)GetModuleHandle(0); | |
- wndclass.style = 0; | |
- wndclass.lpfnWndProc = Messaging_WndProc; | |
- wndclass.cbClsExtra = 0; | |
- wndclass.cbWndExtra = 0; | |
- wndclass.hInstance = s_hinst; | |
- wndclass.hIcon = NULL; | |
- wndclass.hCursor = NULL; | |
- wndclass.hbrBackground = NULL; | |
- wndclass.lpszMenuName = NULL; | |
- wndclass.lpszClassName = VIM_CLASSNAME; | |
- RegisterClass(&wndclass); | |
- | |
- /* Create the message window. It will be hidden, so the details don't | |
- * matter. Don't use WS_OVERLAPPEDWINDOW, it will make a shortcut remove | |
- * focus from gvim. */ | |
- message_window = CreateWindow(VIM_CLASSNAME, "", | |
- WS_POPUPWINDOW | WS_CAPTION, | |
- CW_USEDEFAULT, CW_USEDEFAULT, | |
- 100, 100, NULL, NULL, | |
- s_hinst, NULL); | |
-} | |
- | |
-/* Used by serverSendToVim() to find an alternate server name. */ | |
-static char_u *altname_buf_ptr = NULL; | |
- | |
-/* | |
- * Get the title of the window "hwnd", which is the Vim server name, in | |
- * "name[namelen]" and return the length. | |
- * Returns zero if window "hwnd" is not a Vim server. | |
- */ | |
- static int | |
-getVimServerName(HWND hwnd, char *name, int namelen) | |
-{ | |
- int len; | |
- char buffer[VIM_CLASSNAME_LEN + 1]; | |
- | |
- /* Ignore windows which aren't Vim message windows */ | |
- len = GetClassName(hwnd, buffer, sizeof(buffer)); | |
- if (len != VIM_CLASSNAME_LEN || STRCMP(buffer, VIM_CLASSNAME) != 0) | |
- return 0; | |
- | |
- /* Get the title of the window */ | |
- return GetWindowText(hwnd, name, namelen); | |
-} | |
- | |
- static BOOL CALLBACK | |
-enumWindowsGetServer(HWND hwnd, LPARAM lparam) | |
-{ | |
- struct server_id *id = (struct server_id *)lparam; | |
- char server[MAX_PATH]; | |
- | |
- /* Get the title of the window */ | |
- if (getVimServerName(hwnd, server, sizeof(server)) == 0) | |
- return TRUE; | |
- | |
- /* If this is the server we're looking for, return its HWND */ | |
- if (STRICMP(server, id->name) == 0) | |
- { | |
- id->hwnd = hwnd; | |
- return FALSE; | |
- } | |
- | |
- /* If we are looking for an alternate server, remember this name. */ | |
- if (altname_buf_ptr != NULL | |
- && STRNICMP(server, id->name, STRLEN(id->name)) == 0 | |
- && vim_isdigit(server[STRLEN(id->name)])) | |
- { | |
- STRCPY(altname_buf_ptr, server); | |
- altname_buf_ptr = NULL; /* don't use another name */ | |
- } | |
- | |
- /* Otherwise, keep looking */ | |
- return TRUE; | |
-} | |
- | |
- static BOOL CALLBACK | |
-enumWindowsGetNames(HWND hwnd, LPARAM lparam) | |
-{ | |
- garray_T *ga = (garray_T *)lparam; | |
- char server[MAX_PATH]; | |
- | |
- /* Get the title of the window */ | |
- if (getVimServerName(hwnd, server, sizeof(server)) == 0) | |
- return TRUE; | |
- | |
- /* Add the name to the list */ | |
- ga_concat(ga, server); | |
- ga_concat(ga, "\n"); | |
- return TRUE; | |
-} | |
- | |
- static HWND | |
-findServer(char_u *name) | |
-{ | |
- struct server_id id; | |
- | |
- id.name = name; | |
- id.hwnd = 0; | |
- | |
- EnumWindows(enumWindowsGetServer, (LPARAM)(&id)); | |
- | |
- return id.hwnd; | |
-} | |
- | |
- void | |
-serverSetName(char_u *name) | |
-{ | |
- char_u *ok_name; | |
- HWND hwnd = 0; | |
- int i = 0; | |
- char_u *p; | |
- | |
- /* Leave enough space for a 9-digit suffix to ensure uniqueness! */ | |
- ok_name = alloc((unsigned)STRLEN(name) + 10); | |
- | |
- STRCPY(ok_name, name); | |
- p = ok_name + STRLEN(name); | |
- | |
- for (;;) | |
- { | |
- /* This is inefficient - we're doing an EnumWindows loop for each | |
- * possible name. It would be better to grab all names in one go, | |
- * and scan the list each time... | |
- */ | |
- hwnd = findServer(ok_name); | |
- if (hwnd == 0) | |
- break; | |
- | |
- ++i; | |
- if (i >= 1000) | |
- break; | |
- | |
- sprintf((char *)p, "%d", i); | |
- } | |
- | |
- if (hwnd != 0) | |
- vim_free(ok_name); | |
- else | |
- { | |
- /* Remember the name */ | |
- serverName = ok_name; | |
-#ifdef FEAT_TITLE | |
- need_maketitle = TRUE; /* update Vim window title later */ | |
-#endif | |
- | |
- /* Update the message window title */ | |
- SetWindowText(message_window, ok_name); | |
- | |
-#ifdef FEAT_EVAL | |
- /* Set the servername variable */ | |
- set_vim_var_string(VV_SEND_SERVER, serverName, -1); | |
-#endif | |
- } | |
-} | |
- | |
- char_u * | |
-serverGetVimNames(void) | |
-{ | |
- garray_T ga; | |
- | |
- ga_init2(&ga, 1, 100); | |
- | |
- EnumWindows(enumWindowsGetNames, (LPARAM)(&ga)); | |
- ga_append(&ga, NUL); | |
- | |
- return ga.ga_data; | |
-} | |
- | |
- int | |
-serverSendReply(name, reply) | |
- char_u *name; /* Where to send. */ | |
- char_u *reply; /* What to send. */ | |
-{ | |
- HWND target; | |
- COPYDATASTRUCT data; | |
- long_u n = 0; | |
- | |
- /* The "name" argument is a magic cookie obtained from expand("<client>"). | |
- * It should be of the form 0xXXXXX - i.e. a C hex literal, which is the | |
- * value of the client's message window HWND. | |
- */ | |
- sscanf((char *)name, SCANF_HEX_LONG_U, &n); | |
- if (n == 0) | |
- return -1; | |
- | |
- target = (HWND)n; | |
- if (!IsWindow(target)) | |
- return -1; | |
- | |
- data.dwData = COPYDATA_REPLY; | |
- data.cbData = (DWORD)STRLEN(reply) + 1; | |
- data.lpData = reply; | |
- | |
- serverSendEnc(target); | |
- if (SendMessage(target, WM_COPYDATA, (WPARAM)message_window, | |
- (LPARAM)(&data))) | |
- return 0; | |
- | |
- return -1; | |
-} | |
- | |
- int | |
-serverSendToVim(name, cmd, result, ptarget, asExpr, silent) | |
- char_u *name; /* Where to send. */ | |
- char_u *cmd; /* What to send. */ | |
- char_u **result; /* Result of eval'ed expression */ | |
- void *ptarget; /* HWND of server */ | |
- int asExpr; /* Expression or keys? */ | |
- int silent; /* don't complain about no server */ | |
-{ | |
- HWND target; | |
- COPYDATASTRUCT data; | |
- char_u *retval = NULL; | |
- int retcode = 0; | |
- char_u altname_buf[MAX_PATH]; | |
- | |
- /* If the server name does not end in a digit then we look for an | |
- * alternate name. e.g. when "name" is GVIM the we may find GVIM2. */ | |
- if (STRLEN(name) > 1 && !vim_isdigit(name[STRLEN(name) - 1])) | |
- altname_buf_ptr = altname_buf; | |
- altname_buf[0] = NUL; | |
- target = findServer(name); | |
- altname_buf_ptr = NULL; | |
- if (target == 0 && altname_buf[0] != NUL) | |
- /* Use another server name we found. */ | |
- target = findServer(altname_buf); | |
- | |
- if (target == 0) | |
- { | |
- if (!silent) | |
- EMSG2(_(e_noserver), name); | |
- return -1; | |
- } | |
- | |
- if (ptarget) | |
- *(HWND *)ptarget = target; | |
- | |
- data.dwData = asExpr ? COPYDATA_EXPR : COPYDATA_KEYS; | |
- data.cbData = (DWORD)STRLEN(cmd) + 1; | |
- data.lpData = cmd; | |
- | |
- serverSendEnc(target); | |
- if (SendMessage(target, WM_COPYDATA, (WPARAM)message_window, | |
- (LPARAM)(&data)) == 0) | |
- return -1; | |
- | |
- if (asExpr) | |
- retval = serverGetReply(target, &retcode, TRUE, TRUE); | |
- | |
- if (result == NULL) | |
- vim_free(retval); | |
- else | |
- *result = retval; /* Caller assumes responsibility for freeing */ | |
- | |
- return retcode; | |
-} | |
- | |
-/* | |
- * Bring the server to the foreground. | |
- */ | |
- void | |
-serverForeground(name) | |
- char_u *name; | |
-{ | |
- HWND target = findServer(name); | |
- | |
- if (target != 0) | |
- SetForegroundWindow(target); | |
-} | |
- | |
-/* Replies from server need to be stored until the client picks them up via | |
- * remote_read(). So we maintain a list of server-id/reply pairs. | |
- * Note that there could be multiple replies from one server pending if the | |
- * client is slow picking them up. | |
- * We just store the replies in a simple list. When we remove an entry, we | |
- * move list entries down to fill the gap. | |
- * The server ID is simply the HWND. | |
- */ | |
-typedef struct | |
-{ | |
- HWND server; /* server window */ | |
- char_u *reply; /* reply string */ | |
- int expr_result; /* 0 for REPLY, 1 for RESULT 2 for error */ | |
-} reply_T; | |
- | |
-static garray_T reply_list = {0, 0, sizeof(reply_T), 5, 0}; | |
- | |
-#define REPLY_ITEM(i) ((reply_T *)(reply_list.ga_data) + (i)) | |
-#define REPLY_COUNT (reply_list.ga_len) | |
- | |
-/* Flag which is used to wait for a reply */ | |
-static int reply_received = 0; | |
- | |
-/* | |
- * Store a reply. "reply" must be allocated memory (or NULL). | |
- */ | |
- static int | |
-save_reply(HWND server, char_u *reply, int expr) | |
-{ | |
- reply_T *rep; | |
- | |
- if (ga_grow(&reply_list, 1) == FAIL) | |
- return FAIL; | |
- | |
- rep = REPLY_ITEM(REPLY_COUNT); | |
- rep->server = server; | |
- rep->reply = reply; | |
- rep->expr_result = expr; | |
- if (rep->reply == NULL) | |
- return FAIL; | |
- | |
- ++REPLY_COUNT; | |
- reply_received = 1; | |
- return OK; | |
-} | |
- | |
-/* | |
- * Get a reply from server "server". | |
- * When "expr_res" is non NULL, get the result of an expression, otherwise a | |
- * server2client() message. | |
- * When non NULL, point to return code. 0 => OK, -1 => ERROR | |
- * If "remove" is TRUE, consume the message, the caller must free it then. | |
- * if "wait" is TRUE block until a message arrives (or the server exits). | |
- */ | |
- char_u * | |
-serverGetReply(HWND server, int *expr_res, int remove, int wait) | |
-{ | |
- int i; | |
- char_u *reply; | |
- reply_T *rep; | |
- | |
- /* When waiting, loop until the message waiting for is received. */ | |
- for (;;) | |
- { | |
- /* Reset this here, in case a message arrives while we are going | |
- * through the already received messages. */ | |
- reply_received = 0; | |
- | |
- for (i = 0; i < REPLY_COUNT; ++i) | |
- { | |
- rep = REPLY_ITEM(i); | |
- if (rep->server == server | |
- && ((rep->expr_result != 0) == (expr_res != NULL))) | |
- { | |
- /* Save the values we've found for later */ | |
- reply = rep->reply; | |
- if (expr_res != NULL) | |
- *expr_res = rep->expr_result == 1 ? 0 : -1; | |
- | |
- if (remove) | |
- { | |
- /* Move the rest of the list down to fill the gap */ | |
- mch_memmove(rep, rep + 1, | |
- (REPLY_COUNT - i - 1) * sizeof(reply_T)); | |
- --REPLY_COUNT; | |
- } | |
- | |
- /* Return the reply to the caller, who takes on responsibility | |
- * for freeing it if "remove" is TRUE. */ | |
- return reply; | |
- } | |
- } | |
- | |
- /* If we got here, we didn't find a reply. Return immediately if the | |
- * "wait" parameter isn't set. */ | |
- if (!wait) | |
- break; | |
- | |
- /* We need to wait for a reply. Enter a message loop until the | |
- * "reply_received" flag gets set. */ | |
- | |
- /* Loop until we receive a reply */ | |
- while (reply_received == 0) | |
- { | |
- /* Wait for a SendMessage() call to us. This could be the reply | |
- * we are waiting for. Use a timeout of a second, to catch the | |
- * situation that the server died unexpectedly. */ | |
- MsgWaitForMultipleObjects(0, NULL, TRUE, 1000, QS_ALLINPUT); | |
- | |
- /* If the server has died, give up */ | |
- if (!IsWindow(server)) | |
- return NULL; | |
- | |
- serverProcessPendingMessages(); | |
- } | |
- } | |
- | |
- return NULL; | |
-} | |
- | |
-/* | |
- * Process any messages in the Windows message queue. | |
- */ | |
- void | |
-serverProcessPendingMessages(void) | |
-{ | |
- MSG msg; | |
- | |
- while (pPeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) | |
- { | |
- TranslateMessage(&msg); | |
- pDispatchMessage(&msg); | |
- } | |
-} | |
- | |
-#endif /* FEAT_CLIENTSERVER */ | |
- | |
#if defined(FEAT_GUI) || (defined(FEAT_PRINTER) && !defined(FEAT_POSTSCRIPT)) \ | |
|| defined(PROTO) | |
diff -r 15b934a16641 -r 2082fc32d223 src/os_unix.c | |
--- a/src/os_unix.c Wed Sep 14 19:04:40 2011 +0200 | |
+++ b/src/os_unix.c Thu Sep 15 21:25:11 2011 +0900 | |
@@ -378,6 +378,10 @@ | |
netbeans_parse_messages(); | |
#endif | |
+#if defined(FEAT_CLIENTSERVER) | |
+ cmdsrv_handle_requests(); | |
+#endif | |
+ | |
/* Check if window changed size while we were busy, perhaps the ":set | |
* columns=99" command was used. */ | |
while (do_resize) | |
@@ -394,6 +398,9 @@ | |
/* Process the queued netbeans messages. */ | |
netbeans_parse_messages(); | |
#endif | |
+#ifdef FEAT_CLIENTSERVER | |
+ cmdsrv_handle_requests(); | |
+#endif | |
} | |
} | |
else /* wtime == -1 */ | |
@@ -428,6 +435,9 @@ | |
/* Process the queued netbeans messages. */ | |
netbeans_parse_messages(); | |
#endif | |
+#ifdef FEAT_CLIENTSERVER | |
+ cmdsrv_handle_requests(); | |
+#endif | |
#ifndef VMS /* VMS: must try reading, WaitForChar() does nothing. */ | |
/* | |
* We want to be interrupted by the winch signal | |
@@ -1540,10 +1550,6 @@ | |
{ | |
regmatch_T regmatch; | |
-#if defined(FEAT_CLIENTSERVER) | |
- if (x_force_connect) | |
- return TRUE; | |
-#endif | |
if (x_no_connect) | |
return FALSE; | |
@@ -3131,6 +3137,10 @@ | |
netbeans_send_disconnect(); | |
#endif | |
+#ifdef FEAT_CLIENTSERVER | |
+ cmdsrv_uninit(); | |
+#endif | |
+ | |
#ifdef EXITFREE | |
free_all_mem(); | |
#endif | |
@@ -4864,6 +4874,9 @@ | |
#ifdef FEAT_NETBEANS_INTG | |
int nb_fd = netbeans_filedesc(); | |
#endif | |
+#ifdef FEAT_CLIENTSERVER | |
+ int cmdsrv_fd = cmdsrv_listenfd; | |
+#endif | |
#if defined(FEAT_XCLIPBOARD) || defined(USE_XSMP) || defined(FEAT_MZSCHEME) | |
static int busy = FALSE; | |
@@ -4913,7 +4926,7 @@ | |
# endif | |
#endif | |
#ifndef HAVE_SELECT | |
- struct pollfd fds[6]; | |
+ struct pollfd fds[7]; | |
int nfd; | |
# ifdef FEAT_XCLIPBOARD | |
int xterm_idx = -1; | |
@@ -4927,6 +4940,9 @@ | |
# ifdef FEAT_NETBEANS_INTG | |
int nb_idx = -1; | |
# endif | |
+# ifdef FEAT_CLIENTSERVER | |
+ int cmdsrv_idx = -1; | |
+# endif | |
int towait = (int)msec; | |
# ifdef FEAT_MZSCHEME | |
@@ -4986,6 +5002,15 @@ | |
nfd++; | |
} | |
#endif | |
+#ifdef FEAT_CLIENTSERVER | |
+ if (cmdsrv_fd != -1) | |
+ { | |
+ cmdsrv_idx = nfd; | |
+ fds[nfd].fd = cmdsrv_fd; | |
+ fds[nfd].events = POLLIN; | |
+ nfd++; | |
+ } | |
+#endif | |
ret = poll(fds, nfd, towait); | |
# ifdef FEAT_MZSCHEME | |
@@ -5046,6 +5071,13 @@ | |
--ret; | |
} | |
#endif | |
+# ifdef FEAT_CLIENTSERVER | |
+ if (ret > 0 && cmdsrv_idx != -1 && (fds[cmdsrv_idx].revents & POLLIN)) | |
+ { | |
+ cmdsrv_handle_requests(); | |
+ --ret; | |
+ } | |
+# endif | |
#else /* HAVE_SELECT */ | |
@@ -5136,6 +5168,14 @@ | |
maxfd = nb_fd; | |
} | |
#endif | |
+# ifdef FEAT_CLIENTSERVER | |
+ if (cmdsrv_fd != -1) | |
+ { | |
+ FD_SET(cmdsrv_fd, &rfds); | |
+ if (maxfd < cmdsrv_fd) | |
+ maxfd = cmdsrv_fd; | |
+ } | |
+# endif | |
# ifdef OLD_VMS | |
/* Old VMS as v6.2 and older have broken select(). It waits more than | |
@@ -5228,6 +5268,13 @@ | |
--ret; | |
} | |
#endif | |
+# ifdef FEAT_CLIENTSERVER | |
+ if (ret > 0 && cmdsrv_fd != -1 && FD_ISSET(cmdsrv_fd, &rfds)) | |
+ { | |
+ cmdsrv_handle_requests(); | |
+ --ret; | |
+ } | |
+# endif | |
#endif /* HAVE_SELECT */ | |
@@ -6725,15 +6772,6 @@ | |
while (XtAppPending(app_context) && !vim_is_input_buf_full()) | |
{ | |
XtAppNextEvent(app_context, &event); | |
-#ifdef FEAT_CLIENTSERVER | |
- { | |
- XPropertyEvent *e = (XPropertyEvent *)&event; | |
- | |
- if (e->type == PropertyNotify && e->window == commWindow | |
- && e->atom == commProperty && e->state == PropertyNewValue) | |
- serverEventProc(xterm_dpy, &event); | |
- } | |
-#endif | |
XtDispatchEvent(&event); | |
} | |
} | |
diff -r 15b934a16641 -r 2082fc32d223 src/os_win32.c | |
--- a/src/os_win32.c Wed Sep 14 19:04:40 2011 +0200 | |
+++ b/src/os_win32.c Thu Sep 15 21:25:11 2011 +0900 | |
@@ -1205,7 +1205,7 @@ | |
mzvim_check_threads(); | |
#endif | |
#ifdef FEAT_CLIENTSERVER | |
- serverProcessPendingMessages(); | |
+ cmdsrv_handle_requests(); | |
#endif | |
if (0 | |
#ifdef FEAT_MOUSE | |
@@ -1227,21 +1227,39 @@ | |
if (msec != 0) | |
{ | |
DWORD dwWaitTime = dwEndTime - dwNow; | |
+ DWORD dwWait; | |
+ int nitems = 0; | |
+#ifdef FEAT_CLIENTSERVER | |
+ HANDLE hObjects[1 + CMDSRV_INSTANCES]; | |
+#else | |
+ HANDLE hObjects[1]; | |
+#endif | |
+ | |
+ hObjects[nitems++] = g_hConIn; | |
+ | |
+#ifdef FEAT_CLIENTSERVER | |
+ if (cmdsrv_events != NULL) | |
+ { | |
+ mch_memmove(hObjects + nitems, cmdsrv_events, | |
+ sizeof(HANDLE) * CMDSRV_INSTANCES); | |
+ nitems += CMDSRV_INSTANCES; | |
+ } | |
+#endif | |
#ifdef FEAT_MZSCHEME | |
if (mzthreads_allowed() && p_mzq > 0 | |
&& (msec < 0 || (long)dwWaitTime > p_mzq)) | |
dwWaitTime = p_mzq; /* don't wait longer than 'mzquantum' */ | |
#endif | |
-#ifdef FEAT_CLIENTSERVER | |
- /* Wait for either an event on the console input or a message in | |
- * the client-server window. */ | |
- if (MsgWaitForMultipleObjects(1, &g_hConIn, FALSE, | |
- dwWaitTime, QS_SENDMESSAGE) != WAIT_OBJECT_0) | |
-#else | |
- if (WaitForSingleObject(g_hConIn, dwWaitTime) != WAIT_OBJECT_0) | |
-#endif | |
- continue; | |
+ | |
+ dwWait = WaitForMultipleObjects(nitems, hObjects, FALSE, dwWaitTime); | |
+ if (dwWait == WAIT_FAILED) | |
+ break; | |
+ else if (dwWait == WAIT_TIMEOUT) | |
+ break; | |
+ else if (dwWait != WAIT_OBJECT_0) | |
+ /* Signaled object is not g_hConIn. */ | |
+ continue; | |
} | |
cRecords = 0; | |
@@ -2282,6 +2300,10 @@ | |
ml_close_all(TRUE); /* remove all memfiles */ | |
+#ifdef FEAT_CLIENTSERVER | |
+ cmdsrv_uninit(); | |
+#endif | |
+ | |
if (g_fWindInitCalled) | |
{ | |
#ifdef FEAT_TITLE | |
diff -r 15b934a16641 -r 2082fc32d223 src/proto.h | |
--- a/src/proto.h Wed Sep 14 19:04:40 2011 +0200 | |
+++ b/src/proto.h Thu Sep 15 21:25:11 2011 +0900 | |
@@ -253,8 +253,9 @@ | |
# ifdef FEAT_OLE | |
# include "if_ole.pro" | |
# endif | |
-# if defined(FEAT_CLIENTSERVER) && defined(FEAT_X11) | |
-# include "if_xcmdsrv.pro" | |
+ | |
+# ifdef FEAT_CLIENTSERVER | |
+# include "if_cmdsrv.pro" | |
# endif | |
/* | |
diff -r 15b934a16641 -r 2082fc32d223 src/proto/if_cmdsrv.pro | |
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 | |
+++ b/src/proto/if_cmdsrv.pro Thu Sep 15 21:25:11 2011 +0900 | |
@@ -0,0 +1,20 @@ | |
+/* if_cmdsrv.c */ | |
+int cmdsrv_init __ARGS((void)); | |
+int cmdsrv_uninit __ARGS((void)); | |
+int cmdsrv_gui_register __ARGS((void)); | |
+int cmdsrv_gui_unregister __ARGS((void)); | |
+int cmdsrv_register_name __ARGS((char_u *name)); | |
+int cmdsrv_server_list __ARGS((char_u **result)); | |
+int cmdsrv_wait_request __ARGS((int timeoutmsec)); | |
+int cmdsrv_handle_requests __ARGS((void)); | |
+int cmdsrv_send_keys __ARGS((char_u *serverid, char_u *keys)); | |
+int cmdsrv_send_expr __ARGS((char_u *serverid, char_u *expr, char_u **result)); | |
+int cmdsrv_send_notification __ARGS((char_u *serverid, char_u *reply)); | |
+int cmdsrv_foreground __ARGS((char_u *serverid)); | |
+int cmdsrv_peek_reply __ARGS((char_u *serverid, char_u **pstr)); | |
+int cmdsrv_read_reply __ARGS((char_u *serverid, char_u **pstr)); | |
+int cmdsrv_wait_reply __ARGS((char_u *serverid, char_u **pstr)); | |
+char_u *cmdsrv_find_server __ARGS((char_u *name, int loose)); | |
+char_u *cmdsrv_urlencode(char_u *str); | |
+char_u *cmdsrv_urldecode(char_u *str); | |
+/* vim: set ft=c : */ | |
diff -r 15b934a16641 -r 2082fc32d223 src/proto/if_xcmdsrv.pro | |
--- a/src/proto/if_xcmdsrv.pro Wed Sep 14 19:04:40 2011 +0200 | |
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000 | |
@@ -1,11 +0,0 @@ | |
-/* if_xcmdsrv.c */ | |
-int serverRegisterName __ARGS((Display *dpy, char_u *name)); | |
-void serverChangeRegisteredWindow __ARGS((Display *dpy, Window newwin)); | |
-int serverSendToVim __ARGS((Display *dpy, char_u *name, char_u *cmd, char_u **result, Window *server, int asExpr, int localLoop, int silent)); | |
-char_u *serverGetVimNames __ARGS((Display *dpy)); | |
-Window serverStrToWin __ARGS((char_u *str)); | |
-int serverSendReply __ARGS((char_u *name, char_u *str)); | |
-int serverReadReply __ARGS((Display *dpy, Window win, char_u **str, int localLoop)); | |
-int serverPeekReply __ARGS((Display *dpy, Window win, char_u **str)); | |
-void serverEventProc __ARGS((Display *dpy, XEvent *eventPtr)); | |
-/* vim: set ft=c : */ | |
diff -r 15b934a16641 -r 2082fc32d223 src/proto/os_mswin.pro | |
--- a/src/proto/os_mswin.pro Wed Sep 14 19:04:40 2011 +0200 | |
+++ b/src/proto/os_mswin.pro Thu Sep 15 21:25:11 2011 +0900 | |
@@ -50,14 +50,6 @@ | |
void mch_print_set_fg __ARGS((long_u fgcol)); | |
char_u *mch_resolve_shortcut __ARGS((char_u *fname)); | |
void win32_set_foreground __ARGS((void)); | |
-void serverInitMessaging __ARGS((void)); | |
-void serverSetName __ARGS((char_u *name)); | |
-char_u *serverGetVimNames __ARGS((void)); | |
-int serverSendReply __ARGS((char_u *name, char_u *reply)); | |
-int serverSendToVim __ARGS((char_u *name, char_u *cmd, char_u **result, void *ptarget, int asExpr, int silent)); | |
-void serverForeground __ARGS((char_u *name)); | |
-char_u *serverGetReply __ARGS((HWND server, int *expr_res, int remove, int wait)); | |
-void serverProcessPendingMessages __ARGS((void)); | |
char *charset_id2name __ARGS((int id)); | |
int get_logfont __ARGS((LOGFONT *lf, char_u *name, HDC printer_dc, int verbose)); | |
/* vim: set ft=c : */ | |
diff -r 15b934a16641 -r 2082fc32d223 src/testdir/cmdsrv-test.vim | |
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 | |
+++ b/src/testdir/cmdsrv-test.vim Thu Sep 15 21:25:11 2011 +0900 | |
@@ -0,0 +1,390 @@ | |
+#!vim -u | |
+" Test for cmdsrv-nox. | |
+" Run this script in src directory as ./vim -u testdir/cmdsrv-test.vim. | |
+" This script works under GNOME desktop or Windows desktop. | |
+ | |
+if has('vim_starting') | |
+ set nocompatible | |
+ set loadplugins | |
+ call feedkeys(":source " . expand('<sfile>') . "\<CR>") | |
+ finish | |
+endif | |
+ | |
+set nocompatible | |
+set nomore | |
+ | |
+function! Sleep(n) | |
+ execute 'sleep' a:n | |
+endfunction | |
+ | |
+function! WaitResponse(serverid) | |
+ if has('win32') | |
+ " WORKAROUND: update event loop to receive response | |
+ sleep 1 | |
+ call remote_expr(a:serverid, 'remote_expr("' . v:servername . '", "Sleep(1)")') | |
+ else | |
+ sleep 1 | |
+ endif | |
+endfunction | |
+ | |
+function! s:serverlist() | |
+ return split(serverlist(), '\n') | |
+endfunction | |
+ | |
+function! s:cui_start_server(name) | |
+ let nservers = len(s:serverlist()) | |
+ if has('win32') | |
+ let cmd = printf('start .\vim.exe -u NORC --servername %s', shellescape(a:name, 1)) | |
+ silent! execute '!' . cmd | |
+ else | |
+ let cmd = printf("gnome-terminal -e \"./vim -u NORC --servername %s\"", escape(shellescape(a:name), '\"')) | |
+ call system(cmd) | |
+ endif | |
+ if v:shell_error | |
+ throw 'cui_start_server: failed to start server' | |
+ endif | |
+ " wait server ready | |
+ for i in range(100) | |
+ if nservers != len(s:serverlist()) | |
+ return | |
+ endif | |
+ sleep 100m | |
+ endfor | |
+ throw 'cui_start_server: failed to start server' | |
+endfunction | |
+ | |
+function! s:gui_start_server(name) | |
+ let nservers = len(s:serverlist()) | |
+ if has('win32') | |
+ let cmd = printf('start .\gvim.exe -u NORC --servername %s', shellescape(a:name, 1)) | |
+ silent! execute '!' . cmd | |
+ else | |
+ let cmd = printf('./vim -g -u NORC --servername %s', shellescape(a:name)) | |
+ call system(cmd) | |
+ endif | |
+ if v:shell_error | |
+ throw 'gui_start_server: failed to start server' | |
+ endif | |
+ " wait server ready | |
+ for i in range(100) | |
+ if nservers != len(s:serverlist()) | |
+ return | |
+ endif | |
+ sleep 100m | |
+ endfor | |
+ throw 'gui_start_server: failed to start server' | |
+endfunction | |
+ | |
+let s:servermode = 'cui' | |
+ | |
+function! s:start_server(name) | |
+ if s:servermode == 'cui' | |
+ call s:cui_start_server(a:name) | |
+ else | |
+ call s:gui_start_server(a:name) | |
+ endif | |
+endfunction | |
+ | |
+function! s:close_server(name) | |
+ let nservers = len(s:serverlist()) | |
+ call remote_send(a:name, '<C-\><C-N>:qall!<CR>') | |
+ " wait server closed | |
+ for i in range(100) | |
+ if nservers != len(s:serverlist()) | |
+ return | |
+ endif | |
+ sleep 100m | |
+ endfor | |
+ throw 'close_server: failed to close server' | |
+endfunction | |
+ | |
+function! s:close_all_servers() | |
+ for name in s:serverlist() | |
+ if name =~? '^\(ONE\|TWO\|THREE\)\d*$' | |
+ call s:close_server(name) | |
+ endif | |
+ endfor | |
+endfunction | |
+ | |
+function! s:test_loosename1() | |
+ echomsg 'test_loosename1: Test for loosename' | |
+ call s:start_server('one') | |
+ call s:start_server('one') | |
+ call s:start_server('one') | |
+ let servers = s:serverlist() | |
+ if index(servers, 'one', 0, 1) == -1 || index(servers, 'one1', 0, 1) == -1 | |
+ \ || index(servers, 'one2', 0, 1) == -1 | |
+ throw 'test_loosename1: failed' | |
+ endif | |
+ call s:close_all_servers() | |
+endfunction | |
+ | |
+function! s:test_loosename2() | |
+ echomsg 'test_loosename2: Test for loosename.' | |
+ " one1 already used, second server will be named as one11. | |
+ call s:start_server('one1') | |
+ call s:start_server('one1') | |
+ let servers = s:serverlist() | |
+ if index(servers, 'one1', 0, 1) == -1 || index(servers, 'one11', 0, 1) == -1 | |
+ throw 'test_loosename2: failed' | |
+ endif | |
+ call s:close_all_servers() | |
+endfunction | |
+ | |
+function! s:test_loosename3() | |
+ echomsg 'test_loosename3: Test for many instances.' | |
+ let n = 20 | |
+ for i in range(n) | |
+ let name = printf('one%d', i) | |
+ call s:start_server(name) | |
+ endfor | |
+ for i in range(n) | |
+ let name = printf('one%d', i) | |
+ if remote_expr(name, 'v:servername') !=? name | |
+ throw 'test_loosename3: failed' | |
+ endif | |
+ endfor | |
+ call s:close_all_servers() | |
+endfunction | |
+ | |
+function! s:test_loosename4() | |
+ echomsg 'test_loosename4: Test for special character which is not safe in file name' | |
+ let servername = '/\*"[]:;|=?' | |
+ call s:start_server(servername) | |
+ if index(s:serverlist(), servername) == -1 | |
+ throw 'test_loosename4: failed' | |
+ endif | |
+ call s:close_server(servername) | |
+endfunction | |
+ | |
+function! s:test_remote_send1() | |
+ echomsg 'test_remote_send1: Test for remote_send().' | |
+ call s:start_server('one') | |
+ call remote_send('one', 'ihello<Esc>') | |
+ call WaitResponse('one') | |
+ let result = remote_expr('one', 'getline(1)') | |
+ if result !=# 'hello' | |
+ throw 'test_remote_send1: failed' | |
+ endif | |
+ call s:close_all_servers() | |
+endfunction | |
+ | |
+function! s:test_remote_expr1() | |
+ echomsg 'test_remote_expr1: Test for remote_expr().' | |
+ call s:start_server('one') | |
+ let result = remote_expr('one', 'v:servername') | |
+ if result !=# 'ONE' | |
+ throw 'test_remote_expr1: failed' | |
+ endif | |
+ call s:close_all_servers() | |
+endfunction | |
+ | |
+function! s:test_remote_expr2() | |
+ echomsg 'test_remote_expr2: Test for remote_expr().' | |
+ call s:start_server('one') | |
+ for i in range(100) | |
+ let result = remote_expr('one', i) | |
+ if result != i | |
+ throw 'test_remote_expr2: failed' | |
+ endif | |
+ endfor | |
+ call s:close_all_servers() | |
+endfunction | |
+ | |
+let s:test_remote_expr3_count = 0 | |
+ | |
+function! _test_remote_expr3_recursive(serverid, n) | |
+ if a:n == 0 | |
+ return v:servername | |
+ endif | |
+ let s:test_remote_expr3_count += 1 | |
+ return v:servername . remote_expr(a:serverid, 'v:servername . remote_expr("' . v:servername . '", "_test_remote_expr3_recursive(\"" . v:servername . "\", ' . (a:n - 1) . ')")') | |
+endfunction | |
+ | |
+function! s:test_remote_expr3() | |
+ echomsg 'test_remote_expr3: Test for recursive call.' | |
+ call s:start_server('one') | |
+ let s:test_remote_expr3_count = 0 | |
+ let result = _test_remote_expr3_recursive('one', 3) | |
+ let expect = join(repeat([v:servername], 4), 'ONE') | |
+ if s:test_remote_expr3_count != 3 || result != expect | |
+ throw 'test_remote_expr3: failed' | |
+ endif | |
+ call s:close_all_servers() | |
+endfunction | |
+ | |
+function! s:test_reply1() | |
+ echomsg 'test_reply1: Test for remote_peek().' | |
+ call s:start_server('one') | |
+ let cmd = '<C-\><C-N>:call server2client(expand("<client>"), "hello")<CR>' | |
+ call remote_send('one', cmd, 'serverid') | |
+ call WaitResponse('one') | |
+ let available = remote_peek(serverid, 'result') | |
+ if !available || result !=# 'hello' | |
+ throw 'test_reply1: failed' | |
+ endif | |
+ " clear | |
+ while remote_peek(serverid) | |
+ call remote_read(serverid) | |
+ endwhile | |
+ call s:close_all_servers() | |
+endfunction | |
+ | |
+function! s:test_reply2() | |
+ echomsg 'test_reply2: Test for remote_read().' | |
+ call s:start_server('one') | |
+ let cmd = '<C-\><C-N>:call server2client(expand("<client>"), "hello")<CR>' | |
+ call remote_send('one', cmd, 'serverid') | |
+ call WaitResponse('one') | |
+ let result = remote_read(serverid) | |
+ if result !=# 'hello' | |
+ throw 'test_reply2: failed' | |
+ endif | |
+ " clear | |
+ while remote_peek(serverid) | |
+ call remote_read(serverid) | |
+ endwhile | |
+ call s:close_all_servers() | |
+endfunction | |
+ | |
+function! s:test_reply3() | |
+ echomsg 'test_reply3: Test for remote_read() with remote_expr.' | |
+ call s:start_server('one') | |
+ let cmd = 'server2client(expand("<client>"), "hello")' | |
+ call remote_expr('one', cmd, 'serverid') | |
+ call WaitResponse('one') | |
+ let result = remote_read(serverid) | |
+ if result !=# 'hello' | |
+ throw 'test_reply3: failed' | |
+ endif | |
+ " clear | |
+ while remote_peek(serverid) | |
+ call remote_read(serverid) | |
+ endwhile | |
+ call s:close_all_servers() | |
+endfunction | |
+ | |
+function! s:test_reply4() | |
+ echomsg 'test_reply4: Test for RemoteReply autocommand.' | |
+ let s:test_reply4_result = 'deadbeaf' | |
+ call s:start_server('one') | |
+ let cmd = '<C-\><C-N>:call server2client(expand("<client>"), "hello")<CR>' | |
+ call remote_send('one', cmd, 'serverid') | |
+ augroup TestReply4 | |
+ au! | |
+ execute printf("autocmd RemoteReply %s let s:test_reply4_result = expand('<afile>')", serverid) | |
+ augroup END | |
+ call WaitResponse('one') | |
+ if s:test_reply4_result !=# 'hello' | |
+ throw 'test_reply4: failed' | |
+ endif | |
+ " clear | |
+ while remote_peek(serverid) | |
+ call remote_read(serverid) | |
+ endwhile | |
+ call s:close_all_servers() | |
+ augroup TestReply4 | |
+ au! | |
+ augroup END | |
+endfunction | |
+ | |
+function! s:test_reply5() | |
+ echomsg 'test_reply5: Test for remote_read with sleep' | |
+ call s:start_server('one') | |
+ let cmd = '<C-\><C-N>:sleep 2 | call server2client(expand("<client>"), "hello")<CR>' | |
+ call remote_send('one', cmd, 'serverid') | |
+ let result = remote_read(serverid) | |
+ if result !=# 'hello' | |
+ throw 'test_reply5: failed' | |
+ endif | |
+ " clear | |
+ while remote_peek(serverid) | |
+ call remote_read(serverid) | |
+ endwhile | |
+ call s:close_all_servers() | |
+endfunction | |
+ | |
+function! s:test_error1() | |
+ echomsg 'test_error1: Test for non-existence serverid' | |
+ let ok = 0 | |
+ try | |
+ call remote_expr('nonexistence', '0') | |
+ catch /^Vim\%((\a\+)\)\=:E241:/ | |
+ " E241: Unable to send to %s | |
+ let ok = 1 | |
+ endtry | |
+ if !ok | |
+ throw 'test_error1: failed' | |
+ endif | |
+endfunction | |
+ | |
+function! s:test_error2() | |
+ echomsg 'test_error2: Test for invalid expression' | |
+ call s:start_server('one') | |
+ let ok = 0 | |
+ try | |
+ call remote_expr('one', 'invalid expression') | |
+ catch /^Vim\%((\a\+)\)\=:E449:/ | |
+ " E449: Invalid expression received | |
+ let ok = 1 | |
+ endtry | |
+ if !ok | |
+ throw 'test_error2: failed' | |
+ endif | |
+ call s:close_all_servers() | |
+endfunction | |
+ | |
+function! s:test_error3() | |
+ echomsg 'test_error3: Test for exception' | |
+ call s:start_server('one') | |
+ call remote_send('one', '<C-\><C-N>:function Raise()<CR>throw "Raise()"<CR>endfunction<CR><CR>') | |
+ sleep 1 | |
+ let result = remote_expr('one', 'Raise()') | |
+ if result !=# '0' | |
+ throw 'test_error3: failed' | |
+ endif | |
+ call s:close_all_servers() | |
+endfunction | |
+ | |
+function! s:test_error4() | |
+ echomsg 'test_error4: Test for remote_read() with non-existence serverid' | |
+ let ok = 0 | |
+ try | |
+ call remote_read('0xffffffff') | |
+ catch /^Vim\%((\a\+)\)\=:E277:/ | |
+ " E277: Unable to read a server reply | |
+ let ok = 1 | |
+ endtry | |
+ if !ok | |
+ throw 'test_error4: failed' | |
+ endif | |
+endfunction | |
+ | |
+function! s:main() | |
+ for servermode in ['cui', 'gui'] | |
+ let s:servermode = servermode | |
+ call s:test_loosename1() | |
+ call s:test_loosename2() | |
+ call s:test_loosename3() | |
+ call s:test_loosename4() | |
+ call s:test_remote_send1() | |
+ call s:test_remote_expr1() | |
+ call s:test_remote_expr2() | |
+ call s:test_remote_expr3() | |
+ call s:test_reply1() | |
+ call s:test_reply2() | |
+ call s:test_reply3() | |
+ call s:test_reply4() | |
+ call s:test_reply5() | |
+ call s:test_error1() | |
+ call s:test_error2() | |
+ call s:test_error3() | |
+ call s:test_error4() | |
+ endfor | |
+ echomsg "all test done" | |
+endfunction | |
+ | |
+try | |
+ call s:main() | |
+endtry | |
+ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
このパッチを当てたvimが欲しくなった為、本家のvimをforkして当ててみました。
https://github.com/karino2/vim/tree/cmdsrv-nox