1/* vi:set ts=8 sts=4 sw=4:
2 *
3 * if_sniff.c Interface between Vim and SNiFF+
4 *
5 * See README.txt for an overview of the Vim source code.
6 */
7
8#include "vim.h"
9
10#ifdef WIN32
11# include <stdio.h>
12# include "vimio.h"
13# include <process.h>
14# include <string.h>
15# include <assert.h>
16#else
17# ifdef FEAT_GUI_X11
18#  include "gui_x11.pro"
19# endif
20# include "os_unixx.h"
21#endif
22
23static int sniffemacs_pid;
24
25int fd_from_sniff;
26int sniff_connected = 0;
27int sniff_request_waiting = 0;
28int want_sniff_request = 0;
29
30#define MAX_REQUEST_LEN 512
31
32#define NEED_SYMBOL	2
33#define EMPTY_SYMBOL	4
34#define NEED_FILE	8
35#define SILENT		16
36#define DISCONNECT	32
37#define CONNECT		64
38
39#define RQ_NONE		0
40#define RQ_SIMPLE	1
41#define RQ_CONTEXT	NEED_FILE + NEED_SYMBOL
42#define RQ_SCONTEXT	NEED_FILE + NEED_SYMBOL + EMPTY_SYMBOL
43#define RQ_NOSYMBOL	NEED_FILE
44#define RQ_SILENT	RQ_NOSYMBOL + SILENT
45#define RQ_CONNECT	RQ_NONE + CONNECT
46#define RQ_DISCONNECT	RQ_SIMPLE + DISCONNECT
47
48struct sn_cmd
49{
50    char *cmd_name;
51    char cmd_code;
52    char *cmd_msg;
53    int  cmd_type;
54};
55
56struct sn_cmd_list
57{
58    struct sn_cmd* sniff_cmd;
59    struct sn_cmd_list* next_cmd;
60};
61
62static struct sn_cmd sniff_cmds[] =
63{
64    { "toggle",		'e', N_("Toggle implementation/definition"),RQ_SCONTEXT },
65    { "superclass",	's', N_("Show base class of"),		RQ_CONTEXT },
66    { "overridden",	'm', N_("Show overridden member function"),RQ_SCONTEXT },
67    { "retrieve-file",	'r', N_("Retrieve from file"),		RQ_CONTEXT },
68    { "retrieve-project",'p', N_("Retrieve from project"),	RQ_CONTEXT },
69    { "retrieve-all-projects",
70			'P', N_("Retrieve from all projects"),	RQ_CONTEXT },
71    { "retrieve-next",	'R', N_("Retrieve"),	RQ_CONTEXT },
72    { "goto-symbol",	'g', N_("Show source of"),		RQ_CONTEXT },
73    { "find-symbol",	'f', N_("Find symbol"),			RQ_CONTEXT },
74    { "browse-class",	'w', N_("Browse class"),		RQ_CONTEXT },
75    { "hierarchy",	't', N_("Show class in hierarchy"),	RQ_CONTEXT },
76    { "restr-hier",	'T', N_("Show class in restricted hierarchy"),RQ_CONTEXT },
77    { "xref-to",	'x', N_("Xref refers to"),		RQ_CONTEXT },
78    { "xref-by",	'X', N_("Xref referred by"),		RQ_CONTEXT },
79    { "xref-has",	'c', N_("Xref has a"),			RQ_CONTEXT },
80    { "xref-used-by",	'C', N_("Xref used by"),		RQ_CONTEXT },
81    { "show-docu",	'd', N_("Show docu of"),		RQ_CONTEXT },
82    { "gen-docu",	'D', N_("Generate docu for"),		RQ_CONTEXT },
83    { "connect",	'y', NULL,				RQ_CONNECT },
84    { "disconnect",	'q', NULL,				RQ_DISCONNECT },
85    { "font-info",	'z', NULL,				RQ_SILENT },
86    { "update",		'u', NULL,				RQ_SILENT },
87    { NULL,		'\0', NULL, 0}
88};
89
90
91static char *SniffEmacs[2] = {"sniffemacs", (char *)NULL};  /* Yes, Emacs! */
92static int fd_to_sniff;
93static int sniff_will_disconnect = 0;
94static char msg_sniff_disconnect[] = N_("Cannot connect to SNiFF+. Check environment (sniffemacs must be found in $PATH).\n");
95static char sniff_rq_sep[] = " ";
96static struct sn_cmd_list *sniff_cmd_ext = NULL;
97
98/* Initializing vim commands
99 * executed each time vim connects to Sniff
100 */
101static char *init_cmds[]= {
102    "augroup sniff",
103    "autocmd BufWritePost * sniff update",
104    "autocmd BufReadPost * sniff font-info",
105    "autocmd VimLeave * sniff disconnect",
106    "augroup END",
107
108    "let g:sniff_connected = 1",
109
110    "if ! exists('g:sniff_mappings_sourced')|"
111	"if ! exists('g:sniff_mappings')|"
112	    "if exists('$SNIFF_DIR4')|"
113		"let g:sniff_mappings='$SNIFF_DIR4/config/integrations/vim/sniff.vim'|"
114	    "else|"
115		"let g:sniff_mappings='$SNIFF_DIR/config/sniff.vim'|"
116	    "endif|"
117	"endif|"
118	"let g:sniff_mappings=expand(g:sniff_mappings)|"
119	"if filereadable(g:sniff_mappings)|"
120	    "execute 'source' g:sniff_mappings|"
121	    "let g:sniff_mappings_sourced=1|"
122	"endif|"
123    "endif",
124
125    NULL
126};
127
128/*-------- Function Prototypes ----------------------------------*/
129
130static int ConnectToSniffEmacs __ARGS((void));
131static void sniff_connect __ARGS((void));
132static void HandleSniffRequest __ARGS((char* buffer));
133static int get_request __ARGS((int fd, char *buf, int maxlen));
134static void WriteToSniff __ARGS((char *str));
135static void SendRequest __ARGS((struct sn_cmd *command, char* symbol));
136static void vi_msg __ARGS((char *));
137static void vi_error_msg __ARGS((char *));
138static char *vi_symbol_under_cursor __ARGS((void));
139static void vi_open_file __ARGS((char *));
140static char *vi_buffer_name __ARGS((void));
141static buf_T *vi_find_buffer __ARGS((char *));
142static void vi_exec_cmd __ARGS((char *));
143static void vi_set_cursor_pos __ARGS((long char_nr));
144static long vi_cursor_pos __ARGS((void));
145
146/* debug trace */
147#if 0
148static FILE* _tracefile = NULL;
149#define SNIFF_TRACE_OPEN(file) if (!_tracefile) _tracefile = fopen(file, "w")
150#define SNIFF_TRACE(msg) fprintf(_tracefile, msg); fflush(_tracefile);
151#define SNIFF_TRACE1(msg, arg) fprintf(_tracefile, msg,arg); fflush(_tracefile);
152#define SNIFF_TRACE_CLOSE fclose(_tracefile); _tracefile=NULL;
153#else
154#define SNIFF_TRACE_OPEN(file)
155#define SNIFF_TRACE(msg)
156#define SNIFF_TRACE1(msg, arg)
157#define SNIFF_TRACE_CLOSE
158#endif
159
160/*-------- Windows Only Declarations -----------------------------*/
161#ifdef WIN32
162
163static int  sniff_request_processed=1;
164static HANDLE sniffemacs_handle=NULL;
165static HANDLE readthread_handle=NULL;
166static HANDLE handle_to_sniff=NULL;
167static HANDLE handle_from_sniff=NULL;
168
169struct sniffBufNode
170{
171    struct sniffBufNode *next;
172    int    bufLen;
173    char   buf[MAX_REQUEST_LEN];
174};
175static struct sniffBufNode *sniffBufStart=NULL;
176static struct sniffBufNode *sniffBufEnd=NULL;
177static HANDLE hBufferMutex=NULL;
178
179# ifdef FEAT_GUI_W32
180    extern HWND s_hwnd;       /* gvim's Window handle */
181# endif
182/*
183 * some helper functions for Windows port only
184 */
185
186    static HANDLE
187ExecuteDetachedProgram(char *szBinary, char *szCmdLine,
188    HANDLE hStdInput, HANDLE hStdOutput)
189{
190    BOOL bResult;
191    DWORD nError;
192    PROCESS_INFORMATION aProcessInformation;
193    PROCESS_INFORMATION *pProcessInformation= &aProcessInformation;
194    STARTUPINFO aStartupInfo;
195    STARTUPINFO *pStartupInfo= &aStartupInfo;
196    DWORD dwCreationFlags= 0;
197    char szPath[512];
198    HINSTANCE hResult;
199
200    hResult = FindExecutable(szBinary, ".", szPath);
201    if ((int)hResult <= 32)
202    {
203	/* can't find the exe file */
204	return NULL;
205    }
206
207    ZeroMemory(pStartupInfo, sizeof(*pStartupInfo));
208    pStartupInfo->dwFlags= STARTF_USESHOWWINDOW|STARTF_USESTDHANDLES;
209    pStartupInfo->hStdInput = hStdInput;
210    pStartupInfo->hStdOutput = hStdOutput;
211    pStartupInfo->wShowWindow= SW_HIDE;
212    pStartupInfo->cb = sizeof(STARTUPINFO);
213
214    bResult= CreateProcess(
215	szPath,
216	szCmdLine,
217	NULL,    /* security attr for process */
218	NULL,    /* security attr for primary thread */
219	TRUE,    /* DO inherit stdin and stdout */
220	dwCreationFlags, /* creation flags */
221	NULL,    /* environment */
222	".",    /* current directory */
223	pStartupInfo,   /* startup info: NULL crashes  */
224	pProcessInformation /* process information: NULL crashes */
225    );
226    nError= GetLastError();
227    if (bResult)
228    {
229	CloseHandle(pProcessInformation->hThread);
230	CloseHandle(hStdInput);
231	CloseHandle(hStdOutput);
232	return(pProcessInformation->hProcess);
233    }
234    else
235	return(NULL);
236}
237
238/*
239 * write to the internal Thread / Thread communications buffer.
240 * Return TRUE if successful, FALSE else.
241 */
242    static BOOL
243writeToBuffer(char *msg, int len)
244{
245    DWORD dwWaitResult;     /* Request ownership of mutex. */
246    struct sniffBufNode *bn;
247    int bnSize;
248
249    SNIFF_TRACE1("writeToBuffer %d\n", len);
250    bnSize = sizeof(struct sniffBufNode) - MAX_REQUEST_LEN + len + 1;
251    if (bnSize < 128) bnSize = 128; /* minimum length to avoid fragmentation */
252    bn = (struct sniffBufNode *)malloc(bnSize);
253    if (!bn)
254	return FALSE;
255
256    memcpy(bn->buf, msg, len);
257    bn->buf[len]='\0';    /* terminate CString for added safety */
258    bn->next = NULL;
259    bn->bufLen = len;
260    /* now, acquire a Mutex for adding the string to our linked list */
261    dwWaitResult = WaitForSingleObject(
262	hBufferMutex,   /* handle of mutex */
263	1000L);   /* one-second time-out interval */
264    if (dwWaitResult == WAIT_OBJECT_0)
265    {
266	/* The thread got mutex ownership. */
267	if (sniffBufEnd)
268	{
269	    sniffBufEnd->next = bn;
270	    sniffBufEnd = bn;
271	}
272	else
273	    sniffBufStart = sniffBufEnd = bn;
274	/* Release ownership of the mutex object. */
275	if (! ReleaseMutex(hBufferMutex))
276	{
277	    /* Deal with error. */
278	}
279	return TRUE;
280    }
281
282    /* Cannot get mutex ownership due to time-out or mutex object abandoned. */
283    free(bn);
284    return FALSE;
285}
286
287/*
288 * read from the internal Thread / Thread communications buffer.
289 * Return TRUE if successful, FALSE else.
290 */
291    static int
292ReadFromBuffer(char *buf, int maxlen)
293{
294    DWORD dwWaitResult;     /* Request ownership of mutex. */
295    int   theLen;
296    struct sniffBufNode *bn;
297
298    dwWaitResult = WaitForSingleObject(
299	hBufferMutex,   /* handle of mutex */
300	1000L);		/* one-second time-out interval */
301    if (dwWaitResult == WAIT_OBJECT_0)
302    {
303	if (!sniffBufStart)
304	{
305	    /* all pending Requests Processed */
306	    theLen = 0;
307	}
308	else
309	{
310	    bn = sniffBufStart;
311	    theLen = bn->bufLen;
312	    SNIFF_TRACE1("ReadFromBuffer %d\n", theLen);
313	    if (theLen >= maxlen)
314	    {
315		/* notify the user of buffer overflow? */
316		theLen = maxlen-1;
317	    }
318	    memcpy(buf, bn->buf, theLen);
319	    buf[theLen] = '\0';
320	    if (! (sniffBufStart = bn->next))
321	    {
322		sniffBufEnd = NULL;
323		sniff_request_processed = 1;
324	    }
325	    free(bn);
326	}
327	if (! ReleaseMutex(hBufferMutex))
328	{
329	    /* Deal with error. */
330	}
331	return theLen;
332    }
333
334    /* Cannot get mutex ownership due to time-out or mutex object abandoned. */
335    return -1;
336}
337
338/* on Win32, a separate Thread reads the input pipe. get_request is not needed here. */
339    static void __cdecl
340SniffEmacsReadThread(void *dummy)
341{
342    static char	ReadThreadBuffer[MAX_REQUEST_LEN];
343    int		ReadThreadLen=0;
344    int		result=0;
345    int		msgLen=0;
346    char	*msgStart, *msgCur;
347
348    SNIFF_TRACE("begin thread\n");
349    /* Read from the pipe to SniffEmacs */
350    while (sniff_connected)
351    {
352	if (!ReadFile(handle_from_sniff,
353		ReadThreadBuffer + ReadThreadLen,    /* acknowledge rest in buffer */
354		MAX_REQUEST_LEN - ReadThreadLen,
355		&result,
356		NULL))
357	{
358	    DWORD err = GetLastError();
359	    result = -1;
360	}
361
362	if (result < 0)
363	{
364	    /* probably sniffemacs died... log the Error? */
365	    sniff_disconnect(1);
366	}
367	else if (result > 0)
368	{
369	    ReadThreadLen += result-1;   /* total length of valid chars */
370	    for(msgCur=msgStart=ReadThreadBuffer; ReadThreadLen > 0; msgCur++, ReadThreadLen--)
371	    {
372		if (*msgCur == '\0' || *msgCur == '\r' || *msgCur == '\n')
373		{
374		    msgLen = msgCur-msgStart; /* don't add the CR/LF chars */
375		    if (msgLen > 0)
376			writeToBuffer(msgStart, msgLen);
377		    msgStart = msgCur + 1; /* over-read single CR/LF chars */
378		}
379	    }
380
381	/* move incomplete message to beginning of buffer */
382	ReadThreadLen = msgCur - msgStart;
383	if (ReadThreadLen > 0)
384	    mch_memmove(ReadThreadBuffer, msgStart, ReadThreadLen);
385
386	if (sniff_request_processed)
387	{
388	    /* notify others that new data has arrived */
389	    sniff_request_processed = 0;
390	    sniff_request_waiting = 1;
391#ifdef FEAT_GUI_W32
392	    PostMessage(s_hwnd, WM_USER, (WPARAM)0, (LPARAM)0);
393#endif
394	    }
395	}
396    }
397    SNIFF_TRACE("end thread\n");
398}
399#endif /* WIN32 */
400/*-------- End of Windows Only Declarations ------------------------*/
401
402
403/* ProcessSniffRequests
404 * Function that should be called from outside
405 * to process the waiting sniff requests
406 */
407    void
408ProcessSniffRequests()
409{
410    static char buf[MAX_REQUEST_LEN];
411    int len;
412
413    while (sniff_connected)
414    {
415#ifdef WIN32
416	len = ReadFromBuffer(buf, sizeof(buf));
417#else
418	len = get_request(fd_from_sniff, buf, sizeof(buf));
419#endif
420	if (len < 0)
421	{
422	    vi_error_msg(_("E274: Sniff: Error during read. Disconnected"));
423	    sniff_disconnect(1);
424	    break;
425	}
426	else if (len > 0)
427	    HandleSniffRequest( buf );
428	else
429	    break;
430    }
431
432    if (sniff_will_disconnect)	/* Now the last msg has been processed */
433	sniff_disconnect(1);
434}
435
436    static struct sn_cmd *
437find_sniff_cmd(cmd)
438    char *cmd;
439{
440    struct sn_cmd *sniff_cmd = NULL;
441    int i;
442    for(i=0; sniff_cmds[i].cmd_name; i++)
443    {
444	if (!strcmp(cmd, sniff_cmds[i].cmd_name))
445	{
446	    sniff_cmd = &sniff_cmds[i];
447	    break;
448	}
449    }
450    if (!sniff_cmd)
451    {
452	struct sn_cmd_list *list = sniff_cmd_ext;
453	while(list)
454	{
455	    if (!strcmp(cmd, list->sniff_cmd->cmd_name))
456	    {
457		sniff_cmd = list->sniff_cmd;
458		break;
459	    }
460	    list = list->next_cmd;
461	}
462    }
463    return sniff_cmd;
464}
465
466    static int
467add_sniff_cmd(cmd, def, msg)
468    char *cmd;
469    char *def;
470    char *msg;
471{
472    int rc = 0;
473    if (def != NULL && def[0] != NUL && find_sniff_cmd(cmd) == NULL)
474    {
475	struct sn_cmd_list *list = sniff_cmd_ext;
476	struct sn_cmd *sniff_cmd = (struct sn_cmd*)malloc(sizeof(struct sn_cmd));
477	struct sn_cmd_list *cmd_node = (struct sn_cmd_list*)malloc(sizeof(struct sn_cmd_list));
478	int rq_type = 0;
479
480	/* unescape message text */
481	char *p = msg;
482	char *end = p+strlen(msg);
483	while(*p)
484	{
485	    if (*p == '\\')
486		mch_memmove(p,p+1,end-p);
487	    p++;
488	}
489	SNIFF_TRACE1("request name = %s\n",cmd);
490	SNIFF_TRACE1("request def = %s\n",def);
491	SNIFF_TRACE1("request msg = %s\n",msg);
492
493	while(list && list->next_cmd)
494	    list = list->next_cmd;
495	if (!list)
496	    sniff_cmd_ext = cmd_node;
497	else
498	    list->next_cmd = cmd_node;
499
500	sniff_cmd->cmd_name = cmd;
501	sniff_cmd->cmd_code = def[0];
502	sniff_cmd->cmd_msg = msg;
503	switch(def[1])
504	{
505	    case 'f':
506		rq_type = RQ_NOSYMBOL;
507		break;
508	    case 's':
509		rq_type = RQ_CONTEXT;
510		break;
511	    case 'S':
512		rq_type = RQ_SCONTEXT;
513		break;
514	    default:
515		rq_type = RQ_SIMPLE;
516		break;
517	}
518	sniff_cmd->cmd_type = rq_type;
519	cmd_node->sniff_cmd = sniff_cmd;
520	cmd_node->next_cmd = NULL;
521	rc = 1;
522    }
523    return rc;
524}
525
526/* ex_sniff
527 * Handle ":sniff" command
528 */
529    void
530ex_sniff(eap)
531    exarg_T	*eap;
532{
533    char_u	*arg = eap->arg;
534    char_u *symbol = NULL;
535    char_u *cmd = NULL;
536
537    SNIFF_TRACE_OPEN("if_sniff.log");
538    if (ends_excmd(*arg))	/* no request: print available commands */
539    {
540	int i;
541	msg_start();
542	msg_outtrans_attr((char_u *)"-- SNiFF+ commands --", hl_attr(HLF_T));
543	for(i=0; sniff_cmds[i].cmd_name; i++)
544	{
545	    msg_putchar('\n');
546	    msg_outtrans((char_u *)":sniff ");
547	    msg_outtrans((char_u *)sniff_cmds[i].cmd_name);
548	}
549	msg_putchar('\n');
550	msg_outtrans((char_u *)_("SNiFF+ is currently "));
551	if (!sniff_connected)
552	    msg_outtrans((char_u *)_("not "));
553	msg_outtrans((char_u *)_("connected"));
554	msg_end();
555    }
556    else	/* extract command name and symbol if present */
557    {
558	symbol = skiptowhite(arg);
559	cmd  = vim_strnsave(arg, (int)(symbol-arg));
560	symbol = skipwhite(symbol);
561	if (ends_excmd(*symbol))
562	    symbol = NULL;
563	if (!strcmp((char *)cmd, "addcmd"))
564	{
565	    char_u *def = skiptowhite(symbol);
566	    char_u *name = vim_strnsave(symbol, (int)(def-symbol));
567	    char_u *msg;
568	    def = skipwhite(def);
569	    msg = skiptowhite(def);
570	    def = vim_strnsave(def, (int)(msg-def));
571	    msg = skipwhite(msg);
572	    if (ends_excmd(*msg))
573		msg = vim_strsave(name);
574	    else
575		msg = vim_strnsave(msg, (int)(skiptowhite_esc(msg)-msg));
576	    if (!add_sniff_cmd((char*)name, (char*)def, (char*)msg))
577	    {
578		vim_free(msg);
579		vim_free(def);
580		vim_free(name);
581	    }
582	}
583	else
584	{
585	    struct sn_cmd* sniff_cmd = find_sniff_cmd((char*)cmd);
586	    if (sniff_cmd)
587		SendRequest(sniff_cmd, (char *)symbol);
588	    else
589		EMSG2(_("E275: Unknown SNiFF+ request: %s"), cmd);
590	}
591	vim_free(cmd);
592    }
593}
594
595
596    static void
597sniff_connect()
598{
599    if (sniff_connected)
600	return;
601    if (ConnectToSniffEmacs())
602	vi_error_msg(_("E276: Error connecting to SNiFF+"));
603    else
604    {
605	int i;
606
607	for (i = 0; init_cmds[i]; i++)
608	    vi_exec_cmd(init_cmds[i]);
609    }
610}
611
612    void
613sniff_disconnect(immediately)
614    int immediately;
615{
616    if (!sniff_connected)
617	return;
618    if (immediately)
619    {
620	vi_exec_cmd("augroup sniff");
621	vi_exec_cmd("au!");
622	vi_exec_cmd("augroup END");
623	vi_exec_cmd("unlet g:sniff_connected");
624	sniff_connected = 0;
625	want_sniff_request = 0;
626	sniff_will_disconnect = 0;
627#ifdef FEAT_GUI
628	if (gui.in_use)
629	    gui_mch_wait_for_chars(0L);
630#endif
631#ifdef WIN32
632	while(sniffBufStart != NULL)
633	{
634	    struct sniffBufNode *node = sniffBufStart;
635	    sniffBufStart = sniffBufStart->next;
636	    free(node);
637	}
638	sniffBufStart = sniffBufEnd = NULL;
639	sniff_request_processed = 1;
640	CloseHandle(handle_to_sniff);
641	CloseHandle(handle_from_sniff);
642	WaitForSingleObject(sniffemacs_handle, 1000L);
643	CloseHandle(sniffemacs_handle);
644	sniffemacs_handle = NULL;
645	WaitForSingleObject(readthread_handle, 1000L);
646	readthread_handle = NULL;
647	CloseHandle(hBufferMutex);
648	hBufferMutex = NULL;
649	SNIFF_TRACE_CLOSE;
650#else
651	close(fd_to_sniff);
652	close(fd_from_sniff);
653	wait(NULL);
654#endif
655    }
656    else
657    {
658#ifdef WIN32
659	_sleep(2);
660	if (!sniff_request_processed)
661	    ProcessSniffRequests();
662#else
663	sleep(2);		    /* Incoming msg could disturb edit */
664#endif
665	sniff_will_disconnect = 1;  /* We expect disconnect msg in 2 secs */
666    }
667}
668
669
670/* ConnectToSniffEmacs
671 * Connect to Sniff: returns 1 on error
672 */
673    static int
674ConnectToSniffEmacs()
675{
676#ifdef WIN32		/* Windows Version of the Code */
677    HANDLE ToSniffEmacs[2], FromSniffEmacs[2];
678    SECURITY_ATTRIBUTES sa;
679
680    sa.nLength = sizeof(sa);
681    sa.lpSecurityDescriptor = NULL;
682    sa.bInheritHandle = TRUE;
683
684    if (! CreatePipe(&ToSniffEmacs[0], &ToSniffEmacs[1], &sa, 0))
685	return 1;
686    if (! CreatePipe(&FromSniffEmacs[0], &FromSniffEmacs[1], &sa, 0))
687	return 1;
688
689    sniffemacs_handle = ExecuteDetachedProgram(SniffEmacs[0], SniffEmacs[0],
690	ToSniffEmacs[0], FromSniffEmacs[1]);
691
692    if (sniffemacs_handle)
693    {
694	handle_to_sniff  = ToSniffEmacs[1];
695	handle_from_sniff = FromSniffEmacs[0];
696	sniff_connected = 1;
697	hBufferMutex = CreateMutex(
698	    NULL,			/* no security attributes */
699	    FALSE,			/* initially not owned */
700	    "SniffReadBufferMutex");    /* name of mutex */
701	if (hBufferMutex == NULL)
702	{
703	    /* Check for error. */
704	}
705	readthread_handle = (HANDLE)_beginthread(SniffEmacsReadThread, 0, NULL);
706	return 0;
707    }
708    else
709    {
710	/* error in spawn() */
711	return 1;
712    }
713
714#else		/* UNIX Version of the Code */
715    int ToSniffEmacs[2], FromSniffEmacs[2];
716
717    if (pipe(ToSniffEmacs) != 0)
718	return 1;
719    if (pipe(FromSniffEmacs) != 0)
720	return 1;
721
722    /* fork */
723    if ((sniffemacs_pid=fork()) == 0)
724    {
725	/* child */
726
727	/* prepare communication pipes */
728	close(ToSniffEmacs[1]);
729	close(FromSniffEmacs[0]);
730
731	dup2(ToSniffEmacs[0],fileno(stdin));   /* write to ToSniffEmacs[1] */
732	dup2(FromSniffEmacs[1],fileno(stdout));/* read from FromSniffEmacs[0] */
733
734	close(ToSniffEmacs[0]);
735	close(FromSniffEmacs[1]);
736
737	/* start sniffemacs */
738	execvp (SniffEmacs[0], SniffEmacs);
739	{
740/*	    FILE *out = fdopen(FromSniffEmacs[1], "w"); */
741	    sleep(1);
742	    fputs(_(msg_sniff_disconnect), stdout);
743	    fflush(stdout);
744	    sleep(3);
745#ifdef FEAT_GUI
746	    if (gui.in_use)
747		gui_exit(1);
748#endif
749	    exit(1);
750	}
751	return 1;
752    }
753    else if (sniffemacs_pid > 0)
754    {
755	/* parent process */
756	close(ToSniffEmacs[0]);
757	fd_to_sniff  = ToSniffEmacs[1];
758	close(FromSniffEmacs[1]);
759	fd_from_sniff = FromSniffEmacs[0];
760	sniff_connected = 1;
761	return 0;
762    }
763    else   /* error in fork() */
764	return 1;
765#endif		/* UNIX Version of the Code */
766}
767
768
769/* HandleSniffRequest
770 * Handle one request from SNiFF+
771 */
772    static void
773HandleSniffRequest(buffer)
774    char *buffer;
775{
776    char VICommand[MAX_REQUEST_LEN];
777    char command;
778    char *arguments;
779    char *token;
780    char *argv[3];
781    int argc = 0;
782    buf_T  *buf;
783
784    const char *SetTab     = "set tabstop=%d";
785    const char *SelectBuf  = "buf %s";
786    const char *DeleteBuf  = "bd %s";
787    const char *UnloadBuf  = "bun %s";
788    const char *GotoLine   = "%d";
789
790    command   = buffer[0];
791    arguments = &buffer[1];
792    token = strtok(arguments, sniff_rq_sep);
793    while(argc <3)
794    {
795	if (token)
796	{
797	    argv[argc] = (char*)vim_strsave((char_u *)token);
798	    token = strtok(0, sniff_rq_sep);
799	}
800	else
801	    argv[argc] = strdup("");
802	argc++;
803    }
804
805    switch (command)
806    {
807	case 'o' :  /* visit file at char pos */
808	case 'O' :  /* visit file at line number */
809	{
810	    char *file = argv[0];
811	    int position = atoi(argv[1]);
812
813	    buf = vi_find_buffer(file);
814	    setpcmark();      /* insert current pos in jump list [mark.c]*/
815	    if (!buf)
816		vi_open_file(file);
817	    else if (buf!=curbuf)
818	    {
819		vim_snprintf(VICommand, sizeof(VICommand),
820						     (char *)SelectBuf, file);
821		vi_exec_cmd(VICommand);
822	    }
823	    if (command == 'o')
824		vi_set_cursor_pos((long)position);
825	    else
826	    {
827		vim_snprintf(VICommand, sizeof(VICommand),
828					     (char *)GotoLine, (int)position);
829		vi_exec_cmd(VICommand);
830	    }
831	    checkpcmark();	/* [mark.c] */
832#if defined(FEAT_GUI_X11) || defined(FEAT_GUI_W32)
833	    if (gui.in_use && !gui.in_focus)  /* Raise Vim Window */
834	    {
835# ifdef FEAT_GUI_W32
836		SetForegroundWindow(s_hwnd);
837# else
838		extern Widget vimShell;
839
840		XSetInputFocus(gui.dpy, XtWindow(vimShell), RevertToNone,
841			CurrentTime);
842		XRaiseWindow(gui.dpy, XtWindow(vimShell));
843# endif
844	    }
845#endif
846	    break;
847	}
848	case 'p' :  /* path of file has changed */
849	    /* when changing from shared to private WS (checkout) */
850	{
851	    char *file = argv[0];
852	    char *new_path = argv[1];
853
854	    buf = vi_find_buffer(file);
855	    if (buf && !buf->b_changed) /* delete buffer only if not modified */
856	    {
857		vim_snprintf(VICommand, sizeof(VICommand),
858						     (char *)DeleteBuf, file);
859		vi_exec_cmd(VICommand);
860	    }
861	    vi_open_file(new_path);
862	    break;
863	}
864	case 'w' :  /* writability has changed */
865	    /* Sniff sends request twice,
866	     * but only the last one is the right one */
867	{
868	    char *file = argv[0];
869	    int writable = atoi(argv[1]);
870
871	    buf = vi_find_buffer(file);
872	    if (buf)
873	    {
874		buf->b_p_ro = !writable;
875		if (buf != curbuf)
876		{
877		    buf->b_flags |= BF_CHECK_RO + BF_NEVERLOADED;
878		    if (writable && !buf->b_changed)
879		    {
880			vim_snprintf(VICommand, sizeof(VICommand),
881						     (char *)UnloadBuf, file);
882			vi_exec_cmd(VICommand);
883		    }
884		}
885		else if (writable && !buf->b_changed)
886		{
887		    vi_exec_cmd("e");
888		}
889	    }
890	    break;
891	}
892	case 'h' :  /* highlight info */
893	    break;  /* not implemented */
894
895	case 't' :  /* Set tab width */
896	{
897	    int tab_width = atoi(argv[1]);
898
899	    if (tab_width > 0 && tab_width <= 16)
900	    {
901		vim_snprintf(VICommand, sizeof(VICommand),
902						   (char *)SetTab, tab_width);
903		vi_exec_cmd(VICommand);
904	    }
905	    break;
906	}
907	case '|':
908	{
909	    /* change the request separator */
910	    sniff_rq_sep[0] = arguments[0];
911	    /* echo the request */
912	    WriteToSniff(buffer);
913	    break;
914	}
915	case 'A' :  /* Warning/Info msg */
916	    vi_msg(arguments);
917	    if (!strncmp(arguments, "Disconnected", 12))
918		sniff_disconnect(1);	/* unexpected disconnection */
919	    break;
920	case 'a' :  /* Error msg */
921	    vi_error_msg(arguments);
922	    if (!strncmp(arguments, "Cannot connect", 14))
923		sniff_disconnect(1);
924	    break;
925
926	default :
927	    break;
928    }
929    while(argc)
930	vim_free(argv[--argc]);
931}
932
933
934#ifndef WIN32
935/* get_request
936 * read string from fd up to next newline (excluding the nl),
937 * returns  length of string
938 *	    0 if no data available or no complete line
939 *	   <0 on error
940 */
941    static int
942get_request(fd, buf, maxlen)
943    int		fd;
944    char	*buf;
945    int		maxlen;
946{
947    static char	inbuf[1024];
948    static int	pos = 0, bytes = 0;
949    int		len;
950#ifdef HAVE_SELECT
951    struct timeval tval;
952    fd_set	rfds;
953
954    FD_ZERO(&rfds);
955    FD_SET(fd, &rfds);
956    tval.tv_sec  = 0;
957    tval.tv_usec = 0;
958#else
959    struct pollfd fds;
960
961    fds.fd = fd;
962    fds.events = POLLIN;
963#endif
964
965    for (len = 0; len < maxlen; len++)
966    {
967	if (pos >= bytes)	    /* end of buffer reached? */
968	{
969#ifdef HAVE_SELECT
970	    if (select(fd + 1, &rfds, NULL, NULL, &tval) > 0)
971#else
972	    if (poll(&fds, 1, 0) > 0)
973#endif
974	    {
975		pos = 0;
976		bytes = read(fd, inbuf, sizeof(inbuf));
977		if (bytes <= 0)
978		    return bytes;
979	    }
980	    else
981	    {
982		pos = pos-len;
983		buf[0] = '\0';
984		return 0;
985	    }
986	}
987	if ((buf[len] = inbuf[pos++]) =='\n')
988	    break;
989    }
990    buf[len] = '\0';
991    return len;
992}
993#endif     /* WIN32 */
994
995
996    static void
997SendRequest(command, symbol)
998    struct sn_cmd *command;
999    char *symbol;
1000{
1001    int		cmd_type = command->cmd_type;
1002    static char cmdstr[MAX_REQUEST_LEN];
1003    static char msgtxt[MAX_REQUEST_LEN];
1004    char	*buffer_name = NULL;
1005
1006    if (cmd_type == RQ_CONNECT)
1007    {
1008	sniff_connect();
1009	return;
1010    }
1011    if (!sniff_connected && !(cmd_type & SILENT))
1012    {
1013	vi_error_msg(_("E278: SNiFF+ not connected"));
1014	return;
1015    }
1016
1017    if (cmd_type & NEED_FILE)
1018    {
1019	if (!curbuf->b_sniff)
1020	{
1021	    if (!(cmd_type & SILENT))
1022		vi_error_msg(_("E279: Not a SNiFF+ buffer"));
1023	    return;
1024	}
1025	buffer_name = vi_buffer_name();
1026	if (buffer_name == NULL)
1027	    return;
1028	if (cmd_type & NEED_SYMBOL)
1029	{
1030	    if (cmd_type & EMPTY_SYMBOL)
1031		symbol = " ";
1032	    else if (!symbol && !(symbol = vi_symbol_under_cursor()))
1033		return;	    /* error msg already displayed */
1034	}
1035
1036	if (symbol)
1037	    vim_snprintf(cmdstr, sizeof(cmdstr), "%c%s%s%ld%s%s\n",
1038		command->cmd_code,
1039		buffer_name,
1040		sniff_rq_sep,
1041		vi_cursor_pos(),
1042		sniff_rq_sep,
1043		symbol
1044	    );
1045	else
1046	    vim_snprintf(cmdstr, sizeof(cmdstr), "%c%s\n",
1047		    command->cmd_code, buffer_name);
1048    }
1049    else    /* simple request */
1050    {
1051	cmdstr[0] = command->cmd_code;
1052	cmdstr[1] = '\n';
1053	cmdstr[2] = '\0';
1054    }
1055    if (command->cmd_msg && !(cmd_type & SILENT))
1056    {
1057	if ((cmd_type & NEED_SYMBOL) && !(cmd_type & EMPTY_SYMBOL))
1058	{
1059	    vim_snprintf(msgtxt, sizeof(msgtxt), "%s: %s",
1060						 _(command->cmd_msg), symbol);
1061	    vi_msg(msgtxt);
1062	}
1063	else
1064	    vi_msg(_(command->cmd_msg));
1065    }
1066    WriteToSniff(cmdstr);
1067    if (cmd_type & DISCONNECT)
1068	sniff_disconnect(0);
1069}
1070
1071
1072
1073    static void
1074WriteToSniff(str)
1075    char *str;
1076{
1077    int bytes;
1078#ifdef WIN32
1079    if (! WriteFile(handle_to_sniff, str, strlen(str), &bytes, NULL))
1080    {
1081	DWORD err=GetLastError();
1082	bytes = -1;
1083    }
1084#else
1085    bytes = write(fd_to_sniff, str, strlen(str));
1086#endif
1087    if (bytes<0)
1088    {
1089	vi_msg(_("Sniff: Error during write. Disconnected"));
1090	sniff_disconnect(1);
1091    }
1092}
1093
1094/*-------- vim helping functions --------------------------------*/
1095
1096    static void
1097vi_msg(str)
1098    char *str;
1099{
1100    if (str != NULL && *str != NUL)
1101	MSG((char_u *)str);
1102}
1103
1104    static void
1105vi_error_msg(str)
1106    char *str;
1107{
1108    if (str != NULL && *str != NUL)
1109	EMSG((char_u *)str);
1110}
1111
1112    static void
1113vi_open_file(fname)
1114    char *fname;
1115{
1116    ++no_wait_return;
1117    do_ecmd(0, (char_u *)fname, NULL, NULL, ECMD_ONE, ECMD_HIDE+ECMD_OLDBUF,
1118	    curwin);
1119    curbuf->b_sniff = TRUE;
1120    --no_wait_return;					/* [ex_docmd.c] */
1121}
1122
1123    static buf_T *
1124vi_find_buffer(fname)
1125    char *fname;
1126{			    /* derived from buflist_findname() [buffer.c] */
1127    buf_T	*buf;
1128
1129    for (buf = firstbuf; buf != NULL; buf = buf->b_next)
1130	if (buf->b_sfname != NULL && fnamecmp(fname, buf->b_sfname) == 0)
1131	    return (buf);
1132    return NULL;
1133}
1134
1135
1136    static char *
1137vi_symbol_under_cursor()
1138{
1139    int		len;
1140    char	*symbolp;
1141    char	*p;
1142    static char sniff_symbol[256];
1143
1144    len = find_ident_under_cursor((char_u **)&symbolp, FIND_IDENT);
1145    /* [normal.c] */
1146    if (len <= 0)
1147	return NULL;
1148    for (p=sniff_symbol; len; len--)
1149	*p++ = *symbolp++;
1150    *p = '\0';
1151    return sniff_symbol;
1152}
1153
1154
1155    static char *
1156vi_buffer_name()
1157{
1158    return (char *)curbuf->b_sfname;
1159}
1160
1161    static void
1162vi_exec_cmd(vicmd)
1163    char *vicmd;
1164{
1165    do_cmdline_cmd((char_u *)vicmd);  /* [ex_docmd.c] */
1166}
1167
1168/*
1169 * Set cursor on character position
1170 * derived from cursor_pos_info() [buffer.c]
1171 */
1172    static void
1173vi_set_cursor_pos(char_pos)
1174    long char_pos;
1175{
1176    linenr_T	lnum;
1177    long	char_count = 1;  /* first position = 1 */
1178    int		line_size;
1179    int		eol_size;
1180
1181    if (char_pos == 0)
1182    {
1183	char_pos = 1;
1184    }
1185    if (get_fileformat(curbuf) == EOL_DOS)
1186	eol_size = 2;
1187    else
1188	eol_size = 1;
1189    for (lnum = 1; lnum <= curbuf->b_ml.ml_line_count; ++lnum)
1190    {
1191	line_size = STRLEN(ml_get(lnum)) + eol_size;
1192	if (char_count+line_size > char_pos) break;
1193	char_count += line_size;
1194    }
1195    curwin->w_cursor.lnum = lnum;
1196    curwin->w_cursor.col  = char_pos - char_count;
1197}
1198
1199    static long
1200vi_cursor_pos()
1201{
1202    linenr_T	lnum;
1203    long	char_count=1;  /* sniff starts with pos 1 */
1204    int		line_size;
1205    int		eol_size;
1206
1207    if (curbuf->b_p_tx)
1208	eol_size = 2;
1209    else
1210	eol_size = 1;
1211    for (lnum = 1; lnum < curwin->w_cursor.lnum; ++lnum)
1212    {
1213	line_size = STRLEN(ml_get(lnum)) + eol_size;
1214	char_count += line_size;
1215    }
1216    return char_count + curwin->w_cursor.col;
1217}
1218