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