xref: /vim-8.2.3635/src/netbeans.c (revision cf2d8dee)
1 /* vi:set ts=8 sts=4 sw=4:
2  *
3  * VIM - Vi IMproved	by Bram Moolenaar
4  *			Netbeans integration by David Weatherford
5  *			Adopted for Win32 by Sergey Khorev
6  *
7  * Do ":help uganda"  in Vim to read copying and usage conditions.
8  * Do ":help credits" in Vim to see a list of people who contributed.
9  */
10 
11 /*
12  * Implements client side of org.netbeans.modules.emacs editor
13  * integration protocol.  Be careful!  The protocol uses offsets
14  * which are *between* characters, whereas vim uses line number
15  * and column number which are *on* characters.
16  * See ":help netbeans-protocol" for explanation.
17  *
18  * The Netbeans messages are received and queued in the gui event loop, or in
19  * the select loop when Vim runs in a terminal. These messages are processed
20  * by netbeans_parse_messages() which is invoked in the idle loop when Vim is
21  * waiting for user input. The function netbeans_parse_messages() is also
22  * called from the ":sleep" command, to allow the execution of test cases that
23  * may not invoke the idle loop.
24  */
25 
26 #include "vim.h"
27 
28 #if defined(FEAT_NETBEANS_INTG) || defined(PROTO)
29 
30 #ifndef WIN32
31 # include <netdb.h>
32 # ifdef HAVE_LIBGEN_H
33 #  include <libgen.h>
34 # endif
35 #endif
36 
37 #include "version.h"
38 
39 #define GUARDED		10000 /* typenr for "guarded" annotation */
40 #define GUARDEDOFFSET 1000000 /* base for "guarded" sign id's */
41 #define MAX_COLOR_LENGTH 32 /* max length of color name in defineAnnoType */
42 
43 /* The first implementation (working only with Netbeans) returned "1.1".  The
44  * protocol implemented here also supports A-A-P. */
45 static char *ExtEdProtocolVersion = "2.5";
46 
47 static long pos2off(buf_T *, pos_T *);
48 static pos_T *off2pos(buf_T *, long);
49 static pos_T *get_off_or_lnum(buf_T *buf, char_u **argp);
50 static long get_buf_size(buf_T *);
51 static int netbeans_keystring(char_u *keystr);
52 static void postpone_keycommand(char_u *keystr);
53 static void special_keys(char_u *args);
54 
55 static int netbeans_connect(char *, int);
56 static int getConnInfo(char *file, char **host, char **port, char **password);
57 
58 static void nb_init_graphics(void);
59 static void coloncmd(char *cmd, ...);
60 static void nb_set_curbuf(buf_T *buf);
61 static void nb_parse_cmd(char_u *);
62 static int  nb_do_cmd(int, char_u *, int, int, char_u *);
63 static void nb_send(char *buf, char *fun);
64 static void nb_free(void);
65 
66 #define NETBEANS_OPEN (channel_can_write_to(nb_channel))
67 static channel_T *nb_channel = NULL;
68 
69 static int r_cmdno;			/* current command number for reply */
70 static int dosetvisible = FALSE;
71 
72 /*
73  * Include the debugging code if wanted.
74  */
75 #ifdef NBDEBUG
76 # include "nbdebug.c"
77 #endif
78 
79 static int needupdate = 0;
80 static int inAtomic = 0;
81 
82 /*
83  * Callback invoked when the channel is closed.
84  */
85     static void
86 nb_channel_closed(void)
87 {
88     nb_channel = NULL;
89 }
90 
91 /*
92  * Close the connection and cleanup.
93  * May be called when the socket was closed earlier.
94  */
95     static void
96 netbeans_close(void)
97 {
98     if (NETBEANS_OPEN)
99     {
100 	netbeans_send_disconnect();
101 	if (nb_channel != NULL)
102 	{
103 	    /* Close the socket and remove the input handlers. */
104 	    channel_close(nb_channel, TRUE);
105 	    channel_clear(nb_channel);
106 	}
107 	nb_channel = NULL;
108     }
109 
110 #ifdef FEAT_BEVAL
111     bevalServers &= ~BEVAL_NETBEANS;
112 #endif
113 
114     needupdate = 0;
115     inAtomic = 0;
116     nb_free();
117 
118     /* remove all signs and update the screen after gutter removal */
119     coloncmd(":sign unplace *");
120     changed_window_setting();
121     update_screen(CLEAR);
122     setcursor();
123     cursor_on();
124     out_flush();
125 #ifdef FEAT_GUI
126     if (gui.in_use)
127     {
128 	gui_update_cursor(TRUE, FALSE);
129 	gui_mch_flush();
130     }
131 #endif
132 }
133 
134 #define NB_DEF_HOST "localhost"
135 #define NB_DEF_ADDR "3219"
136 #define NB_DEF_PASS "changeme"
137 
138     static int
139 netbeans_connect(char *params, int doabort)
140 {
141     int		port;
142     char	buf[32];
143     char	*hostname = NULL;
144     char	*address = NULL;
145     char	*password = NULL;
146     char	*fname;
147     char	*arg = NULL;
148 
149     if (*params == '=')
150     {
151 	/* "=fname": Read info from specified file. */
152 	if (getConnInfo(params + 1, &hostname, &address, &password) == FAIL)
153 	    return FAIL;
154     }
155     else
156     {
157 	if (*params == ':')
158 	    /* ":<host>:<addr>:<password>": get info from argument */
159 	    arg = params + 1;
160 	if (arg == NULL && (fname = getenv("__NETBEANS_CONINFO")) != NULL)
161 	{
162 	    /* "": get info from file specified in environment */
163 	    if (getConnInfo(fname, &hostname, &address, &password) == FAIL)
164 		return FAIL;
165 	}
166 	else
167 	{
168 	    if (arg != NULL)
169 	    {
170 		/* ":<host>:<addr>:<password>": get info from argument */
171 		hostname = arg;
172 		address = strchr(hostname, ':');
173 		if (address != NULL)
174 		{
175 		    *address++ = '\0';
176 		    password = strchr(address, ':');
177 		    if (password != NULL)
178 			*password++ = '\0';
179 		}
180 	    }
181 
182 	    /* Get the missing values from the environment. */
183 	    if (hostname == NULL || *hostname == '\0')
184 		hostname = getenv("__NETBEANS_HOST");
185 	    if (address == NULL)
186 		address = getenv("__NETBEANS_SOCKET");
187 	    if (password == NULL)
188 		password = getenv("__NETBEANS_VIM_PASSWORD");
189 
190 	    /* Move values to allocated memory. */
191 	    if (hostname != NULL)
192 		hostname = (char *)vim_strsave((char_u *)hostname);
193 	    if (address != NULL)
194 		address = (char *)vim_strsave((char_u *)address);
195 	    if (password != NULL)
196 		password = (char *)vim_strsave((char_u *)password);
197 	}
198     }
199 
200     /* Use the default when a value is missing. */
201     if (hostname == NULL || *hostname == '\0')
202     {
203 	vim_free(hostname);
204 	hostname = (char *)vim_strsave((char_u *)NB_DEF_HOST);
205     }
206     if (address == NULL || *address == '\0')
207     {
208 	vim_free(address);
209 	address = (char *)vim_strsave((char_u *)NB_DEF_ADDR);
210     }
211     if (password == NULL || *password == '\0')
212     {
213 	vim_free(password);
214 	password = (char *)vim_strsave((char_u *)NB_DEF_PASS);
215     }
216     if (hostname != NULL && address != NULL && password != NULL)
217     {
218 	port = atoi(address);
219 	nb_channel = channel_open(hostname, port, 3000, nb_channel_closed);
220 	if (nb_channel != NULL)
221 	{
222 	    /* success */
223 # ifdef FEAT_BEVAL
224 	    bevalServers |= BEVAL_NETBEANS;
225 # endif
226 
227 	    /* success, login */
228 	    vim_snprintf(buf, sizeof(buf), "AUTH %s\n", password);
229 	    nb_send(buf, "netbeans_connect");
230 
231 	    sprintf(buf, "0:version=0 \"%s\"\n", ExtEdProtocolVersion);
232 	    nb_send(buf, "externaleditor_version");
233 	}
234     }
235 
236     if (nb_channel == NULL && doabort)
237 	getout(1);
238 
239     vim_free(hostname);
240     vim_free(address);
241     vim_free(password);
242     return NETBEANS_OPEN ? OK : FAIL;
243 }
244 
245 /*
246  * Obtain the NetBeans hostname, port address and password from a file.
247  * Return the strings in allocated memory.
248  * Return FAIL if the file could not be read, OK otherwise (no matter what it
249  * contains).
250  */
251     static int
252 getConnInfo(char *file, char **host, char **port, char **auth)
253 {
254     FILE *fp;
255     char_u buf[BUFSIZ];
256     char_u *lp;
257     char_u *nlp;
258 #ifdef UNIX
259     struct stat	st;
260 
261     /*
262      * For Unix only accept the file when it's not accessible by others.
263      * The open will then fail if we don't own the file.
264      */
265     if (mch_stat(file, &st) == 0 && (st.st_mode & 0077) != 0)
266     {
267 	nbdebug(("Wrong access mode for NetBeans connection info file: \"%s\"\n",
268 								       file));
269 	EMSG2(_("E668: Wrong access mode for NetBeans connection info file: \"%s\""),
270 									file);
271 	return FAIL;
272     }
273 #endif
274 
275     fp = mch_fopen(file, "r");
276     if (fp == NULL)
277     {
278 	nbdebug(("Cannot open NetBeans connection info file\n"));
279 	PERROR("E660: Cannot open NetBeans connection info file");
280 	return FAIL;
281     }
282 
283     /* Read the file. There should be one of each parameter */
284     while ((lp = (char_u *)fgets((char *)buf, BUFSIZ, fp)) != NULL)
285     {
286 	if ((nlp = vim_strchr(lp, '\n')) != NULL)
287 	    *nlp = 0;	    /* strip off the trailing newline */
288 
289 	if (STRNCMP(lp, "host=", 5) == 0)
290 	{
291 	    vim_free(*host);
292 	    *host = (char *)vim_strsave(&buf[5]);
293 	}
294 	else if (STRNCMP(lp, "port=", 5) == 0)
295 	{
296 	    vim_free(*port);
297 	    *port = (char *)vim_strsave(&buf[5]);
298 	}
299 	else if (STRNCMP(lp, "auth=", 5) == 0)
300 	{
301 	    vim_free(*auth);
302 	    *auth = (char *)vim_strsave(&buf[5]);
303 	}
304     }
305     fclose(fp);
306 
307     return OK;
308 }
309 
310 
311 struct keyqueue
312 {
313     char_u	    *keystr;
314     struct keyqueue *next;
315     struct keyqueue *prev;
316 };
317 
318 typedef struct keyqueue keyQ_T;
319 
320 static keyQ_T keyHead; /* dummy node, header for circular queue */
321 
322 
323 /*
324  * Queue up key commands sent from netbeans.
325  * We store the string, because it may depend on the global mod_mask and
326  * :nbkey doesn't have a key number.
327  */
328     static void
329 postpone_keycommand(char_u *keystr)
330 {
331     keyQ_T *node;
332 
333     node = (keyQ_T *)alloc(sizeof(keyQ_T));
334     if (node == NULL)
335 	return;  /* out of memory, drop the key */
336 
337     if (keyHead.next == NULL) /* initialize circular queue */
338     {
339 	keyHead.next = &keyHead;
340 	keyHead.prev = &keyHead;
341     }
342 
343     /* insert node at tail of queue */
344     node->next = &keyHead;
345     node->prev = keyHead.prev;
346     keyHead.prev->next = node;
347     keyHead.prev = node;
348 
349     node->keystr = vim_strsave(keystr);
350 }
351 
352 /*
353  * Handle any queued-up NetBeans keycommands to be send.
354  */
355     static void
356 handle_key_queue(void)
357 {
358     int postponed = FALSE;
359 
360     while (!postponed && keyHead.next && keyHead.next != &keyHead)
361     {
362 	/* first, unlink the node */
363 	keyQ_T *node = keyHead.next;
364 	keyHead.next = node->next;
365 	node->next->prev = node->prev;
366 
367 	/* Now, send the keycommand.  This may cause it to be postponed again
368 	 * and change keyHead. */
369 	if (node->keystr != NULL)
370 	    postponed = !netbeans_keystring(node->keystr);
371 	vim_free(node->keystr);
372 
373 	/* Finally, dispose of the node */
374 	vim_free(node);
375     }
376 }
377 
378 
379 /*
380  * While there's still a command in the work queue, parse and execute it.
381  */
382     void
383 netbeans_parse_messages(void)
384 {
385     char_u	*buffer;
386     char_u	*p;
387     int		own_node;
388 
389     while (nb_channel != NULL)
390     {
391 	buffer = channel_peek(nb_channel, PART_SOCK);
392 	if (buffer == NULL)
393 	    break;	/* nothing to read */
394 
395 	/* Locate the first line in the first buffer. */
396 	p = vim_strchr(buffer, '\n');
397 	if (p == NULL)
398 	{
399 	    /* Command isn't complete.  If there is no following buffer,
400 	     * return (wait for more). If there is another buffer following,
401 	     * prepend the text to that buffer and delete this one.  */
402 	    if (channel_collapse(nb_channel, PART_SOCK) == FAIL)
403 		return;
404 	}
405 	else
406 	{
407 	    /* There is a complete command at the start of the buffer.
408 	     * Terminate it with a NUL.  When no more text is following unlink
409 	     * the buffer.  Do this before executing, because new buffers can
410 	     * be added while busy handling the command. */
411 	    *p++ = NUL;
412 	    if (*p == NUL)
413 	    {
414 		own_node = TRUE;
415 		channel_get(nb_channel, PART_SOCK);
416 	    }
417 	    else
418 		own_node = FALSE;
419 
420 	    /* now, parse and execute the commands */
421 	    nb_parse_cmd(buffer);
422 
423 	    if (own_node)
424 		/* buffer finished, dispose of it */
425 		vim_free(buffer);
426 	    else
427 		/* more follows, move it to the start */
428 		STRMOVE(buffer, p);
429 	}
430     }
431 }
432 
433 /*
434  * Handle one NUL terminated command.
435  *
436  * format of a command from netbeans:
437  *
438  *    6:setTitle!84 "a.c"
439  *
440  *    bufno
441  *     colon
442  *      cmd
443  *		!
444  *		 cmdno
445  *		    args
446  *
447  * for function calls, the ! is replaced by a /
448  */
449     static void
450 nb_parse_cmd(char_u *cmd)
451 {
452     char	*verb;
453     char	*q;
454     int		bufno;
455     int		isfunc = -1;
456 
457     if (STRCMP(cmd, "DISCONNECT") == 0)
458     {
459 	/* We assume the server knows that we can safely exit! */
460 	/* Disconnect before exiting, Motif hangs in a Select error
461 	 * message otherwise. */
462 	netbeans_close();
463 	getout(0);
464 	/* NOTREACHED */
465     }
466 
467     if (STRCMP(cmd, "DETACH") == 0)
468     {
469 	buf_T	*buf;
470 
471 	for (buf = firstbuf; buf != NULL; buf = buf->b_next)
472 	    buf->b_has_sign_column = FALSE;
473 
474 	/* The IDE is breaking the connection. */
475 	netbeans_close();
476 	return;
477     }
478 
479     bufno = strtol((char *)cmd, &verb, 10);
480 
481     if (*verb != ':')
482     {
483 	nbdebug(("    missing colon: %s\n", cmd));
484 	EMSG2("E627: missing colon: %s", cmd);
485 	return;
486     }
487     ++verb; /* skip colon */
488 
489     for (q = verb; *q; q++)
490     {
491 	if (*q == '!')
492 	{
493 	    *q++ = NUL;
494 	    isfunc = 0;
495 	    break;
496 	}
497 	else if (*q == '/')
498 	{
499 	    *q++ = NUL;
500 	    isfunc = 1;
501 	    break;
502 	}
503     }
504 
505     if (isfunc < 0)
506     {
507 	nbdebug(("    missing ! or / in: %s\n", cmd));
508 	EMSG2("E628: missing ! or / in: %s", cmd);
509 	return;
510     }
511 
512     r_cmdno = strtol(q, &q, 10);
513 
514     q = (char *)skipwhite((char_u *)q);
515 
516     if (nb_do_cmd(bufno, (char_u *)verb, isfunc, r_cmdno, (char_u *)q) == FAIL)
517     {
518 #ifdef NBDEBUG
519 	/*
520 	 * This happens because the ExtEd can send a command or 2 after
521 	 * doing a stopDocumentListen command. It doesn't harm anything
522 	 * so I'm disabling it except for debugging.
523 	 */
524 	nbdebug(("nb_parse_cmd: Command error for \"%s\"\n", cmd));
525 	EMSG("E629: bad return from nb_do_cmd");
526 #endif
527     }
528 }
529 
530 struct nbbuf_struct
531 {
532     buf_T		*bufp;
533     unsigned int	 fireChanges:1;
534     unsigned int	 initDone:1;
535     unsigned int	 insertDone:1;
536     unsigned int	 modified:1;
537     int			 nbbuf_number;
538     char		*displayname;
539     int			*signmap;
540     short_u		 signmaplen;
541     short_u		 signmapused;
542 };
543 
544 typedef struct nbbuf_struct nbbuf_T;
545 
546 static nbbuf_T *buf_list = NULL;
547 static int buf_list_size = 0;	/* size of buf_list */
548 static int buf_list_used = 0;	/* nr of entries in buf_list actually in use */
549 
550 static char **globalsignmap = NULL;
551 static int globalsignmaplen = 0;
552 static int globalsignmapused = 0;
553 
554 static int  mapsigntype(nbbuf_T *, int localsigntype);
555 static void addsigntype(nbbuf_T *, int localsigntype, char_u *typeName,
556 			char_u *tooltip, char_u *glyphfile,
557 			char_u *fg, char_u *bg);
558 static void print_read_msg(nbbuf_T *buf);
559 static void print_save_msg(nbbuf_T *buf, off_t nchars);
560 
561 static int curPCtype = -1;
562 
563 /*
564  * Free netbeans resources.
565  */
566     static void
567 nb_free(void)
568 {
569     keyQ_T *key_node = keyHead.next;
570     nbbuf_T buf;
571     int i;
572 
573     /* free the netbeans buffer list */
574     for (i = 0; i < buf_list_used; i++)
575     {
576 	buf = buf_list[i];
577 	vim_free(buf.displayname);
578 	vim_free(buf.signmap);
579 	if (buf.bufp != NULL)
580 	{
581 	    buf.bufp->b_netbeans_file = FALSE;
582 	    buf.bufp->b_was_netbeans_file = FALSE;
583 	}
584     }
585     vim_free(buf_list);
586     buf_list = NULL;
587     buf_list_size = 0;
588     buf_list_used = 0;
589 
590     /* free the queued key commands */
591     while (key_node != NULL && key_node != &keyHead)
592     {
593 	keyQ_T *next = key_node->next;
594 	vim_free(key_node->keystr);
595 	vim_free(key_node);
596 	if (next == &keyHead)
597 	{
598 	    keyHead.next = &keyHead;
599 	    keyHead.prev = &keyHead;
600 	    break;
601 	}
602 	key_node = next;
603     }
604 
605     /* free the queued netbeans commands */
606     if (nb_channel != NULL)
607 	channel_clear(nb_channel);
608 }
609 
610 /*
611  * Get the Netbeans buffer number for the specified buffer.
612  */
613     static int
614 nb_getbufno(buf_T *bufp)
615 {
616     int i;
617 
618     for (i = 0; i < buf_list_used; i++)
619 	if (buf_list[i].bufp == bufp)
620 	    return i;
621     return -1;
622 }
623 
624 /*
625  * Is this a NetBeans-owned buffer?
626  */
627     int
628 isNetbeansBuffer(buf_T *bufp)
629 {
630     return NETBEANS_OPEN && bufp->b_netbeans_file;
631 }
632 
633 /*
634  * NetBeans and Vim have different undo models. In Vim, the file isn't
635  * changed if changes are undone via the undo command. In NetBeans, once
636  * a change has been made the file is marked as modified until saved. It
637  * doesn't matter if the change was undone.
638  *
639  * So this function is for the corner case where Vim thinks a buffer is
640  * unmodified but NetBeans thinks it IS modified.
641  */
642     int
643 isNetbeansModified(buf_T *bufp)
644 {
645     if (isNetbeansBuffer(bufp))
646     {
647 	int bufno = nb_getbufno(bufp);
648 
649 	if (bufno > 0)
650 	    return buf_list[bufno].modified;
651 	else
652 	    return FALSE;
653     }
654     else
655 	return FALSE;
656 }
657 
658 /*
659  * Given a Netbeans buffer number, return the netbeans buffer.
660  * Returns NULL for 0 or a negative number. A 0 bufno means a
661  * non-buffer related command has been sent.
662  */
663     static nbbuf_T *
664 nb_get_buf(int bufno)
665 {
666     /* find or create a buffer with the given number */
667     int incr;
668 
669     if (bufno <= 0)
670 	return NULL;
671 
672     if (!buf_list)
673     {
674 	/* initialize */
675 	buf_list = (nbbuf_T *)alloc_clear(100 * sizeof(nbbuf_T));
676 	buf_list_size = 100;
677     }
678     if (bufno >= buf_list_used) /* new */
679     {
680 	if (bufno >= buf_list_size) /* grow list */
681 	{
682 	    nbbuf_T *t_buf_list = buf_list;
683 
684 	    incr = bufno - buf_list_size + 90;
685 	    buf_list_size += incr;
686 	    buf_list = (nbbuf_T *)vim_realloc(
687 				   buf_list, buf_list_size * sizeof(nbbuf_T));
688 	    if (buf_list == NULL)
689 	    {
690 		vim_free(t_buf_list);
691 		buf_list_size = 0;
692 		return NULL;
693 	    }
694 	    vim_memset(buf_list + buf_list_size - incr, 0,
695 						      incr * sizeof(nbbuf_T));
696 	}
697 
698 	while (buf_list_used <= bufno)
699 	{
700 	    /* Default is to fire text changes. */
701 	    buf_list[buf_list_used].fireChanges = 1;
702 	    ++buf_list_used;
703 	}
704     }
705 
706     return buf_list + bufno;
707 }
708 
709 /*
710  * Return the number of buffers that are modified.
711  */
712     static int
713 count_changed_buffers(void)
714 {
715     buf_T	*bufp;
716     int		n;
717 
718     n = 0;
719     for (bufp = firstbuf; bufp != NULL; bufp = bufp->b_next)
720 	if (bufp->b_changed)
721 	    ++n;
722     return n;
723 }
724 
725 /*
726  * End the netbeans session.
727  */
728     void
729 netbeans_end(void)
730 {
731     int i;
732     static char buf[128];
733 
734     if (!NETBEANS_OPEN)
735 	return;
736 
737     for (i = 0; i < buf_list_used; i++)
738     {
739 	if (!buf_list[i].bufp)
740 	    continue;
741 	if (netbeansForcedQuit)
742 	{
743 	    /* mark as unmodified so NetBeans won't put up dialog on "killed" */
744 	    sprintf(buf, "%d:unmodified=%d\n", i, r_cmdno);
745 	    nbdebug(("EVT: %s", buf));
746 	    nb_send(buf, "netbeans_end");
747 	}
748 	sprintf(buf, "%d:killed=%d\n", i, r_cmdno);
749 	nbdebug(("EVT: %s", buf));
750 	/* nb_send(buf, "netbeans_end");    avoid "write failed" messages */
751 	nb_send(buf, NULL);
752     }
753 }
754 
755 /*
756  * Send a message to netbeans.
757  * When "fun" is NULL no error is given.
758  */
759     static void
760 nb_send(char *buf, char *fun)
761 {
762     if (nb_channel != NULL)
763 	channel_send(nb_channel, PART_SOCK, (char_u *)buf, fun);
764 }
765 
766 /*
767  * Some input received from netbeans requires a response. This function
768  * handles a response with no information (except the command number).
769  */
770     static void
771 nb_reply_nil(int cmdno)
772 {
773     char reply[32];
774 
775     nbdebug(("REP %d: <none>\n", cmdno));
776 
777     /* Avoid printing an annoying error message. */
778     if (!NETBEANS_OPEN)
779 	return;
780 
781     sprintf(reply, "%d\n", cmdno);
782     nb_send(reply, "nb_reply_nil");
783 }
784 
785 
786 /*
787  * Send a response with text.
788  * "result" must have been quoted already (using nb_quote()).
789  */
790     static void
791 nb_reply_text(int cmdno, char_u *result)
792 {
793     char_u *reply;
794 
795     nbdebug(("REP %d: %s\n", cmdno, (char *)result));
796 
797     reply = alloc((unsigned)STRLEN(result) + 32);
798     sprintf((char *)reply, "%d %s\n", cmdno, (char *)result);
799     nb_send((char *)reply, "nb_reply_text");
800 
801     vim_free(reply);
802 }
803 
804 
805 /*
806  * Send a response with a number result code.
807  */
808     static void
809 nb_reply_nr(int cmdno, long result)
810 {
811     char reply[32];
812 
813     nbdebug(("REP %d: %ld\n", cmdno, result));
814 
815     sprintf(reply, "%d %ld\n", cmdno, result);
816     nb_send(reply, "nb_reply_nr");
817 }
818 
819 
820 /*
821  * Encode newline, ret, backslash, double quote for transmission to NetBeans.
822  */
823     static char_u *
824 nb_quote(char_u *txt)
825 {
826     char_u *buf = alloc((unsigned)(2 * STRLEN(txt) + 1));
827     char_u *p = txt;
828     char_u *q = buf;
829 
830     if (buf == NULL)
831 	return NULL;
832     for (; *p; p++)
833     {
834 	switch (*p)
835 	{
836 	    case '\"':
837 	    case '\\':
838 		*q++ = '\\'; *q++ = *p; break;
839 	 /* case '\t': */
840 	 /*     *q++ = '\\'; *q++ = 't'; break; */
841 	    case '\n':
842 		*q++ = '\\'; *q++ = 'n'; break;
843 	    case '\r':
844 		*q++ = '\\'; *q++ = 'r'; break;
845 	    default:
846 		*q++ = *p;
847 		break;
848 	}
849     }
850     *q = '\0';
851 
852     return buf;
853 }
854 
855 
856 /*
857  * Remove top level double quotes; convert backslashed chars.
858  * Returns an allocated string (NULL for failure).
859  * If "endp" is not NULL it is set to the character after the terminating
860  * quote.
861  */
862     static char *
863 nb_unquote(char_u *p, char_u **endp)
864 {
865     char *result = 0;
866     char *q;
867     int done = 0;
868 
869     /* result is never longer than input */
870     result = (char *)alloc_clear((unsigned)STRLEN(p) + 1);
871     if (result == NULL)
872 	return NULL;
873 
874     if (*p++ != '"')
875     {
876 	nbdebug(("nb_unquote called with string that doesn't start with a quote!: %s\n",
877 			p));
878 	result[0] = NUL;
879 	return result;
880     }
881 
882     for (q = result; !done && *p != NUL;)
883     {
884 	switch (*p)
885 	{
886 	    case '"':
887 		/*
888 		 * Unbackslashed dquote marks the end, if first char was dquote.
889 		 */
890 		done = 1;
891 		break;
892 
893 	    case '\\':
894 		++p;
895 		switch (*p)
896 		{
897 		    case '\\':	*q++ = '\\';	break;
898 		    case 'n':	*q++ = '\n';	break;
899 		    case 't':	*q++ = '\t';	break;
900 		    case 'r':	*q++ = '\r';	break;
901 		    case '"':	*q++ = '"';	break;
902 		    case NUL:	--p;		break;
903 		    /* default: skip over illegal chars */
904 		}
905 		++p;
906 		break;
907 
908 	    default:
909 		*q++ = *p++;
910 	}
911     }
912 
913     if (endp != NULL)
914 	*endp = p;
915 
916     return result;
917 }
918 
919 /*
920  * Remove from "first" byte to "last" byte (inclusive), at line "lnum" of the
921  * current buffer.  Remove to end of line when "last" is MAXCOL.
922  */
923     static void
924 nb_partialremove(linenr_T lnum, colnr_T first, colnr_T last)
925 {
926     char_u *oldtext, *newtext;
927     int oldlen;
928     int lastbyte = last;
929 
930     oldtext = ml_get(lnum);
931     oldlen = (int)STRLEN(oldtext);
932     if (first >= (colnr_T)oldlen || oldlen == 0)  /* just in case */
933 	return;
934     if (lastbyte >= oldlen)
935 	lastbyte = oldlen - 1;
936     newtext = alloc(oldlen - (int)(lastbyte - first));
937     if (newtext != NULL)
938     {
939 	mch_memmove(newtext, oldtext, first);
940 	STRMOVE(newtext + first, oldtext + lastbyte + 1);
941 	nbdebug(("    NEW LINE %d: %s\n", lnum, newtext));
942 	ml_replace(lnum, newtext, FALSE);
943     }
944 }
945 
946 /*
947  * Replace the "first" line with the concatenation of the "first" and
948  * the "other" line. The "other" line is not removed.
949  */
950     static void
951 nb_joinlines(linenr_T first, linenr_T other)
952 {
953     int len_first, len_other;
954     char_u *p;
955 
956     len_first = (int)STRLEN(ml_get(first));
957     len_other = (int)STRLEN(ml_get(other));
958     p = alloc((unsigned)(len_first + len_other + 1));
959     if (p != NULL)
960     {
961       mch_memmove(p, ml_get(first), len_first);
962       mch_memmove(p + len_first, ml_get(other), len_other + 1);
963       ml_replace(first, p, FALSE);
964     }
965 }
966 
967 #define SKIP_STOP 2
968 #define streq(a,b) (strcmp(a,b) == 0)
969 
970 /*
971  * Do the actual processing of a single netbeans command or function.
972  * The difference between a command and function is that a function
973  * gets a response (it's required) but a command does not.
974  * For arguments see comment for nb_parse_cmd().
975  */
976     static int
977 nb_do_cmd(
978     int		bufno,
979     char_u	*cmd,
980     int		func,
981     int		cmdno,
982     char_u	*args)	    /* points to space before arguments or NUL */
983 {
984     int		do_update = 0;
985     long	off = 0;
986     nbbuf_T	*buf = nb_get_buf(bufno);
987     static int	skip = 0;
988     int		retval = OK;
989     char	*cp;	    /* for when a char pointer is needed */
990 
991     nbdebug(("%s %d: (%d) %s %s\n", (func) ? "FUN" : "CMD", cmdno, bufno, cmd,
992 	STRCMP(cmd, "insert") == 0 ? "<text>" : (char *)args));
993 
994     if (func)
995     {
996 /* =====================================================================*/
997 	if (streq((char *)cmd, "getModified"))
998 	{
999 	    if (buf == NULL || buf->bufp == NULL)
1000 		/* Return the number of buffers that are modified. */
1001 		nb_reply_nr(cmdno, (long)count_changed_buffers());
1002 	    else
1003 		/* Return whether the buffer is modified. */
1004 		nb_reply_nr(cmdno, (long)(buf->bufp->b_changed
1005 					   || isNetbeansModified(buf->bufp)));
1006 /* =====================================================================*/
1007 	}
1008 	else if (streq((char *)cmd, "saveAndExit"))
1009 	{
1010 	    /* Note: this will exit Vim if successful. */
1011 	    coloncmd(":confirm qall");
1012 
1013 	    /* We didn't exit: return the number of changed buffers. */
1014 	    nb_reply_nr(cmdno, (long)count_changed_buffers());
1015 /* =====================================================================*/
1016 	}
1017 	else if (streq((char *)cmd, "getCursor"))
1018 	{
1019 	    char_u text[200];
1020 
1021 	    /* Note: nb_getbufno() may return -1.  This indicates the IDE
1022 	     * didn't assign a number to the current buffer in response to a
1023 	     * fileOpened event. */
1024 	    sprintf((char *)text, "%d %ld %d %ld",
1025 		    nb_getbufno(curbuf),
1026 		    (long)curwin->w_cursor.lnum,
1027 		    (int)curwin->w_cursor.col,
1028 		    pos2off(curbuf, &curwin->w_cursor));
1029 	    nb_reply_text(cmdno, text);
1030 /* =====================================================================*/
1031 	}
1032 	else if (streq((char *)cmd, "getAnno"))
1033 	{
1034 	    long linenum = 0;
1035 #ifdef FEAT_SIGNS
1036 	    if (buf == NULL || buf->bufp == NULL)
1037 	    {
1038 		nbdebug(("    Invalid buffer identifier in getAnno\n"));
1039 		EMSG("E652: Invalid buffer identifier in getAnno");
1040 		retval = FAIL;
1041 	    }
1042 	    else
1043 	    {
1044 		int serNum;
1045 
1046 		cp = (char *)args;
1047 		serNum = strtol(cp, &cp, 10);
1048 		/* If the sign isn't found linenum will be zero. */
1049 		linenum = (long)buf_findsign(buf->bufp, serNum);
1050 	    }
1051 #endif
1052 	    nb_reply_nr(cmdno, linenum);
1053 /* =====================================================================*/
1054 	}
1055 	else if (streq((char *)cmd, "getLength"))
1056 	{
1057 	    long len = 0;
1058 
1059 	    if (buf == NULL || buf->bufp == NULL)
1060 	    {
1061 		nbdebug(("    invalid buffer identifier in getLength\n"));
1062 		EMSG("E632: invalid buffer identifier in getLength");
1063 		retval = FAIL;
1064 	    }
1065 	    else
1066 	    {
1067 		len = get_buf_size(buf->bufp);
1068 	    }
1069 	    nb_reply_nr(cmdno, len);
1070 /* =====================================================================*/
1071 	}
1072 	else if (streq((char *)cmd, "getText"))
1073 	{
1074 	    long	len;
1075 	    linenr_T	nlines;
1076 	    char_u	*text = NULL;
1077 	    linenr_T	lno = 1;
1078 	    char_u	*p;
1079 	    char_u	*line;
1080 
1081 	    if (buf == NULL || buf->bufp == NULL)
1082 	    {
1083 		nbdebug(("    invalid buffer identifier in getText\n"));
1084 		EMSG("E633: invalid buffer identifier in getText");
1085 		retval = FAIL;
1086 	    }
1087 	    else
1088 	    {
1089 		len = get_buf_size(buf->bufp);
1090 		nlines = buf->bufp->b_ml.ml_line_count;
1091 		text = alloc((unsigned)((len > 0)
1092 						 ? ((len + nlines) * 2) : 4));
1093 		if (text == NULL)
1094 		{
1095 		    nbdebug(("    nb_do_cmd: getText has null text field\n"));
1096 		    retval = FAIL;
1097 		}
1098 		else
1099 		{
1100 		    p = text;
1101 		    *p++ = '\"';
1102 		    for (; lno <= nlines ; lno++)
1103 		    {
1104 			line = nb_quote(ml_get_buf(buf->bufp, lno, FALSE));
1105 			if (line != NULL)
1106 			{
1107 			    STRCPY(p, line);
1108 			    p += STRLEN(line);
1109 			    *p++ = '\\';
1110 			    *p++ = 'n';
1111 			    vim_free(line);
1112 			}
1113 		    }
1114 		    *p++ = '\"';
1115 		    *p = '\0';
1116 		}
1117 	    }
1118 	    if (text == NULL)
1119 		nb_reply_text(cmdno, (char_u *)"");
1120 	    else
1121 	    {
1122 		nb_reply_text(cmdno, text);
1123 		vim_free(text);
1124 	    }
1125 /* =====================================================================*/
1126 	}
1127 	else if (streq((char *)cmd, "remove"))
1128 	{
1129 	    long count;
1130 	    pos_T first, last;
1131 	    pos_T *pos;
1132 	    pos_T *next;
1133 	    linenr_T del_from_lnum, del_to_lnum;  /* lines to be deleted as a whole */
1134 	    int oldFire = netbeansFireChanges;
1135 	    int oldSuppress = netbeansSuppressNoLines;
1136 	    int wasChanged;
1137 
1138 	    if (skip >= SKIP_STOP)
1139 	    {
1140 		nbdebug(("    Skipping %s command\n", (char *) cmd));
1141 		nb_reply_nil(cmdno);
1142 		return OK;
1143 	    }
1144 
1145 	    if (buf == NULL || buf->bufp == NULL)
1146 	    {
1147 		nbdebug(("    invalid buffer identifier in remove\n"));
1148 		EMSG("E634: invalid buffer identifier in remove");
1149 		retval = FAIL;
1150 	    }
1151 	    else
1152 	    {
1153 		netbeansFireChanges = FALSE;
1154 		netbeansSuppressNoLines = TRUE;
1155 
1156 		nb_set_curbuf(buf->bufp);
1157 		wasChanged = buf->bufp->b_changed;
1158 		cp = (char *)args;
1159 		off = strtol(cp, &cp, 10);
1160 		count = strtol(cp, &cp, 10);
1161 		args = (char_u *)cp;
1162 		/* delete "count" chars, starting at "off" */
1163 		pos = off2pos(buf->bufp, off);
1164 		if (!pos)
1165 		{
1166 		    nbdebug(("    !bad position\n"));
1167 		    nb_reply_text(cmdno, (char_u *)"!bad position");
1168 		    netbeansFireChanges = oldFire;
1169 		    netbeansSuppressNoLines = oldSuppress;
1170 		    return FAIL;
1171 		}
1172 		first = *pos;
1173 		nbdebug(("    FIRST POS: line %d, col %d\n",
1174 						      first.lnum, first.col));
1175 		pos = off2pos(buf->bufp, off+count-1);
1176 		if (!pos)
1177 		{
1178 		    nbdebug(("    !bad count\n"));
1179 		    nb_reply_text(cmdno, (char_u *)"!bad count");
1180 		    netbeansFireChanges = oldFire;
1181 		    netbeansSuppressNoLines = oldSuppress;
1182 		    return FAIL;
1183 		}
1184 		last = *pos;
1185 		nbdebug(("    LAST POS: line %d, col %d\n",
1186 							last.lnum, last.col));
1187 		del_from_lnum = first.lnum;
1188 		del_to_lnum = last.lnum;
1189 		do_update = 1;
1190 
1191 		/* Get the position of the first byte after the deleted
1192 		 * section.  "next" is NULL when deleting to the end of the
1193 		 * file. */
1194 		next = off2pos(buf->bufp, off + count);
1195 
1196 		/* Remove part of the first line. */
1197 		if (first.col != 0
1198 				|| (next != NULL && first.lnum == next->lnum))
1199 		{
1200 		    if (first.lnum != last.lnum
1201 			    || (next != NULL && first.lnum != next->lnum))
1202 		    {
1203 			/* remove to the end of the first line */
1204 			nb_partialremove(first.lnum, first.col,
1205 							     (colnr_T)MAXCOL);
1206 			if (first.lnum == last.lnum)
1207 			{
1208 			    /* Partial line to remove includes the end of
1209 			     * line.  Join the line with the next one, have
1210 			     * the next line deleted below. */
1211 			    nb_joinlines(first.lnum, next->lnum);
1212 			    del_to_lnum = next->lnum;
1213 			}
1214 		    }
1215 		    else
1216 		    {
1217 			/* remove within one line */
1218 			nb_partialremove(first.lnum, first.col, last.col);
1219 		    }
1220 		    ++del_from_lnum;  /* don't delete the first line */
1221 		}
1222 
1223 		/* Remove part of the last line. */
1224 		if (first.lnum != last.lnum && next != NULL
1225 			&& next->col != 0 && last.lnum == next->lnum)
1226 		{
1227 		    nb_partialremove(last.lnum, 0, last.col);
1228 		    if (del_from_lnum > first.lnum)
1229 		    {
1230 			/* Join end of last line to start of first line; last
1231 			 * line is deleted below. */
1232 			nb_joinlines(first.lnum, last.lnum);
1233 		    }
1234 		    else
1235 			/* First line is deleted as a whole, keep the last
1236 			 * line. */
1237 			--del_to_lnum;
1238 		}
1239 
1240 		/* First is partial line; last line to remove includes
1241 		 * the end of line; join first line to line following last
1242 		 * line; line following last line is deleted below. */
1243 		if (first.lnum != last.lnum && del_from_lnum > first.lnum
1244 			&& next != NULL && last.lnum != next->lnum)
1245 		{
1246 		    nb_joinlines(first.lnum, next->lnum);
1247 		    del_to_lnum = next->lnum;
1248 		}
1249 
1250 		/* Delete whole lines if there are any. */
1251 		if (del_to_lnum >= del_from_lnum)
1252 		{
1253 		    int i;
1254 
1255 		    /* delete signs from the lines being deleted */
1256 		    for (i = del_from_lnum; i <= del_to_lnum; i++)
1257 		    {
1258 			int id = buf_findsign_id(buf->bufp, (linenr_T)i);
1259 			if (id > 0)
1260 			{
1261 			    nbdebug(("    Deleting sign %d on line %d\n",
1262 								      id, i));
1263 			    buf_delsign(buf->bufp, id);
1264 			}
1265 			else
1266 			{
1267 			    nbdebug(("    No sign on line %d\n", i));
1268 			}
1269 		    }
1270 
1271 		    nbdebug(("    Deleting lines %d through %d\n",
1272 						 del_from_lnum, del_to_lnum));
1273 		    curwin->w_cursor.lnum = del_from_lnum;
1274 		    curwin->w_cursor.col = 0;
1275 		    del_lines(del_to_lnum - del_from_lnum + 1, FALSE);
1276 		}
1277 
1278 		/* Leave cursor at first deleted byte. */
1279 		curwin->w_cursor = first;
1280 		check_cursor_lnum();
1281 		buf->bufp->b_changed = wasChanged; /* logically unchanged */
1282 		netbeansFireChanges = oldFire;
1283 		netbeansSuppressNoLines = oldSuppress;
1284 
1285 		u_blockfree(buf->bufp);
1286 		u_clearall(buf->bufp);
1287 	    }
1288 	    nb_reply_nil(cmdno);
1289 /* =====================================================================*/
1290 	}
1291 	else if (streq((char *)cmd, "insert"))
1292 	{
1293 	    char_u	*to_free;
1294 
1295 	    if (skip >= SKIP_STOP)
1296 	    {
1297 		nbdebug(("    Skipping %s command\n", (char *) cmd));
1298 		nb_reply_nil(cmdno);
1299 		return OK;
1300 	    }
1301 
1302 	    /* get offset */
1303 	    cp = (char *)args;
1304 	    off = strtol(cp, &cp, 10);
1305 	    args = (char_u *)cp;
1306 
1307 	    /* get text to be inserted */
1308 	    args = skipwhite(args);
1309 	    args = to_free = (char_u *)nb_unquote(args, NULL);
1310 	    /*
1311 	    nbdebug(("    CHUNK[%d]: %d bytes at offset %d\n",
1312 		    buf->bufp->b_ml.ml_line_count, STRLEN(args), off));
1313 	    */
1314 
1315 	    if (buf == NULL || buf->bufp == NULL)
1316 	    {
1317 		nbdebug(("    invalid buffer identifier in insert\n"));
1318 		EMSG("E635: invalid buffer identifier in insert");
1319 		retval = FAIL;
1320 	    }
1321 	    else if (args != NULL)
1322 	    {
1323 		int	ff_detected = EOL_UNKNOWN;
1324 		int	buf_was_empty = (buf->bufp->b_ml.ml_flags & ML_EMPTY);
1325 		size_t	len = 0;
1326 		int	added = 0;
1327 		int	oldFire = netbeansFireChanges;
1328 		int	old_b_changed;
1329 		char_u	*nlp;
1330 		linenr_T lnum;
1331 		linenr_T lnum_start;
1332 		pos_T	*pos;
1333 
1334 		netbeansFireChanges = 0;
1335 
1336 		/* Jump to the buffer where we insert.  After this "curbuf"
1337 		 * can be used. */
1338 		nb_set_curbuf(buf->bufp);
1339 		old_b_changed = curbuf->b_changed;
1340 
1341 		/* Convert the specified character offset into a lnum/col
1342 		 * position. */
1343 		pos = off2pos(curbuf, off);
1344 		if (pos != NULL)
1345 		{
1346 		    if (pos->lnum <= 0)
1347 			lnum_start = 1;
1348 		    else
1349 			lnum_start = pos->lnum;
1350 		}
1351 		else
1352 		{
1353 		    /* If the given position is not found, assume we want
1354 		     * the end of the file.  See setLocAndSize HACK. */
1355 		    if (buf_was_empty)
1356 			lnum_start = 1;	    /* above empty line */
1357 		    else
1358 			lnum_start = curbuf->b_ml.ml_line_count + 1;
1359 		}
1360 
1361 		/* "lnum" is the line where we insert: either append to it or
1362 		 * insert a new line above it. */
1363 		lnum = lnum_start;
1364 
1365 		/* Loop over the "\n" separated lines of the argument. */
1366 		do_update = 1;
1367 		while (*args != NUL)
1368 		{
1369 		    nlp = vim_strchr(args, '\n');
1370 		    if (nlp == NULL)
1371 		    {
1372 			/* Incomplete line, probably truncated.  Next "insert"
1373 			 * command should append to this one. */
1374 			len = STRLEN(args);
1375 		    }
1376 		    else
1377 		    {
1378 			len = nlp - args;
1379 
1380 			/*
1381 			 * We need to detect EOL style, because the commands
1382 			 * use a character offset.
1383 			 */
1384 			if (nlp > args && nlp[-1] == '\r')
1385 			{
1386 			    ff_detected = EOL_DOS;
1387 			    --len;
1388 			}
1389 			else
1390 			    ff_detected = EOL_UNIX;
1391 		    }
1392 		    args[len] = NUL;
1393 
1394 		    if (lnum == lnum_start
1395 			    && ((pos != NULL && pos->col > 0)
1396 				|| (lnum == 1 && buf_was_empty)))
1397 		    {
1398 			char_u *oldline = ml_get(lnum);
1399 			char_u *newline;
1400 
1401 			/* Insert halfway a line. */
1402 			newline = alloc_check(
1403 				       (unsigned)(STRLEN(oldline) + len + 1));
1404 			if (newline != NULL)
1405 			{
1406 			    mch_memmove(newline, oldline, (size_t)pos->col);
1407 			    newline[pos->col] = NUL;
1408 			    STRCAT(newline, args);
1409 			    STRCAT(newline, oldline + pos->col);
1410 			    ml_replace(lnum, newline, FALSE);
1411 			}
1412 		    }
1413 		    else
1414 		    {
1415 			/* Append a new line.  Not that we always do this,
1416 			 * also when the text doesn't end in a "\n". */
1417 			ml_append((linenr_T)(lnum - 1), args,
1418 						   (colnr_T)(len + 1), FALSE);
1419 			++added;
1420 		    }
1421 
1422 		    if (nlp == NULL)
1423 			break;
1424 		    ++lnum;
1425 		    args = nlp + 1;
1426 		}
1427 
1428 		/* Adjust the marks below the inserted lines. */
1429 		appended_lines_mark(lnum_start - 1, (long)added);
1430 
1431 		/*
1432 		 * When starting with an empty buffer set the fileformat.
1433 		 * This is just guessing...
1434 		 */
1435 		if (buf_was_empty)
1436 		{
1437 		    if (ff_detected == EOL_UNKNOWN)
1438 #if defined(MSWIN)
1439 			ff_detected = EOL_DOS;
1440 #else
1441 			ff_detected = EOL_UNIX;
1442 #endif
1443 		    set_fileformat(ff_detected, OPT_LOCAL);
1444 		    curbuf->b_start_ffc = *curbuf->b_p_ff;
1445 		}
1446 
1447 		/*
1448 		 * XXX - GRP - Is the next line right? If I've inserted
1449 		 * text the buffer has been updated but not written. Will
1450 		 * netbeans guarantee to write it? Even if I do a :q! ?
1451 		 */
1452 		curbuf->b_changed = old_b_changed; /* logically unchanged */
1453 		netbeansFireChanges = oldFire;
1454 
1455 		/* Undo info is invalid now... */
1456 		u_blockfree(curbuf);
1457 		u_clearall(curbuf);
1458 	    }
1459 	    vim_free(to_free);
1460 	    nb_reply_nil(cmdno); /* or !error */
1461 	}
1462 	else
1463 	{
1464 	    nbdebug(("UNIMPLEMENTED FUNCTION: %s\n", cmd));
1465 	    nb_reply_nil(cmdno);
1466 	    retval = FAIL;
1467 	}
1468     }
1469     else /* Not a function; no reply required. */
1470     {
1471 /* =====================================================================*/
1472 	if (streq((char *)cmd, "create"))
1473 	{
1474 	    /* Create a buffer without a name. */
1475 	    if (buf == NULL)
1476 	    {
1477 		nbdebug(("    invalid buffer identifier in create\n"));
1478 		EMSG("E636: invalid buffer identifier in create");
1479 		return FAIL;
1480 	    }
1481 	    vim_free(buf->displayname);
1482 	    buf->displayname = NULL;
1483 
1484 	    netbeansReadFile = 0; /* don't try to open disk file */
1485 	    do_ecmd(0, NULL, 0, 0, ECMD_ONE, ECMD_HIDE + ECMD_OLDBUF, curwin);
1486 	    netbeansReadFile = 1;
1487 	    buf->bufp = curbuf;
1488 	    maketitle();
1489 	    buf->insertDone = FALSE;
1490 #if defined(FEAT_MENU) && defined(FEAT_GUI)
1491 	    if (gui.in_use)
1492 		gui_update_menus(0);
1493 #endif
1494 /* =====================================================================*/
1495 	}
1496 	else if (streq((char *)cmd, "insertDone"))
1497 	{
1498 	    if (buf == NULL || buf->bufp == NULL)
1499 	    {
1500 		nbdebug(("    invalid buffer identifier in insertDone\n"));
1501 	    }
1502 	    else
1503 	    {
1504 		buf->bufp->b_start_eol = *args == 'T';
1505 		buf->insertDone = TRUE;
1506 		args += 2;
1507 		buf->bufp->b_p_ro = *args == 'T';
1508 		print_read_msg(buf);
1509 	    }
1510 /* =====================================================================*/
1511 	}
1512 	else if (streq((char *)cmd, "saveDone"))
1513 	{
1514 	    long savedChars = atol((char *)args);
1515 
1516 	    if (buf == NULL || buf->bufp == NULL)
1517 	    {
1518 		nbdebug(("    invalid buffer identifier in saveDone\n"));
1519 	    }
1520 	    else
1521 		print_save_msg(buf, savedChars);
1522 /* =====================================================================*/
1523 	}
1524 	else if (streq((char *)cmd, "startDocumentListen"))
1525 	{
1526 	    if (buf == NULL)
1527 	    {
1528 		nbdebug(("    invalid buffer identifier in startDocumentListen\n"));
1529 		EMSG("E637: invalid buffer identifier in startDocumentListen");
1530 		return FAIL;
1531 	    }
1532 	    buf->fireChanges = 1;
1533 /* =====================================================================*/
1534 	}
1535 	else if (streq((char *)cmd, "stopDocumentListen"))
1536 	{
1537 	    if (buf == NULL)
1538 	    {
1539 		nbdebug(("    invalid buffer identifier in stopDocumentListen\n"));
1540 		EMSG("E638: invalid buffer identifier in stopDocumentListen");
1541 		return FAIL;
1542 	    }
1543 	    buf->fireChanges = 0;
1544 	    if (buf->bufp != NULL && buf->bufp->b_was_netbeans_file)
1545 	    {
1546 		if (!buf->bufp->b_netbeans_file)
1547 		{
1548 		    nbdebug(("E658: NetBeans connection lost for buffer %ld\n", buf->bufp->b_fnum));
1549 		    EMSGN(_("E658: NetBeans connection lost for buffer %ld"),
1550 							   buf->bufp->b_fnum);
1551 		}
1552 		else
1553 		{
1554 		    /* NetBeans uses stopDocumentListen when it stops editing
1555 		     * a file.  It then expects the buffer in Vim to
1556 		     * disappear. */
1557 		    do_bufdel(DOBUF_DEL, (char_u *)"", 1,
1558 				  buf->bufp->b_fnum, buf->bufp->b_fnum, TRUE);
1559 		    vim_memset(buf, 0, sizeof(nbbuf_T));
1560 		}
1561 	    }
1562 /* =====================================================================*/
1563 	}
1564 	else if (streq((char *)cmd, "setTitle"))
1565 	{
1566 	    if (buf == NULL)
1567 	    {
1568 		nbdebug(("    invalid buffer identifier in setTitle\n"));
1569 		EMSG("E639: invalid buffer identifier in setTitle");
1570 		return FAIL;
1571 	    }
1572 	    vim_free(buf->displayname);
1573 	    buf->displayname = nb_unquote(args, NULL);
1574 /* =====================================================================*/
1575 	}
1576 	else if (streq((char *)cmd, "initDone"))
1577 	{
1578 	    if (buf == NULL || buf->bufp == NULL)
1579 	    {
1580 		nbdebug(("    invalid buffer identifier in initDone\n"));
1581 		EMSG("E640: invalid buffer identifier in initDone");
1582 		return FAIL;
1583 	    }
1584 	    do_update = 1;
1585 	    buf->initDone = TRUE;
1586 	    nb_set_curbuf(buf->bufp);
1587 #if defined(FEAT_AUTOCMD)
1588 	    apply_autocmds(EVENT_BUFREADPOST, 0, 0, FALSE, buf->bufp);
1589 #endif
1590 
1591 	    /* handle any postponed key commands */
1592 	    handle_key_queue();
1593 /* =====================================================================*/
1594 	}
1595 	else if (streq((char *)cmd, "setBufferNumber")
1596 		|| streq((char *)cmd, "putBufferNumber"))
1597 	{
1598 	    char_u	*path;
1599 	    buf_T	*bufp;
1600 
1601 	    if (buf == NULL)
1602 	    {
1603 		nbdebug(("    invalid buffer identifier in setBufferNumber\n"));
1604 		EMSG("E641: invalid buffer identifier in setBufferNumber");
1605 		return FAIL;
1606 	    }
1607 	    path = (char_u *)nb_unquote(args, NULL);
1608 	    if (path == NULL)
1609 		return FAIL;
1610 	    bufp = buflist_findname(path);
1611 	    vim_free(path);
1612 	    if (bufp == NULL)
1613 	    {
1614 		nbdebug(("    File %s not found in setBufferNumber\n", args));
1615 		EMSG2("E642: File %s not found in setBufferNumber", args);
1616 		return FAIL;
1617 	    }
1618 	    buf->bufp = bufp;
1619 	    buf->nbbuf_number = bufp->b_fnum;
1620 
1621 	    /* "setBufferNumber" has the side effect of jumping to the buffer
1622 	     * (don't know why!).  Don't do that for "putBufferNumber". */
1623 	    if (*cmd != 'p')
1624 		coloncmd(":buffer %d", bufp->b_fnum);
1625 	    else
1626 	    {
1627 		buf->initDone = TRUE;
1628 
1629 		/* handle any postponed key commands */
1630 		handle_key_queue();
1631 	    }
1632 
1633 /* =====================================================================*/
1634 	}
1635 	else if (streq((char *)cmd, "setFullName"))
1636 	{
1637 	    if (buf == NULL)
1638 	    {
1639 		nbdebug(("    invalid buffer identifier in setFullName\n"));
1640 		EMSG("E643: invalid buffer identifier in setFullName");
1641 		return FAIL;
1642 	    }
1643 	    vim_free(buf->displayname);
1644 	    buf->displayname = nb_unquote(args, NULL);
1645 
1646 	    netbeansReadFile = 0; /* don't try to open disk file */
1647 	    do_ecmd(0, (char_u *)buf->displayname, 0, 0, ECMD_ONE,
1648 					     ECMD_HIDE + ECMD_OLDBUF, curwin);
1649 	    netbeansReadFile = 1;
1650 	    buf->bufp = curbuf;
1651 	    maketitle();
1652 #if defined(FEAT_MENU) && defined(FEAT_GUI)
1653 	    if (gui.in_use)
1654 		gui_update_menus(0);
1655 #endif
1656 /* =====================================================================*/
1657 	}
1658 	else if (streq((char *)cmd, "editFile"))
1659 	{
1660 	    if (buf == NULL)
1661 	    {
1662 		nbdebug(("    invalid buffer identifier in editFile\n"));
1663 		EMSG("E644: invalid buffer identifier in editFile");
1664 		return FAIL;
1665 	    }
1666 	    /* Edit a file: like create + setFullName + read the file. */
1667 	    vim_free(buf->displayname);
1668 	    buf->displayname = nb_unquote(args, NULL);
1669 	    do_ecmd(0, (char_u *)buf->displayname, NULL, NULL, ECMD_ONE,
1670 					     ECMD_HIDE + ECMD_OLDBUF, curwin);
1671 	    buf->bufp = curbuf;
1672 	    buf->initDone = TRUE;
1673 	    do_update = 1;
1674 #if defined(FEAT_TITLE)
1675 	    maketitle();
1676 #endif
1677 #if defined(FEAT_MENU) && defined(FEAT_GUI)
1678 	    if (gui.in_use)
1679 		gui_update_menus(0);
1680 #endif
1681 /* =====================================================================*/
1682 	}
1683 	else if (streq((char *)cmd, "setVisible"))
1684 	{
1685 	    if (buf == NULL || buf->bufp == NULL)
1686 	    {
1687 		nbdebug(("    invalid buffer identifier in setVisible\n"));
1688 		/* This message was commented out, probably because it can
1689 		 * happen when shutting down. */
1690 		if (p_verbose > 0)
1691 		    EMSG("E645: invalid buffer identifier in setVisible");
1692 		return FAIL;
1693 	    }
1694 	    if (streq((char *)args, "T") && buf->bufp != curbuf)
1695 	    {
1696 		exarg_T exarg;
1697 		exarg.cmd = (char_u *)"goto";
1698 		exarg.forceit = FALSE;
1699 		dosetvisible = TRUE;
1700 		goto_buffer(&exarg, DOBUF_FIRST, FORWARD, buf->bufp->b_fnum);
1701 		do_update = 1;
1702 		dosetvisible = FALSE;
1703 
1704 #ifdef FEAT_GUI
1705 		/* Side effect!!!. */
1706 		if (gui.in_use)
1707 		    gui_mch_set_foreground();
1708 #endif
1709 	    }
1710 /* =====================================================================*/
1711 	}
1712 	else if (streq((char *)cmd, "raise"))
1713 	{
1714 #ifdef FEAT_GUI
1715 	    /* Bring gvim to the foreground. */
1716 	    if (gui.in_use)
1717 		gui_mch_set_foreground();
1718 #endif
1719 /* =====================================================================*/
1720 	}
1721 	else if (streq((char *)cmd, "setModified"))
1722 	{
1723 	    int prev_b_changed;
1724 
1725 	    if (buf == NULL || buf->bufp == NULL)
1726 	    {
1727 		nbdebug(("    invalid buffer identifier in setModified\n"));
1728 		/* This message was commented out, probably because it can
1729 		 * happen when shutting down. */
1730 		if (p_verbose > 0)
1731 		    EMSG("E646: invalid buffer identifier in setModified");
1732 		return FAIL;
1733 	    }
1734 	    prev_b_changed = buf->bufp->b_changed;
1735 	    if (streq((char *)args, "T"))
1736 		buf->bufp->b_changed = TRUE;
1737 	    else
1738 	    {
1739 		struct stat	st;
1740 
1741 		/* Assume NetBeans stored the file.  Reset the timestamp to
1742 		 * avoid "file changed" warnings. */
1743 		if (buf->bufp->b_ffname != NULL
1744 			&& mch_stat((char *)buf->bufp->b_ffname, &st) >= 0)
1745 		    buf_store_time(buf->bufp, &st, buf->bufp->b_ffname);
1746 		buf->bufp->b_changed = FALSE;
1747 	    }
1748 	    buf->modified = buf->bufp->b_changed;
1749 	    if (prev_b_changed != buf->bufp->b_changed)
1750 	    {
1751 #ifdef FEAT_WINDOWS
1752 		check_status(buf->bufp);
1753 		redraw_tabline = TRUE;
1754 #endif
1755 #ifdef FEAT_TITLE
1756 		maketitle();
1757 #endif
1758 		update_screen(0);
1759 	    }
1760 /* =====================================================================*/
1761 	}
1762 	else if (streq((char *)cmd, "setModtime"))
1763 	{
1764 	    if (buf == NULL || buf->bufp == NULL)
1765 		nbdebug(("    invalid buffer identifier in setModtime\n"));
1766 	    else
1767 		buf->bufp->b_mtime = atoi((char *)args);
1768 /* =====================================================================*/
1769 	}
1770 	else if (streq((char *)cmd, "setReadOnly"))
1771 	{
1772 	    if (buf == NULL || buf->bufp == NULL)
1773 		nbdebug(("    invalid buffer identifier in setReadOnly\n"));
1774 	    else if (streq((char *)args, "T"))
1775 		buf->bufp->b_p_ro = TRUE;
1776 	    else
1777 		buf->bufp->b_p_ro = FALSE;
1778 /* =====================================================================*/
1779 	}
1780 	else if (streq((char *)cmd, "setMark"))
1781 	{
1782 	    /* not yet */
1783 /* =====================================================================*/
1784 	}
1785 	else if (streq((char *)cmd, "showBalloon"))
1786 	{
1787 #if defined(FEAT_BEVAL)
1788 	    static char	*text = NULL;
1789 
1790 	    /*
1791 	     * Set up the Balloon Expression Evaluation area.
1792 	     * Ignore 'ballooneval' here.
1793 	     * The text pointer must remain valid for a while.
1794 	     */
1795 	    if (balloonEval != NULL)
1796 	    {
1797 		vim_free(text);
1798 		text = nb_unquote(args, NULL);
1799 		if (text != NULL)
1800 		    gui_mch_post_balloon(balloonEval, (char_u *)text);
1801 	    }
1802 #endif
1803 /* =====================================================================*/
1804 	}
1805 	else if (streq((char *)cmd, "setDot"))
1806 	{
1807 	    pos_T *pos;
1808 #ifdef NBDEBUG
1809 	    char_u *s;
1810 #endif
1811 
1812 	    if (buf == NULL || buf->bufp == NULL)
1813 	    {
1814 		nbdebug(("    invalid buffer identifier in setDot\n"));
1815 		EMSG("E647: invalid buffer identifier in setDot");
1816 		return FAIL;
1817 	    }
1818 
1819 	    nb_set_curbuf(buf->bufp);
1820 
1821 	    /* Don't want Visual mode now. */
1822 	    if (VIsual_active)
1823 		end_visual_mode();
1824 #ifdef NBDEBUG
1825 	    s = args;
1826 #endif
1827 	    pos = get_off_or_lnum(buf->bufp, &args);
1828 	    if (pos)
1829 	    {
1830 		curwin->w_cursor = *pos;
1831 		check_cursor();
1832 #ifdef FEAT_FOLDING
1833 		foldOpenCursor();
1834 #endif
1835 	    }
1836 	    else
1837 	    {
1838 		nbdebug(("    BAD POSITION in setDot: %s\n", s));
1839 	    }
1840 
1841 	    /* gui_update_cursor(TRUE, FALSE); */
1842 	    /* update_curbuf(NOT_VALID); */
1843 	    update_topline();		/* scroll to show the line */
1844 	    update_screen(VALID);
1845 	    setcursor();
1846 	    cursor_on();
1847 	    out_flush();
1848 #ifdef FEAT_GUI
1849 	    if (gui.in_use)
1850 	    {
1851 		gui_update_cursor(TRUE, FALSE);
1852 		gui_mch_flush();
1853 	    }
1854 #endif
1855 	    /* Quit a hit-return or more prompt. */
1856 	    if (State == HITRETURN || State == ASKMORE)
1857 	    {
1858 #ifdef FEAT_GUI_GTK
1859 		if (gui.in_use && gtk_main_level() > 0)
1860 		    gtk_main_quit();
1861 #endif
1862 	    }
1863 /* =====================================================================*/
1864 	}
1865 	else if (streq((char *)cmd, "close"))
1866 	{
1867 #ifdef NBDEBUG
1868 	    char *name = "<NONE>";
1869 #endif
1870 
1871 	    if (buf == NULL)
1872 	    {
1873 		nbdebug(("    invalid buffer identifier in close\n"));
1874 		EMSG("E648: invalid buffer identifier in close");
1875 		return FAIL;
1876 	    }
1877 
1878 #ifdef NBDEBUG
1879 	    if (buf->displayname != NULL)
1880 		name = buf->displayname;
1881 #endif
1882 	    if (buf->bufp == NULL)
1883 	    {
1884 		nbdebug(("    invalid buffer identifier in close\n"));
1885 		/* This message was commented out, probably because it can
1886 		 * happen when shutting down. */
1887 		if (p_verbose > 0)
1888 		    EMSG("E649: invalid buffer identifier in close");
1889 	    }
1890 	    nbdebug(("    CLOSE %d: %s\n", bufno, name));
1891 #ifdef FEAT_GUI
1892 	    need_mouse_correct = TRUE;
1893 #endif
1894 	    if (buf->bufp != NULL)
1895 		do_buffer(DOBUF_WIPE, DOBUF_FIRST, FORWARD,
1896 						     buf->bufp->b_fnum, TRUE);
1897 	    buf->bufp = NULL;
1898 	    buf->initDone = FALSE;
1899 	    do_update = 1;
1900 /* =====================================================================*/
1901 	}
1902 	else if (streq((char *)cmd, "setStyle")) /* obsolete... */
1903 	{
1904 	    nbdebug(("    setStyle is obsolete!\n"));
1905 /* =====================================================================*/
1906 	}
1907 	else if (streq((char *)cmd, "setExitDelay"))
1908 	{
1909 	    /* Only used in version 2.1. */
1910 /* =====================================================================*/
1911 	}
1912 	else if (streq((char *)cmd, "defineAnnoType"))
1913 	{
1914 #ifdef FEAT_SIGNS
1915 	    int typeNum;
1916 	    char_u *typeName;
1917 	    char_u *tooltip;
1918 	    char_u *p;
1919 	    char_u *glyphFile;
1920 	    int parse_error = FALSE;
1921 	    char_u *fg;
1922 	    char_u *bg;
1923 
1924 	    if (buf == NULL)
1925 	    {
1926 		nbdebug(("    invalid buffer identifier in defineAnnoType\n"));
1927 		EMSG("E650: invalid buffer identifier in defineAnnoType");
1928 		return FAIL;
1929 	    }
1930 
1931 	    cp = (char *)args;
1932 	    typeNum = strtol(cp, &cp, 10);
1933 	    args = (char_u *)cp;
1934 	    args = skipwhite(args);
1935 	    typeName = (char_u *)nb_unquote(args, &args);
1936 	    args = skipwhite(args + 1);
1937 	    tooltip = (char_u *)nb_unquote(args, &args);
1938 	    args = skipwhite(args + 1);
1939 
1940 	    p = (char_u *)nb_unquote(args, &args);
1941 	    glyphFile = vim_strsave_escaped(p, escape_chars);
1942 	    vim_free(p);
1943 
1944 	    args = skipwhite(args + 1);
1945 	    p = skiptowhite(args);
1946 	    if (*p != NUL)
1947 	    {
1948 		*p = NUL;
1949 		p = skipwhite(p + 1);
1950 	    }
1951 	    fg = vim_strsave(args);
1952 	    bg = vim_strsave(p);
1953 	    if (STRLEN(fg) > MAX_COLOR_LENGTH || STRLEN(bg) > MAX_COLOR_LENGTH)
1954 	    {
1955 		EMSG("E532: highlighting color name too long in defineAnnoType");
1956 		vim_free(typeName);
1957 		parse_error = TRUE;
1958 	    }
1959 	    else if (typeName != NULL && tooltip != NULL && glyphFile != NULL)
1960 		addsigntype(buf, typeNum, typeName, tooltip, glyphFile, fg, bg);
1961 	    else
1962 		vim_free(typeName);
1963 
1964 	    /* don't free typeName; it's used directly in addsigntype() */
1965 	    vim_free(fg);
1966 	    vim_free(bg);
1967 	    vim_free(tooltip);
1968 	    vim_free(glyphFile);
1969 	    if (parse_error)
1970 		return FAIL;
1971 
1972 #endif
1973 /* =====================================================================*/
1974 	}
1975 	else if (streq((char *)cmd, "addAnno"))
1976 	{
1977 #ifdef FEAT_SIGNS
1978 	    int serNum;
1979 	    int localTypeNum;
1980 	    int typeNum;
1981 	    pos_T *pos;
1982 
1983 	    if (buf == NULL || buf->bufp == NULL)
1984 	    {
1985 		nbdebug(("    invalid buffer identifier in addAnno\n"));
1986 		EMSG("E651: invalid buffer identifier in addAnno");
1987 		return FAIL;
1988 	    }
1989 
1990 	    do_update = 1;
1991 
1992 	    cp = (char *)args;
1993 	    serNum = strtol(cp, &cp, 10);
1994 
1995 	    /* Get the typenr specific for this buffer and convert it to
1996 	     * the global typenumber, as used for the sign name. */
1997 	    localTypeNum = strtol(cp, &cp, 10);
1998 	    args = (char_u *)cp;
1999 	    typeNum = mapsigntype(buf, localTypeNum);
2000 
2001 	    pos = get_off_or_lnum(buf->bufp, &args);
2002 
2003 	    cp = (char *)args;
2004 	    ignored = (int)strtol(cp, &cp, 10);
2005 	    args = (char_u *)cp;
2006 # ifdef NBDEBUG
2007 	    if (ignored != -1)
2008 	    {
2009 		nbdebug(("    partial line annotation -- Not Yet Implemented!\n"));
2010 	    }
2011 # endif
2012 	    if (serNum >= GUARDEDOFFSET)
2013 	    {
2014 		nbdebug(("    too many annotations! ignoring...\n"));
2015 		return FAIL;
2016 	    }
2017 	    if (pos)
2018 	    {
2019 		coloncmd(":sign place %d line=%ld name=%d buffer=%d",
2020 			   serNum, pos->lnum, typeNum, buf->bufp->b_fnum);
2021 		if (typeNum == curPCtype)
2022 		    coloncmd(":sign jump %d buffer=%d", serNum,
2023 						       buf->bufp->b_fnum);
2024 	    }
2025 #endif
2026 /* =====================================================================*/
2027 	}
2028 	else if (streq((char *)cmd, "removeAnno"))
2029 	{
2030 #ifdef FEAT_SIGNS
2031 	    int serNum;
2032 
2033 	    if (buf == NULL || buf->bufp == NULL)
2034 	    {
2035 		nbdebug(("    invalid buffer identifier in removeAnno\n"));
2036 		return FAIL;
2037 	    }
2038 	    do_update = 1;
2039 	    cp = (char *)args;
2040 	    serNum = strtol(cp, &cp, 10);
2041 	    args = (char_u *)cp;
2042 	    coloncmd(":sign unplace %d buffer=%d",
2043 		     serNum, buf->bufp->b_fnum);
2044 	    redraw_buf_later(buf->bufp, NOT_VALID);
2045 #endif
2046 /* =====================================================================*/
2047 	}
2048 	else if (streq((char *)cmd, "moveAnnoToFront"))
2049 	{
2050 #ifdef FEAT_SIGNS
2051 	    nbdebug(("    moveAnnoToFront: Not Yet Implemented!\n"));
2052 #endif
2053 /* =====================================================================*/
2054 	}
2055 	else if (streq((char *)cmd, "guard") || streq((char *)cmd, "unguard"))
2056 	{
2057 	    int len;
2058 	    pos_T first;
2059 	    pos_T last;
2060 	    pos_T *pos;
2061 	    int un = (cmd[0] == 'u');
2062 	    static int guardId = GUARDEDOFFSET;
2063 
2064 	    if (skip >= SKIP_STOP)
2065 	    {
2066 		nbdebug(("    Skipping %s command\n", (char *) cmd));
2067 		return OK;
2068 	    }
2069 
2070 	    nb_init_graphics();
2071 
2072 	    if (buf == NULL || buf->bufp == NULL)
2073 	    {
2074 		nbdebug(("    invalid buffer identifier in %s command\n", cmd));
2075 		return FAIL;
2076 	    }
2077 	    nb_set_curbuf(buf->bufp);
2078 	    cp = (char *)args;
2079 	    off = strtol(cp, &cp, 10);
2080 	    len = strtol(cp, NULL, 10);
2081 	    args = (char_u *)cp;
2082 	    pos = off2pos(buf->bufp, off);
2083 	    do_update = 1;
2084 	    if (!pos)
2085 		nbdebug(("    no such start pos in %s, %ld\n", cmd, off));
2086 	    else
2087 	    {
2088 		first = *pos;
2089 		pos = off2pos(buf->bufp, off + len - 1);
2090 		if (pos != NULL && pos->col == 0)
2091 		{
2092 			/*
2093 			 * In Java Swing the offset is a position between 2
2094 			 * characters. If col == 0 then we really want the
2095 			 * previous line as the end.
2096 			 */
2097 			pos = off2pos(buf->bufp, off + len - 2);
2098 		}
2099 		if (!pos)
2100 		    nbdebug(("    no such end pos in %s, %ld\n",
2101 			    cmd, off + len - 1));
2102 		else
2103 		{
2104 		    long lnum;
2105 		    last = *pos;
2106 		    /* set highlight for region */
2107 		    nbdebug(("    %sGUARD %ld,%d to %ld,%d\n", (un) ? "UN" : "",
2108 			     first.lnum, first.col,
2109 			     last.lnum, last.col));
2110 #ifdef FEAT_SIGNS
2111 		    for (lnum = first.lnum; lnum <= last.lnum; lnum++)
2112 		    {
2113 			if (un)
2114 			{
2115 			    /* never used */
2116 			}
2117 			else
2118 			{
2119 			    if (buf_findsigntype_id(buf->bufp, lnum,
2120 				GUARDED) == 0)
2121 			    {
2122 				coloncmd(
2123 				    ":sign place %d line=%ld name=%d buffer=%d",
2124 				     guardId++, lnum, GUARDED,
2125 				     buf->bufp->b_fnum);
2126 			    }
2127 			}
2128 		    }
2129 #endif
2130 		    redraw_buf_later(buf->bufp, NOT_VALID);
2131 		}
2132 	    }
2133 /* =====================================================================*/
2134 	}
2135 	else if (streq((char *)cmd, "startAtomic"))
2136 	{
2137 	    inAtomic = 1;
2138 /* =====================================================================*/
2139 	}
2140 	else if (streq((char *)cmd, "endAtomic"))
2141 	{
2142 	    inAtomic = 0;
2143 	    if (needupdate)
2144 	    {
2145 		do_update = 1;
2146 		needupdate = 0;
2147 	    }
2148 /* =====================================================================*/
2149 	}
2150 	else if (streq((char *)cmd, "save"))
2151 	{
2152 	    /*
2153 	     * NOTE - This command is obsolete wrt NetBeans. Its left in
2154 	     * only for historical reasons.
2155 	     */
2156 	    if (buf == NULL || buf->bufp == NULL)
2157 	    {
2158 		nbdebug(("    invalid buffer identifier in %s command\n", cmd));
2159 		return FAIL;
2160 	    }
2161 
2162 	    /* the following is taken from ex_cmds.c (do_wqall function) */
2163 	    if (bufIsChanged(buf->bufp))
2164 	    {
2165 		/* Only write if the buffer can be written. */
2166 		if (p_write
2167 			&& !buf->bufp->b_p_ro
2168 			&& buf->bufp->b_ffname != NULL
2169 #ifdef FEAT_QUICKFIX
2170 			&& !bt_dontwrite(buf->bufp)
2171 #endif
2172 			)
2173 		{
2174 		    buf_write_all(buf->bufp, FALSE);
2175 #ifdef FEAT_AUTOCMD
2176 		    /* an autocommand may have deleted the buffer */
2177 		    if (!buf_valid(buf->bufp))
2178 			buf->bufp = NULL;
2179 #endif
2180 		}
2181 	    }
2182 	    else
2183 	    {
2184 		nbdebug(("    Buffer has no changes!\n"));
2185 	    }
2186 /* =====================================================================*/
2187 	}
2188 	else if (streq((char *)cmd, "netbeansBuffer"))
2189 	{
2190 	    if (buf == NULL || buf->bufp == NULL)
2191 	    {
2192 		nbdebug(("    invalid buffer identifier in %s command\n", cmd));
2193 		return FAIL;
2194 	    }
2195 	    if (*args == 'T')
2196 	    {
2197 		buf->bufp->b_netbeans_file = TRUE;
2198 		buf->bufp->b_was_netbeans_file = TRUE;
2199 	    }
2200 	    else
2201 		buf->bufp->b_netbeans_file = FALSE;
2202 /* =====================================================================*/
2203 	}
2204 	else if (streq((char *)cmd, "specialKeys"))
2205 	{
2206 	    special_keys(args);
2207 /* =====================================================================*/
2208 	}
2209 	else if (streq((char *)cmd, "actionMenuItem"))
2210 	{
2211 	    /* not used yet */
2212 /* =====================================================================*/
2213 	}
2214 	else if (streq((char *)cmd, "version"))
2215 	{
2216 	    /* not used yet */
2217 	}
2218 	else
2219 	{
2220 	    nbdebug(("Unrecognised command: %s\n", cmd));
2221 	}
2222 	/*
2223 	 * Unrecognized command is ignored.
2224 	 */
2225     }
2226     if (inAtomic && do_update)
2227     {
2228 	needupdate = 1;
2229 	do_update = 0;
2230     }
2231 
2232     /*
2233      * Is this needed? I moved the netbeans_Xt_connect() later during startup
2234      * and it may no longer be necessary. If its not needed then needupdate
2235      * and do_update can also be removed.
2236      */
2237     if (buf != NULL && buf->initDone && do_update)
2238     {
2239 	update_screen(NOT_VALID);
2240 	setcursor();
2241 	cursor_on();
2242 	out_flush();
2243 #ifdef FEAT_GUI
2244 	if (gui.in_use)
2245 	{
2246 	    gui_update_cursor(TRUE, FALSE);
2247 	    gui_mch_flush();
2248 	}
2249 #endif
2250 	/* Quit a hit-return or more prompt. */
2251 	if (State == HITRETURN || State == ASKMORE)
2252 	{
2253 #ifdef FEAT_GUI_GTK
2254 	    if (gui.in_use && gtk_main_level() > 0)
2255 		gtk_main_quit();
2256 #endif
2257 	}
2258     }
2259 
2260     return retval;
2261 }
2262 
2263 
2264 /*
2265  * If "buf" is not the current buffer try changing to a window that edits this
2266  * buffer.  If there is no such window then close the current buffer and set
2267  * the current buffer as "buf".
2268  */
2269     static void
2270 nb_set_curbuf(buf_T *buf)
2271 {
2272     if (curbuf != buf) {
2273 	if (buf_jump_open_win(buf) != NULL)
2274 	    return;
2275 # ifdef FEAT_WINDOWS
2276 	if ((swb_flags & SWB_USETAB) && buf_jump_open_tab(buf) != NULL)
2277 	    return;
2278 # endif
2279 	set_curbuf(buf, DOBUF_GOTO);
2280     }
2281 }
2282 
2283 /*
2284  * Process a vim colon command.
2285  */
2286     static void
2287 coloncmd(char *cmd, ...)
2288 {
2289     char buf[1024];
2290     va_list ap;
2291 
2292     va_start(ap, cmd);
2293     vim_vsnprintf(buf, sizeof(buf), cmd, ap, NULL);
2294     va_end(ap);
2295 
2296     nbdebug(("    COLONCMD %s\n", buf));
2297 
2298 /*     ALT_INPUT_LOCK_ON; */
2299     do_cmdline((char_u *)buf, NULL, NULL, DOCMD_NOWAIT | DOCMD_KEYTYPED);
2300 /*     ALT_INPUT_LOCK_OFF; */
2301 
2302     setcursor();		/* restore the cursor position */
2303     out_flush();		/* make sure output has been written */
2304 
2305 #ifdef FEAT_GUI
2306     if (gui.in_use)
2307     {
2308 	gui_update_cursor(TRUE, FALSE);
2309 	gui_mch_flush();
2310     }
2311 #endif
2312 }
2313 
2314 
2315 /*
2316  * Parse the specialKeys argument and issue the appropriate map commands.
2317  */
2318     static void
2319 special_keys(char_u *args)
2320 {
2321     char *save_str = nb_unquote(args, NULL);
2322     char *tok = strtok(save_str, " ");
2323     char *sep;
2324     char keybuf[64];
2325     char cmdbuf[256];
2326 
2327     while (tok != NULL)
2328     {
2329 	int i = 0;
2330 
2331 	if ((sep = strchr(tok, '-')) != NULL)
2332 	{
2333 	    *sep = NUL;
2334 	    while (*tok)
2335 	    {
2336 		switch (*tok)
2337 		{
2338 		    case 'A':
2339 		    case 'M':
2340 		    case 'C':
2341 		    case 'S':
2342 			keybuf[i++] = *tok;
2343 			keybuf[i++] = '-';
2344 			break;
2345 		}
2346 		tok++;
2347 	    }
2348 	    tok++;
2349 	}
2350 
2351 	strcpy(&keybuf[i], tok);
2352 	vim_snprintf(cmdbuf, sizeof(cmdbuf),
2353 				"<silent><%s> :nbkey %s<CR>", keybuf, keybuf);
2354 	do_map(0, (char_u *)cmdbuf, NORMAL, FALSE);
2355 	tok = strtok(NULL, " ");
2356     }
2357     vim_free(save_str);
2358 }
2359 
2360     void
2361 ex_nbclose(exarg_T *eap UNUSED)
2362 {
2363     netbeans_close();
2364 }
2365 
2366     void
2367 ex_nbkey(exarg_T *eap)
2368 {
2369     (void)netbeans_keystring(eap->arg);
2370 }
2371 
2372     void
2373 ex_nbstart(
2374     exarg_T	*eap)
2375 {
2376 #ifdef FEAT_GUI
2377 # if !defined(FEAT_GUI_X11) && !defined(FEAT_GUI_GTK)  \
2378 		&& !defined(FEAT_GUI_W32)
2379     if (gui.in_use)
2380     {
2381 	EMSG(_("E838: netbeans is not supported with this GUI"));
2382 	return;
2383     }
2384 # endif
2385 #endif
2386     netbeans_open((char *)eap->arg, FALSE);
2387 }
2388 
2389 /*
2390  * Initialize highlights and signs for use by netbeans  (mostly obsolete)
2391  */
2392     static void
2393 nb_init_graphics(void)
2394 {
2395     static int did_init = FALSE;
2396 
2397     if (!did_init)
2398     {
2399 	coloncmd(":highlight NBGuarded guibg=Cyan guifg=Black"
2400 			    " ctermbg=LightCyan ctermfg=Black");
2401 	coloncmd(":sign define %d linehl=NBGuarded", GUARDED);
2402 
2403 	did_init = TRUE;
2404     }
2405 }
2406 
2407 /*
2408  * Convert key to netbeans name.  This uses the global "mod_mask".
2409  */
2410     static void
2411 netbeans_keyname(int key, char *buf)
2412 {
2413     char *name = 0;
2414     char namebuf[2];
2415     int ctrl  = 0;
2416     int shift = 0;
2417     int alt   = 0;
2418 
2419     if (mod_mask & MOD_MASK_CTRL)
2420 	ctrl = 1;
2421     if (mod_mask & MOD_MASK_SHIFT)
2422 	shift = 1;
2423     if (mod_mask & MOD_MASK_ALT)
2424 	alt = 1;
2425 
2426 
2427     switch (key)
2428     {
2429 	case K_F1:		name = "F1";		break;
2430 	case K_S_F1:	name = "F1";	shift = 1;	break;
2431 	case K_F2:		name = "F2";		break;
2432 	case K_S_F2:	name = "F2";	shift = 1;	break;
2433 	case K_F3:		name = "F3";		break;
2434 	case K_S_F3:	name = "F3";	shift = 1;	break;
2435 	case K_F4:		name = "F4";		break;
2436 	case K_S_F4:	name = "F4";	shift = 1;	break;
2437 	case K_F5:		name = "F5";		break;
2438 	case K_S_F5:	name = "F5";	shift = 1;	break;
2439 	case K_F6:		name = "F6";		break;
2440 	case K_S_F6:	name = "F6";	shift = 1;	break;
2441 	case K_F7:		name = "F7";		break;
2442 	case K_S_F7:	name = "F7";	shift = 1;	break;
2443 	case K_F8:		name = "F8";		break;
2444 	case K_S_F8:	name = "F8";	shift = 1;	break;
2445 	case K_F9:		name = "F9";		break;
2446 	case K_S_F9:	name = "F9";	shift = 1;	break;
2447 	case K_F10:		name = "F10";		break;
2448 	case K_S_F10:	name = "F10";	shift = 1;	break;
2449 	case K_F11:		name = "F11";		break;
2450 	case K_S_F11:	name = "F11";	shift = 1;	break;
2451 	case K_F12:		name = "F12";		break;
2452 	case K_S_F12:	name = "F12";	shift = 1;	break;
2453 	default:
2454 			if (key >= ' ' && key <= '~')
2455 			{
2456 			    /* Allow ASCII characters. */
2457 			    name = namebuf;
2458 			    namebuf[0] = key;
2459 			    namebuf[1] = NUL;
2460 			}
2461 			else
2462 			    name = "X";
2463 			break;
2464     }
2465 
2466     buf[0] = '\0';
2467     if (ctrl)
2468 	strcat(buf, "C");
2469     if (shift)
2470 	strcat(buf, "S");
2471     if (alt)
2472 	strcat(buf, "M"); /* META */
2473     if (ctrl || shift || alt)
2474 	strcat(buf, "-");
2475     strcat(buf, name);
2476 }
2477 
2478 #if defined(FEAT_BEVAL) || defined(PROTO)
2479 /*
2480  * Function to be called for balloon evaluation.  Grabs the text under the
2481  * cursor and sends it to the debugger for evaluation.  The debugger should
2482  * respond with a showBalloon command when there is a useful result.
2483  */
2484     void
2485 netbeans_beval_cb(
2486 	BalloonEval	*beval,
2487 	int		 state UNUSED)
2488 {
2489     win_T	*wp;
2490     char_u	*text;
2491     linenr_T	lnum;
2492     int		col;
2493     char	*buf;
2494     char_u	*p;
2495 
2496     /* Don't do anything when 'ballooneval' is off, messages scrolled the
2497      * windows up or we have no connection. */
2498     if (!p_beval || msg_scrolled > 0 || !NETBEANS_OPEN)
2499 	return;
2500 
2501     if (get_beval_info(beval, TRUE, &wp, &lnum, &text, &col) == OK)
2502     {
2503 	/* Send debugger request.  Only when the text is of reasonable
2504 	 * length. */
2505 	if (text != NULL && text[0] != NUL && STRLEN(text) < MAXPATHL)
2506 	{
2507 	    buf = (char *)alloc(MAXPATHL * 2 + 25);
2508 	    if (buf != NULL)
2509 	    {
2510 		p = nb_quote(text);
2511 		if (p != NULL)
2512 		{
2513 		    vim_snprintf(buf, MAXPATHL * 2 + 25,
2514 				     "0:balloonText=%d \"%s\"\n", r_cmdno, p);
2515 		    vim_free(p);
2516 		}
2517 		nbdebug(("EVT: %s", buf));
2518 		nb_send(buf, "netbeans_beval_cb");
2519 		vim_free(buf);
2520 	    }
2521 	}
2522 	vim_free(text);
2523     }
2524 }
2525 #endif
2526 
2527 /*
2528  * Return TRUE when the netbeans connection is active.
2529  */
2530     int
2531 netbeans_active(void)
2532 {
2533     return NETBEANS_OPEN;
2534 }
2535 
2536 /*
2537  * Tell netbeans that the window was opened, ready for commands.
2538  */
2539     void
2540 netbeans_open(char *params, int doabort)
2541 {
2542     char *cmd = "0:startupDone=0\n";
2543 
2544     if (NETBEANS_OPEN)
2545     {
2546 	EMSG(_("E511: netbeans already connected"));
2547 	return;
2548     }
2549 
2550     if (netbeans_connect(params, doabort) != OK)
2551 	return;
2552 
2553     nbdebug(("EVT: %s", cmd));
2554     nb_send(cmd, "netbeans_startup_done");
2555 
2556     /* update the screen after having added the gutter */
2557     changed_window_setting();
2558     update_screen(CLEAR);
2559     setcursor();
2560     cursor_on();
2561     out_flush();
2562 #ifdef FEAT_GUI
2563     if (gui.in_use)
2564     {
2565 	gui_update_cursor(TRUE, FALSE);
2566 	gui_mch_flush();
2567     }
2568 #endif
2569 }
2570 
2571 /*
2572  * Tell netbeans that we're exiting. This should be called right
2573  * before calling exit.
2574  */
2575     void
2576 netbeans_send_disconnect(void)
2577 {
2578     char buf[128];
2579 
2580     if (NETBEANS_OPEN)
2581     {
2582 	sprintf(buf, "0:disconnect=%d\n", r_cmdno);
2583 	nbdebug(("EVT: %s", buf));
2584 	nb_send(buf, "netbeans_disconnect");
2585     }
2586 }
2587 
2588 #if defined(FEAT_GUI_X11) || defined(FEAT_GUI_W32) || defined(PROTO)
2589 /*
2590  * Tell netbeans that the window was moved or resized.
2591  */
2592     void
2593 netbeans_frame_moved(int new_x, int new_y)
2594 {
2595     char buf[128];
2596 
2597     if (!NETBEANS_OPEN)
2598 	return;
2599 
2600     sprintf(buf, "0:geometry=%d %d %d %d %d\n",
2601 		    r_cmdno, (int)Columns, (int)Rows, new_x, new_y);
2602     /*nbdebug(("EVT: %s", buf)); happens too many times during a move */
2603     nb_send(buf, "netbeans_frame_moved");
2604 }
2605 #endif
2606 
2607 /*
2608  * Tell netbeans the user opened or activated a file.
2609  */
2610     void
2611 netbeans_file_activated(buf_T *bufp)
2612 {
2613     int bufno = nb_getbufno(bufp);
2614     nbbuf_T *bp = nb_get_buf(bufno);
2615     char    buffer[2*MAXPATHL];
2616     char_u  *q;
2617 
2618     if (!NETBEANS_OPEN || !bufp->b_netbeans_file || dosetvisible)
2619 	return;
2620 
2621     q = nb_quote(bufp->b_ffname);
2622     if (q == NULL || bp == NULL)
2623 	return;
2624 
2625     vim_snprintf(buffer, sizeof(buffer),  "%d:fileOpened=%d \"%s\" %s %s\n",
2626 	    bufno,
2627 	    bufno,
2628 	    (char *)q,
2629 	    "T",  /* open in NetBeans */
2630 	    "F"); /* modified */
2631 
2632     vim_free(q);
2633     nbdebug(("EVT: %s", buffer));
2634 
2635     nb_send(buffer, "netbeans_file_opened");
2636 }
2637 
2638 /*
2639  * Tell netbeans the user opened a file.
2640  */
2641     void
2642 netbeans_file_opened(buf_T *bufp)
2643 {
2644     int bufno = nb_getbufno(bufp);
2645     char    buffer[2*MAXPATHL];
2646     char_u  *q;
2647     nbbuf_T *bp = nb_get_buf(nb_getbufno(bufp));
2648     int	    bnum;
2649 
2650     if (!NETBEANS_OPEN)
2651 	return;
2652 
2653     q = nb_quote(bufp->b_ffname);
2654     if (q == NULL)
2655 	return;
2656     if (bp != NULL)
2657 	bnum = bufno;
2658     else
2659 	bnum = 0;
2660 
2661     vim_snprintf(buffer, sizeof(buffer), "%d:fileOpened=%d \"%s\" %s %s\n",
2662 	    bnum,
2663 	    0,
2664 	    (char *)q,
2665 	    "T",  /* open in NetBeans */
2666 	    "F"); /* modified */
2667 
2668     vim_free(q);
2669     nbdebug(("EVT: %s", buffer));
2670 
2671     nb_send(buffer, "netbeans_file_opened");
2672     if (p_acd && vim_chdirfile(bufp->b_ffname) == OK)
2673 	shorten_fnames(TRUE);
2674 }
2675 
2676 /*
2677  * Tell netbeans that a file was deleted or wiped out.
2678  */
2679     void
2680 netbeans_file_killed(buf_T *bufp)
2681 {
2682     int		bufno = nb_getbufno(bufp);
2683     nbbuf_T	*nbbuf = nb_get_buf(bufno);
2684     char	buffer[2*MAXPATHL];
2685 
2686     if (!NETBEANS_OPEN || bufno == -1)
2687 	return;
2688 
2689     nbdebug(("netbeans_file_killed:\n"));
2690     nbdebug(("    Killing bufno: %d", bufno));
2691 
2692     sprintf(buffer, "%d:killed=%d\n", bufno, r_cmdno);
2693 
2694     nbdebug(("EVT: %s", buffer));
2695 
2696     nb_send(buffer, "netbeans_file_killed");
2697 
2698     if (nbbuf != NULL)
2699 	nbbuf->bufp = NULL;
2700 }
2701 
2702 /*
2703  * Get a pointer to the Netbeans buffer for Vim buffer "bufp".
2704  * Return NULL if there is no such buffer or changes are not to be reported.
2705  * Otherwise store the buffer number in "*bufnop".
2706  */
2707     static nbbuf_T *
2708 nb_bufp2nbbuf_fire(buf_T *bufp, int *bufnop)
2709 {
2710     int		bufno;
2711     nbbuf_T	*nbbuf;
2712 
2713     if (!NETBEANS_OPEN || !netbeansFireChanges)
2714 	return NULL;		/* changes are not reported at all */
2715 
2716     bufno = nb_getbufno(bufp);
2717     if (bufno <= 0)
2718 	return NULL;		/* file is not known to NetBeans */
2719 
2720     nbbuf = nb_get_buf(bufno);
2721     if (nbbuf != NULL && !nbbuf->fireChanges)
2722 	return NULL;		/* changes in this buffer are not reported */
2723 
2724     *bufnop = bufno;
2725     return nbbuf;
2726 }
2727 
2728 /*
2729  * Tell netbeans the user inserted some text.
2730  */
2731     void
2732 netbeans_inserted(
2733     buf_T	*bufp,
2734     linenr_T	linenr,
2735     colnr_T	col,
2736     char_u	*txt,
2737     int		newlen)
2738 {
2739     char_u	*buf;
2740     int		bufno;
2741     nbbuf_T	*nbbuf;
2742     pos_T	pos;
2743     long	off;
2744     char_u	*p;
2745     char_u	*newtxt;
2746 
2747     if (!NETBEANS_OPEN)
2748 	return;
2749 
2750     nbbuf = nb_bufp2nbbuf_fire(bufp, &bufno);
2751     if (nbbuf == NULL)
2752 	return;
2753 
2754     /* Don't mark as modified for initial read */
2755     if (nbbuf->insertDone)
2756 	nbbuf->modified = 1;
2757 
2758     pos.lnum = linenr;
2759     pos.col = col;
2760     off = pos2off(bufp, &pos);
2761 
2762     /* send the "insert" EVT */
2763     newtxt = alloc(newlen + 1);
2764     vim_strncpy(newtxt, txt, newlen);
2765     p = nb_quote(newtxt);
2766     if (p != NULL)
2767     {
2768 	buf = alloc(128 + 2*newlen);
2769 	sprintf((char *)buf, "%d:insert=%d %ld \"%s\"\n",
2770 						      bufno, r_cmdno, off, p);
2771 	nbdebug(("EVT: %s", buf));
2772 	nb_send((char *)buf, "netbeans_inserted");
2773 	vim_free(p);
2774 	vim_free(buf);
2775     }
2776     vim_free(newtxt);
2777 }
2778 
2779 /*
2780  * Tell netbeans some bytes have been removed.
2781  */
2782     void
2783 netbeans_removed(
2784     buf_T	*bufp,
2785     linenr_T	linenr,
2786     colnr_T	col,
2787     long	len)
2788 {
2789     char_u	buf[128];
2790     int		bufno;
2791     nbbuf_T	*nbbuf;
2792     pos_T	pos;
2793     long	off;
2794 
2795     if (!NETBEANS_OPEN)
2796 	return;
2797 
2798     nbbuf = nb_bufp2nbbuf_fire(bufp, &bufno);
2799     if (nbbuf == NULL)
2800 	return;
2801 
2802     if (len < 0)
2803     {
2804 	nbdebug(("Negative len %ld in netbeans_removed()!\n", len));
2805 	return;
2806     }
2807 
2808     nbbuf->modified = 1;
2809 
2810     pos.lnum = linenr;
2811     pos.col = col;
2812 
2813     off = pos2off(bufp, &pos);
2814 
2815     sprintf((char *)buf, "%d:remove=%d %ld %ld\n", bufno, r_cmdno, off, len);
2816     nbdebug(("EVT: %s", buf));
2817     nb_send((char *)buf, "netbeans_removed");
2818 }
2819 
2820 /*
2821  * Send netbeans an unmodified command.
2822  */
2823     void
2824 netbeans_unmodified(buf_T *bufp UNUSED)
2825 {
2826     /* This is a no-op, because NetBeans considers a buffer modified
2827      * even when all changes have been undone. */
2828 }
2829 
2830 /*
2831  * Send a button release event back to netbeans. Its up to netbeans
2832  * to decide what to do (if anything) with this event.
2833  */
2834     void
2835 netbeans_button_release(int button)
2836 {
2837     char	buf[128];
2838     int		bufno;
2839 
2840     if (!NETBEANS_OPEN)
2841 	return;
2842 
2843     bufno = nb_getbufno(curbuf);
2844 
2845     if (bufno >= 0 && curwin != NULL && curwin->w_buffer == curbuf)
2846     {
2847 	int col = mouse_col - W_WINCOL(curwin)
2848 			      - ((curwin->w_p_nu || curwin->w_p_rnu) ? 9 : 1);
2849 	long off = pos2off(curbuf, &curwin->w_cursor);
2850 
2851 	/* sync the cursor position */
2852 	sprintf(buf, "%d:newDotAndMark=%d %ld %ld\n", bufno, r_cmdno, off, off);
2853 	nbdebug(("EVT: %s", buf));
2854 	nb_send(buf, "netbeans_button_release[newDotAndMark]");
2855 
2856 	sprintf(buf, "%d:buttonRelease=%d %d %ld %d\n", bufno, r_cmdno,
2857 				    button, (long)curwin->w_cursor.lnum, col);
2858 	nbdebug(("EVT: %s", buf));
2859 	nb_send(buf, "netbeans_button_release");
2860     }
2861 }
2862 
2863 
2864 /*
2865  * Send a keypress event back to netbeans. This usually simulates some
2866  * kind of function key press. This function operates on a key code.
2867  * Return TRUE when the key was sent, FALSE when the command has been
2868  * postponed.
2869  */
2870     int
2871 netbeans_keycommand(int key)
2872 {
2873     char	keyName[60];
2874 
2875     netbeans_keyname(key, keyName);
2876     return netbeans_keystring((char_u *)keyName);
2877 }
2878 
2879 
2880 /*
2881  * Send a keypress event back to netbeans. This usually simulates some
2882  * kind of function key press. This function operates on a key string.
2883  * Return TRUE when the key was sent, FALSE when the command has been
2884  * postponed.
2885  */
2886     static int
2887 netbeans_keystring(char_u *keyName)
2888 {
2889     char	buf[2*MAXPATHL];
2890     int		bufno = nb_getbufno(curbuf);
2891     long	off;
2892     char_u	*q;
2893 
2894     if (!NETBEANS_OPEN)
2895 	return TRUE;
2896 
2897     if (bufno == -1)
2898     {
2899 	nbdebug(("got keycommand for non-NetBeans buffer, opening...\n"));
2900 	q = curbuf->b_ffname == NULL ? (char_u *)""
2901 						 : nb_quote(curbuf->b_ffname);
2902 	if (q == NULL)
2903 	    return TRUE;
2904 	vim_snprintf(buf, sizeof(buf), "0:fileOpened=%d \"%s\" %s %s\n", 0,
2905 		q,
2906 		"T",  /* open in NetBeans */
2907 		"F"); /* modified */
2908 	if (curbuf->b_ffname != NULL)
2909 	    vim_free(q);
2910 	nbdebug(("EVT: %s", buf));
2911 	nb_send(buf, "netbeans_keycommand");
2912 
2913 	postpone_keycommand(keyName);
2914 	return FALSE;
2915     }
2916 
2917     /* sync the cursor position */
2918     off = pos2off(curbuf, &curwin->w_cursor);
2919     sprintf(buf, "%d:newDotAndMark=%d %ld %ld\n", bufno, r_cmdno, off, off);
2920     nbdebug(("EVT: %s", buf));
2921     nb_send(buf, "netbeans_keycommand");
2922 
2923     /* To work on Win32 you must apply patch to ExtEditor module
2924      * from ExtEdCaret.java.diff - make EVT_newDotAndMark handler
2925      * more synchronous
2926      */
2927 
2928     /* now send keyCommand event */
2929     vim_snprintf(buf, sizeof(buf), "%d:keyCommand=%d \"%s\"\n",
2930 						     bufno, r_cmdno, keyName);
2931     nbdebug(("EVT: %s", buf));
2932     nb_send(buf, "netbeans_keycommand");
2933 
2934     /* New: do both at once and include the lnum/col. */
2935     vim_snprintf(buf, sizeof(buf), "%d:keyAtPos=%d \"%s\" %ld %ld/%ld\n",
2936 	    bufno, r_cmdno, keyName,
2937 		off, (long)curwin->w_cursor.lnum, (long)curwin->w_cursor.col);
2938     nbdebug(("EVT: %s", buf));
2939     nb_send(buf, "netbeans_keycommand");
2940     return TRUE;
2941 }
2942 
2943 
2944 /*
2945  * Send a save event to netbeans.
2946  */
2947     void
2948 netbeans_save_buffer(buf_T *bufp)
2949 {
2950     char_u	buf[64];
2951     int		bufno;
2952     nbbuf_T	*nbbuf;
2953 
2954     if (!NETBEANS_OPEN)
2955 	return;
2956 
2957     nbbuf = nb_bufp2nbbuf_fire(bufp, &bufno);
2958     if (nbbuf == NULL)
2959 	return;
2960 
2961     nbbuf->modified = 0;
2962 
2963     sprintf((char *)buf, "%d:save=%d\n", bufno, r_cmdno);
2964     nbdebug(("EVT: %s", buf));
2965     nb_send((char *)buf, "netbeans_save_buffer");
2966 }
2967 
2968 
2969 /*
2970  * Send remove command to netbeans (this command has been turned off).
2971  */
2972     void
2973 netbeans_deleted_all_lines(buf_T *bufp)
2974 {
2975     char_u	buf[64];
2976     int		bufno;
2977     nbbuf_T	*nbbuf;
2978 
2979     if (!NETBEANS_OPEN)
2980 	return;
2981 
2982     nbbuf = nb_bufp2nbbuf_fire(bufp, &bufno);
2983     if (nbbuf == NULL)
2984 	return;
2985 
2986     /* Don't mark as modified for initial read */
2987     if (nbbuf->insertDone)
2988 	nbbuf->modified = 1;
2989 
2990     sprintf((char *)buf, "%d:remove=%d 0 -1\n", bufno, r_cmdno);
2991     nbdebug(("EVT(suppressed): %s", buf));
2992 /*     nb_send(buf, "netbeans_deleted_all_lines"); */
2993 }
2994 
2995 
2996 /*
2997  * See if the lines are guarded. The top and bot parameters are from
2998  * u_savecommon(), these are the line above the change and the line below the
2999  * change.
3000  */
3001     int
3002 netbeans_is_guarded(linenr_T top, linenr_T bot)
3003 {
3004     signlist_T	*p;
3005     int		lnum;
3006 
3007     if (!NETBEANS_OPEN)
3008 	return FALSE;
3009 
3010     for (p = curbuf->b_signlist; p != NULL; p = p->next)
3011 	if (p->id >= GUARDEDOFFSET)
3012 	    for (lnum = top + 1; lnum < bot; lnum++)
3013 		if (lnum == p->lnum)
3014 		    return TRUE;
3015 
3016     return FALSE;
3017 }
3018 
3019 #if defined(FEAT_GUI_X11) || defined(PROTO)
3020 /*
3021  * We have multiple signs to draw at the same location. Draw the
3022  * multi-sign indicator instead. This is the Motif version.
3023  */
3024     void
3025 netbeans_draw_multisign_indicator(int row)
3026 {
3027     int i;
3028     int y;
3029     int x;
3030 
3031     if (!NETBEANS_OPEN)
3032 	return;
3033 
3034     x = 0;
3035     y = row * gui.char_height + 2;
3036 
3037     for (i = 0; i < gui.char_height - 3; i++)
3038 	XDrawPoint(gui.dpy, gui.wid, gui.text_gc, x+2, y++);
3039 
3040     XDrawPoint(gui.dpy, gui.wid, gui.text_gc, x+0, y);
3041     XDrawPoint(gui.dpy, gui.wid, gui.text_gc, x+2, y);
3042     XDrawPoint(gui.dpy, gui.wid, gui.text_gc, x+4, y++);
3043     XDrawPoint(gui.dpy, gui.wid, gui.text_gc, x+1, y);
3044     XDrawPoint(gui.dpy, gui.wid, gui.text_gc, x+2, y);
3045     XDrawPoint(gui.dpy, gui.wid, gui.text_gc, x+3, y++);
3046     XDrawPoint(gui.dpy, gui.wid, gui.text_gc, x+2, y);
3047 }
3048 #endif /* FEAT_GUI_X11 */
3049 
3050 #if defined(FEAT_GUI_GTK) && !defined(PROTO)
3051 /*
3052  * We have multiple signs to draw at the same location. Draw the
3053  * multi-sign indicator instead. This is the GTK/Gnome version.
3054  */
3055     void
3056 netbeans_draw_multisign_indicator(int row)
3057 {
3058     int i;
3059     int y;
3060     int x;
3061 #if GTK_CHECK_VERSION(3,0,0)
3062     cairo_t *cr = NULL;
3063 #else
3064     GdkDrawable *drawable = gui.drawarea->window;
3065 #endif
3066 
3067     if (!NETBEANS_OPEN)
3068 	return;
3069 
3070 #if GTK_CHECK_VERSION(3,0,0)
3071     cr = cairo_create(gui.surface);
3072     {
3073 	GdkVisual *visual = NULL;
3074 	guint32 r_mask, g_mask, b_mask;
3075 	gint r_shift, g_shift, b_shift;
3076 
3077 	visual = gdk_window_get_visual(gtk_widget_get_window(gui.drawarea));
3078 	if (visual != NULL)
3079 	{
3080 	    gdk_visual_get_red_pixel_details(visual, &r_mask, &r_shift, NULL);
3081 	    gdk_visual_get_green_pixel_details(visual, &g_mask, &g_shift, NULL);
3082 	    gdk_visual_get_blue_pixel_details(visual, &b_mask, &b_shift, NULL);
3083 
3084 	    cairo_set_source_rgb(cr,
3085 		    ((gui.fgcolor->red & r_mask) >> r_shift) / 255.0,
3086 		    ((gui.fgcolor->green & g_mask) >> g_shift) / 255.0,
3087 		    ((gui.fgcolor->blue & b_mask) >> b_shift) / 255.0);
3088 	}
3089     }
3090 #endif
3091 
3092     x = 0;
3093     y = row * gui.char_height + 2;
3094 
3095     for (i = 0; i < gui.char_height - 3; i++)
3096 #if GTK_CHECK_VERSION(3,0,0)
3097 	cairo_rectangle(cr, x+2, y++, 1, 1);
3098 #else
3099 	gdk_draw_point(drawable, gui.text_gc, x+2, y++);
3100 #endif
3101 
3102 #if GTK_CHECK_VERSION(3,0,0)
3103     cairo_rectangle(cr, x+0, y, 1, 1);
3104     cairo_rectangle(cr, x+2, y, 1, 1);
3105     cairo_rectangle(cr, x+4, y++, 1, 1);
3106     cairo_rectangle(cr, x+1, y, 1, 1);
3107     cairo_rectangle(cr, x+2, y, 1, 1);
3108     cairo_rectangle(cr, x+3, y++, 1, 1);
3109     cairo_rectangle(cr, x+2, y, 1, 1);
3110 #else
3111     gdk_draw_point(drawable, gui.text_gc, x+0, y);
3112     gdk_draw_point(drawable, gui.text_gc, x+2, y);
3113     gdk_draw_point(drawable, gui.text_gc, x+4, y++);
3114     gdk_draw_point(drawable, gui.text_gc, x+1, y);
3115     gdk_draw_point(drawable, gui.text_gc, x+2, y);
3116     gdk_draw_point(drawable, gui.text_gc, x+3, y++);
3117     gdk_draw_point(drawable, gui.text_gc, x+2, y);
3118 #endif
3119 
3120 #if GTK_CHECK_VERSION(3,0,0)
3121     cairo_destroy(cr);
3122 #endif
3123 }
3124 #endif /* FEAT_GUI_GTK */
3125 
3126 /*
3127  * If the mouse is clicked in the gutter of a line with multiple
3128  * annotations, cycle through the set of signs.
3129  */
3130     void
3131 netbeans_gutter_click(linenr_T lnum)
3132 {
3133     signlist_T	*p;
3134 
3135     if (!NETBEANS_OPEN)
3136 	return;
3137 
3138     for (p = curbuf->b_signlist; p != NULL; p = p->next)
3139     {
3140 	if (p->lnum == lnum && p->next && p->next->lnum == lnum)
3141 	{
3142 	    signlist_T *tail;
3143 
3144 	    /* remove "p" from list, reinsert it at the tail of the sublist */
3145 	    if (p->prev)
3146 		p->prev->next = p->next;
3147 	    else
3148 		curbuf->b_signlist = p->next;
3149 	    p->next->prev = p->prev;
3150 	    /* now find end of sublist and insert p */
3151 	    for (tail = p->next;
3152 		  tail->next && tail->next->lnum == lnum
3153 					    && tail->next->id < GUARDEDOFFSET;
3154 		  tail = tail->next)
3155 		;
3156 	    /* tail now points to last entry with same lnum (except
3157 	     * that "guarded" annotations are always last) */
3158 	    p->next = tail->next;
3159 	    if (tail->next)
3160 		tail->next->prev = p;
3161 	    p->prev = tail;
3162 	    tail->next = p;
3163 	    update_debug_sign(curbuf, lnum);
3164 	    break;
3165 	}
3166     }
3167 }
3168 
3169 /*
3170  * Add a sign of the requested type at the requested location.
3171  *
3172  * Reverse engineering:
3173  * Apparently an annotation is defined the first time it is used in a buffer.
3174  * When the same annotation is used in two buffers, the second time we do not
3175  * need to define a new sign name but reuse the existing one.  But since the
3176  * ID number used in the second buffer starts counting at one again, a mapping
3177  * is made from the ID specifically for the buffer to the global sign name
3178  * (which is a number).
3179  *
3180  * globalsignmap[]	stores the signs that have been defined globally.
3181  * buf->signmapused[]	maps buffer-local annotation IDs to an index in
3182  *			globalsignmap[].
3183  */
3184     static void
3185 addsigntype(
3186     nbbuf_T	*buf,
3187     int		typeNum,
3188     char_u	*typeName,
3189     char_u	*tooltip UNUSED,
3190     char_u	*glyphFile,
3191     char_u	*fg,
3192     char_u	*bg)
3193 {
3194     int i, j;
3195     int use_fg = (*fg && STRCMP(fg, "none") != 0);
3196     int use_bg = (*bg && STRCMP(bg, "none") != 0);
3197 
3198     for (i = 0; i < globalsignmapused; i++)
3199 	if (STRCMP(typeName, globalsignmap[i]) == 0)
3200 	    break;
3201 
3202     if (i == globalsignmapused) /* not found; add it to global map */
3203     {
3204 	nbdebug(("DEFINEANNOTYPE(%d,%s,%s,%s,%s,%s)\n",
3205 			    typeNum, typeName, tooltip, glyphFile, fg, bg));
3206 	if (use_fg || use_bg)
3207 	{
3208 	    char fgbuf[2 * (8 + MAX_COLOR_LENGTH) + 1];
3209 	    char bgbuf[2 * (8 + MAX_COLOR_LENGTH) + 1];
3210 	    char *ptr;
3211 	    int value;
3212 
3213 	    value = strtol((char *)fg, &ptr, 10);
3214 	    if (ptr != (char *)fg)
3215 		sprintf(fgbuf, "guifg=#%06x", value & 0xFFFFFF);
3216 	    else
3217 		sprintf(fgbuf, "guifg=%s ctermfg=%s", fg, fg);
3218 
3219 	    value = strtol((char *)bg, &ptr, 10);
3220 	    if (ptr != (char *)bg)
3221 		sprintf(bgbuf, "guibg=#%06x", value & 0xFFFFFF);
3222 	    else
3223 		sprintf(bgbuf, "guibg=%s ctermbg=%s", bg, bg);
3224 
3225 	    coloncmd(":highlight NB_%s %s %s", typeName, (use_fg) ? fgbuf : "",
3226 		     (use_bg) ? bgbuf : "");
3227 	    if (*glyphFile == NUL)
3228 		/* no glyph, line highlighting only */
3229 		coloncmd(":sign define %d linehl=NB_%s", i + 1, typeName);
3230 	    else if (vim_strsize(glyphFile) <= 2)
3231 		/* one- or two-character glyph name, use as text glyph with
3232 		 * texthl */
3233 		coloncmd(":sign define %d text=%s texthl=NB_%s", i + 1,
3234 							 glyphFile, typeName);
3235 	    else
3236 		/* glyph, line highlighting */
3237 		coloncmd(":sign define %d icon=%s linehl=NB_%s", i + 1,
3238 							 glyphFile, typeName);
3239 	}
3240 	else
3241 	    /* glyph, no line highlighting */
3242 	    coloncmd(":sign define %d icon=%s", i + 1, glyphFile);
3243 
3244 	if (STRCMP(typeName,"CurrentPC") == 0)
3245 	    curPCtype = typeNum;
3246 
3247 	if (globalsignmapused == globalsignmaplen)
3248 	{
3249 	    if (globalsignmaplen == 0) /* first allocation */
3250 	    {
3251 		globalsignmaplen = 20;
3252 		globalsignmap = (char **)alloc_clear(globalsignmaplen*sizeof(char *));
3253 	    }
3254 	    else    /* grow it */
3255 	    {
3256 		int incr;
3257 		int oldlen = globalsignmaplen;
3258 		char **t_globalsignmap = globalsignmap;
3259 
3260 		globalsignmaplen *= 2;
3261 		incr = globalsignmaplen - oldlen;
3262 		globalsignmap = (char **)vim_realloc(globalsignmap,
3263 					   globalsignmaplen * sizeof(char *));
3264 		if (globalsignmap == NULL)
3265 		{
3266 		    vim_free(t_globalsignmap);
3267 		    globalsignmaplen = 0;
3268 		    return;
3269 		}
3270 		vim_memset(globalsignmap + oldlen, 0, incr * sizeof(char *));
3271 	    }
3272 	}
3273 
3274 	globalsignmap[i] = (char *)typeName;
3275 	globalsignmapused = i + 1;
3276     }
3277 
3278     /* check local map; should *not* be found! */
3279     for (j = 0; j < buf->signmapused; j++)
3280 	if (buf->signmap[j] == i + 1)
3281 	    return;
3282 
3283     /* add to local map */
3284     if (buf->signmapused == buf->signmaplen)
3285     {
3286 	if (buf->signmaplen == 0) /* first allocation */
3287 	{
3288 	    buf->signmaplen = 5;
3289 	    buf->signmap = (int *)alloc_clear(buf->signmaplen * sizeof(int));
3290 	}
3291 	else    /* grow it */
3292 	{
3293 	    int incr;
3294 	    int oldlen = buf->signmaplen;
3295 	    int *t_signmap = buf->signmap;
3296 
3297 	    buf->signmaplen *= 2;
3298 	    incr = buf->signmaplen - oldlen;
3299 	    buf->signmap = (int *)vim_realloc(buf->signmap,
3300 					       buf->signmaplen * sizeof(int));
3301 	    if (buf->signmap == NULL)
3302 	    {
3303 		vim_free(t_signmap);
3304 		buf->signmaplen = 0;
3305 		return;
3306 	    }
3307 	    vim_memset(buf->signmap + oldlen, 0, incr * sizeof(int));
3308 	}
3309     }
3310 
3311     buf->signmap[buf->signmapused++] = i + 1;
3312 
3313 }
3314 
3315 
3316 /*
3317  * See if we have the requested sign type in the buffer.
3318  */
3319     static int
3320 mapsigntype(nbbuf_T *buf, int localsigntype)
3321 {
3322     if (--localsigntype >= 0 && localsigntype < buf->signmapused)
3323 	return buf->signmap[localsigntype];
3324 
3325     return 0;
3326 }
3327 
3328 
3329 /*
3330  * Compute length of buffer, don't print anything.
3331  */
3332     static long
3333 get_buf_size(buf_T *bufp)
3334 {
3335     linenr_T	lnum;
3336     long	char_count = 0;
3337     int		eol_size;
3338     long	last_check = 100000L;
3339 
3340     if (bufp->b_ml.ml_flags & ML_EMPTY)
3341 	return 0;
3342     else
3343     {
3344 	if (get_fileformat(bufp) == EOL_DOS)
3345 	    eol_size = 2;
3346 	else
3347 	    eol_size = 1;
3348 	for (lnum = 1; lnum <= bufp->b_ml.ml_line_count; ++lnum)
3349 	{
3350 	    char_count += (long)STRLEN(ml_get_buf(bufp, lnum, FALSE))
3351 								   + eol_size;
3352 	    /* Check for a CTRL-C every 100000 characters */
3353 	    if (char_count > last_check)
3354 	    {
3355 		ui_breakcheck();
3356 		if (got_int)
3357 		    return char_count;
3358 		last_check = char_count + 100000L;
3359 	    }
3360 	}
3361 	/* Correction for when last line doesn't have an EOL. */
3362 	if (!bufp->b_p_eol && (bufp->b_p_bin || !bufp->b_p_fixeol))
3363 	    char_count -= eol_size;
3364     }
3365 
3366     return char_count;
3367 }
3368 
3369 /*
3370  * Convert character offset to lnum,col
3371  */
3372     static pos_T *
3373 off2pos(buf_T *buf, long offset)
3374 {
3375     linenr_T	 lnum;
3376     static pos_T pos;
3377 
3378     pos.lnum = 0;
3379     pos.col = 0;
3380 #ifdef FEAT_VIRTUALEDIT
3381     pos.coladd = 0;
3382 #endif
3383 
3384     if (!(buf->b_ml.ml_flags & ML_EMPTY))
3385     {
3386 	if ((lnum = ml_find_line_or_offset(buf, (linenr_T)0, &offset)) < 0)
3387 	    return NULL;
3388 	pos.lnum = lnum;
3389 	pos.col = offset;
3390     }
3391 
3392     return &pos;
3393 }
3394 
3395 /*
3396  * Convert an argument in the form "1234" to an offset and compute the
3397  * lnum/col from it.  Convert an argument in the form "123/12" directly to a
3398  * lnum/col.
3399  * "argp" is advanced to after the argument.
3400  * Return a pointer to the position, NULL if something is wrong.
3401  */
3402     static pos_T *
3403 get_off_or_lnum(buf_T *buf, char_u **argp)
3404 {
3405     static pos_T	mypos;
3406     long		off;
3407 
3408     off = strtol((char *)*argp, (char **)argp, 10);
3409     if (**argp == '/')
3410     {
3411 	mypos.lnum = (linenr_T)off;
3412 	++*argp;
3413 	mypos.col = strtol((char *)*argp, (char **)argp, 10);
3414 #ifdef FEAT_VIRTUALEDIT
3415 	mypos.coladd = 0;
3416 #endif
3417 	return &mypos;
3418     }
3419     return off2pos(buf, off);
3420 }
3421 
3422 
3423 /*
3424  * Convert (lnum,col) to byte offset in the file.
3425  */
3426     static long
3427 pos2off(buf_T *buf, pos_T *pos)
3428 {
3429     long	 offset = 0;
3430 
3431     if (!(buf->b_ml.ml_flags & ML_EMPTY))
3432     {
3433 	if ((offset = ml_find_line_or_offset(buf, pos->lnum, 0)) < 0)
3434 	    return 0;
3435 	offset += pos->col;
3436     }
3437 
3438     return offset;
3439 }
3440 
3441 
3442 /*
3443  * This message is printed after NetBeans opens a new file. Its
3444  * similar to the message readfile() uses, but since NetBeans
3445  * doesn't normally call readfile, we do our own.
3446  */
3447     static void
3448 print_read_msg(nbbuf_T *buf)
3449 {
3450     int	    lnum = buf->bufp->b_ml.ml_line_count;
3451     off_t   nchars = buf->bufp->b_orig_size;
3452     char_u  c;
3453 
3454     msg_add_fname(buf->bufp, buf->bufp->b_ffname);
3455     c = FALSE;
3456 
3457     if (buf->bufp->b_p_ro)
3458     {
3459 	STRCAT(IObuff, shortmess(SHM_RO) ? _("[RO]") : _("[readonly]"));
3460 	c = TRUE;
3461     }
3462     if (!buf->bufp->b_start_eol)
3463     {
3464 	STRCAT(IObuff, shortmess(SHM_LAST) ? _("[noeol]")
3465 					       : _("[Incomplete last line]"));
3466 	c = TRUE;
3467     }
3468     msg_add_lines(c, (long)lnum, nchars);
3469 
3470     /* Now display it */
3471     vim_free(keep_msg);
3472     keep_msg = NULL;
3473     msg_scrolled_ign = TRUE;
3474     msg_trunc_attr(IObuff, FALSE, 0);
3475     msg_scrolled_ign = FALSE;
3476 }
3477 
3478 
3479 /*
3480  * Print a message after NetBeans writes the file. This message should be
3481  * identical to the standard message a non-netbeans user would see when
3482  * writing a file.
3483  */
3484     static void
3485 print_save_msg(nbbuf_T *buf, off_t nchars)
3486 {
3487     char_u	c;
3488     char_u	*p;
3489 
3490     if (nchars >= 0)
3491     {
3492 	/* put fname in IObuff with quotes */
3493 	msg_add_fname(buf->bufp, buf->bufp->b_ffname);
3494 	c = FALSE;
3495 
3496 	msg_add_lines(c, buf->bufp->b_ml.ml_line_count,
3497 						buf->bufp->b_orig_size);
3498 
3499 	vim_free(keep_msg);
3500 	keep_msg = NULL;
3501 	msg_scrolled_ign = TRUE;
3502 	p = msg_trunc_attr(IObuff, FALSE, 0);
3503 	if ((msg_scrolled && !need_wait_return) || !buf->initDone)
3504 	{
3505 	    /* Need to repeat the message after redrawing when:
3506 	     * - When reading from stdin (the screen will be cleared next).
3507 	     * - When restart_edit is set (otherwise there will be a delay
3508 	     *   before redrawing).
3509 	     * - When the screen was scrolled but there is no wait-return
3510 	     *   prompt. */
3511 	    set_keep_msg(p, 0);
3512 	}
3513 	msg_scrolled_ign = FALSE;
3514 	/* add_to_input_buf((char_u *)"\f", 1); */
3515     }
3516     else
3517     {
3518 	char_u msgbuf[IOSIZE];
3519 
3520 	vim_snprintf((char *)msgbuf, IOSIZE,
3521 		_("E505: %s is read-only (add ! to override)"), IObuff);
3522 	nbdebug(("    %s\n", msgbuf));
3523 	emsg(msgbuf);
3524     }
3525 }
3526 
3527 #endif /* defined(FEAT_NETBEANS_INTG) */
3528