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