1 /* vi:set ts=8 sts=4 sw=4:
2  *
3  * Copyright (C) 2004 Xavier de Gaye.
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2, or (at your option)
8  * any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program (see the file COPYING); if not, write to the
17  * Free Software Foundation, Inc.,
18  * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
19  *
20  * $Id: netbeans.c 217 2008-10-11 14:29:18Z xavier $
21  */
22 
23 #include <config.h>
24 
25 #include <sys/types.h>
26 #include <sys/socket.h>
27 #include <netinet/in.h>
28 #include <netdb.h>
29 #include <arpa/inet.h>
30 
31 #include "obstack.h"
32 #include "clewn.h"
33 #include "gdb.h"
34 #include "misc.h"
35 
36 #define NETBEANS_REQSTED_VERSION "2.1"
37 #define NETBEANS_SPECIALKEYS_VERSION "2.3"
38 #define NETBEANS_GETANNO_VERSION "2.4"
39 #define NETBEANS_CLOSE_BUGFIXED_VERSION "2.4"
40 
41 #define BUFF_ALLOC_INCREMENT	    20
42 #define MAXMSGSIZE		    4096
43 
44 /* NetBeans parsed events */
45 #define EVT_ERROR		-1
46 #define EVT_BALLOONEVAL		0
47 #define EVT_BALLOONTEXT		1
48 #define EVT_FILEOPENED		2
49 #define EVT_GEOMETRY		3
50 #define EVT_INSERT		4
51 #define EVT_KEYCOMMAND		5
52 #define EVT_KEYATPOS		6
53 #define EVT_KILLED		7
54 #define EVT_NEWDOTANDMARK	8
55 #define EVT_REMOVE		9
56 #define EVT_SAVE		10
57 #define EVT_STARTUPDONE		11
58 #define EVT_UNMODIFIED		12
59 #define EVT_VERSION		13
60 
61 typedef struct cnbl_struct cnbl_T;
62 
63 /*
64  * The code handling the 'txt' content of a buffer assumes:
65  *
66  *	- not much editing is done by a Vim user on a file: a user
67  *	  usually removes a line in the variables file (we may realloc
68  *	  for just one character inserted, sending this char over TCP
69  *	  is already using resources anyway)
70  *	- a NetBeans 'insert' or 'remove' event is atomic and operates
71  *	  on one line
72  *	- a new line is inserted by Vim with an 'insert' event containing
73  *	  only the new line character
74  *
75  * No effort is made to handle race conditions when "insert" or "remove"
76  * events are sent by the editor while we are sending an "insert" or "remove"
77  * function: the NetBeans protocol cannot guarantee that events are
78  * not queued in the network, so this would be difficult to cleanly
79  * manage anyway.
80  */
81 
82 /* NetBeans buffer states */
83 #define BUFS_INITDONE	0x01
84 #define BUFS_KILLED	0x02
85 #define BUFS_ASM	0x04
86 
87 /* The Netbeans buffer structure */
88 typedef struct
89 {
90     char *name;		/* buffer file name */
91     int state;		/* buffer state */
92     cnbl_T *txt;	/* buffer content: a list of cnbl_T lines */
93     int lastsign;	/* last sign type number */
94 } cnbuf_T;
95 
96 /* The line structure */
97 struct cnbl_struct
98 {
99     char *line;		/* the line */
100     int len;		/* line length */
101     cnbl_T *next;	/* next line */
102 };
103 
104 typedef struct
105 {
106     char * gdb_path;
107     char * vim_path;
108 } pathmap_T;
109 
110 /* NetBeans states */
111 #define NBS_INIT	0x00
112 #define NBS_AUTH	0x01
113 #define NBS_VERSION	0x02
114 #define NBS_READY	0x04
115 #define NBS_DISCONN	0x08
116 #define NBS_CLOSING	0x10
117 #define NBS_SPECIALKEYS	0x20
118 #define NBS_GETANNO	0x40
119 #define NBS_CLOSE_FIX	0x80
120 
121 /* The Clewn NetBeans structure */
122 typedef struct
123 {
124     int instance;	/* gdb instance number */
125     int state;		/* NetBeans state */
126     char *passwd;	/* non allocated NetBeans password */
127     int debug;		/* TRUE debugging info enabled */
128     pathmap_T * remote_map; /* array of path mappings, when remote debugging */
129     int fdcon;		/* socket over which the connection is accepted */
130     int fdata;		/* socket over which NetBeans messages are exchanged */
131     int seqno;		/* current command or function sequence number */
132     int lastbuf;	/* last buffer number (buffer numbers start at 1) */
133     char *line;		/* partial line received from NetBeans */
134     cnbuf_T *cnbuf;	/* buffer list */
135     int cnbuf_size;	/* buffer list current size */
136     int fr_buf;		/* frame sign buffer number */
137     int cur_buf;	/* current buffer number */
138     int cur_line;	/* current buffer line number */
139 
140     /* the variables data */
141     int * pvar_buf;	/* variables buffer number */
142     int modified;	/* TRUE when var_buf is modified */
143     int first_noaq;	/* first function seqno not acknowledged for var_buf */
144     int last_noaq;	/* last function seqno not acknowledged for var_buf */
145 } cnb_T;
146 
147 static cnb_T *cnb;		    /* the cnb_T instance */
148 static nb_event_T cnb_event;	    /* netbeans event */
149 static char cnb_buf[MAXMSGSIZE];    /* buffer for reading NetBeans messages */
150 
151 /* Forward declarations */
152 static int parse_msg __ARGS((char *, struct obstack *));
153 static int conn_setup __ARGS((char *, struct obstack *));
154 static int get_evt __ARGS((char *, int *, char **));
155 static void send_cmd __ARGS((int, char *, char *));
156 static void send_function __ARGS((int, char *, char *));
157 static pathmap_T * pm_parse __ARGS((char *));
158 static char * pm_mapto_vim __ARGS((char *, char *, char *, char *, struct obstack *));
159 static char * pm_mapto_gdb __ARGS((char *, struct obstack *));
160 static char * unquote __ARGS((char *, char **, struct obstack *));
161 static char * quote __ARGS((char *, struct obstack *));
162 static cnbuf_T * lowlevel_buf __ARGS((int));
163 static cnbuf_T * getbuf __ARGS((int));
164 static int edit_file __ARGS((int, struct obstack *));
165 static cnbl_T ** line_get __ARGS((int, int, int *));
166 static void line_insert __ARGS((int, int, char *));
167 static void line_remove __ARGS((int, int, int));
168 static void line_debug __ARGS((int));
169 
170 /*
171  * Set socket fdcon to listen for connections.
172  * When the variables file name is not NULL, create a buffer for this file
173  * and set the buffer number pointed to by pvar_buf.
174  * Return FAIL when error.
175  */
176     int
cnb_open(arg,pvar_buf,debug,reuse_addr,pathnames_map)177 cnb_open(arg, pvar_buf, debug, reuse_addr, pathnames_map)
178     char *arg;		/* netbeans connection parameters */
179     int *pvar_buf;	/* pointer to buffer number of variables file name */
180     int debug;		/* TRUE when debug mode */
181     int reuse_addr;	/* TRUE when socket option SO_REUSEADDR */
182     char * pathnames_map;/* pathnames_map when remote debugging */
183 {
184     char *hostname;
185     char *address;
186     char *password;
187     struct sockaddr_in local_add;
188     struct hostent *host;
189     int port;
190     int flags;
191 
192     /* create the cnb instance */
193     cnb = (cnb_T *)xcalloc(sizeof(cnb_T));
194     cnb->fdcon	    = -1;
195     cnb->fdata	    = -1;
196     cnb->pvar_buf   = pvar_buf;
197     cnb->debug	    = debug;
198     cnb->remote_map = pm_parse(pathnames_map);
199 
200     /* arg is NULL or <host>:<addr>:<password> */
201     hostname = arg;
202     if (hostname == NULL || *hostname == '\0')
203 	hostname = getenv("__NETBEANS_HOST");
204     if (hostname == NULL || *hostname == '\0')
205 	hostname = "localhost"; /* default */
206 
207     address = strchr(hostname, ':');
208     if (address != NULL)
209 	*address++ = '\0';
210     else
211 	address = getenv("__NETBEANS_SOCKET");
212     if (address == NULL || *address == '\0')
213 	address = "3219";  /* default */
214 
215     password = strchr(address, ':');
216     if (password != NULL)
217 	*password++ = '\0';
218     else
219 	password = getenv("__NETBEANS_VIM_PASSWORD");
220     if (password == NULL || *password == '\0')
221 	password = "changeme"; /* default */
222     cnb->passwd = password;
223 
224     /* create the socket */
225     if ((cnb->fdcon = socket(AF_INET, SOCK_STREAM, 0)) == -1)
226     {
227 	perror("socket() failed in cnb_listen()");
228 	return FAIL;
229     }
230 
231     /* set this socket as non blocking */
232     flags = fcntl(cnb->fdcon, F_GETFL, 0);
233     if (flags != -1)
234     {
235 	flags |= O_NONBLOCK;
236 	flags = fcntl(cnb->fdcon, F_SETFL, flags);
237     }
238     if (flags == -1)
239     {
240 	perror("fcntl() failed in cnb_listen()");
241 	goto fail;
242     }
243 
244     /* build the local address */
245     memset((void *)&local_add, 0, sizeof(local_add));
246     port = atoi(address);
247     local_add.sin_family = AF_INET;
248     local_add.sin_port = htons(port);
249     if ((host = gethostbyname(hostname)) == NULL)
250     {
251 	fprintf(stderr, "gethostbyname() failed in cnb_listen() with h_errno: %d\n", h_errno);
252 	goto fail;
253     }
254     memcpy((void *)&local_add.sin_addr, host->h_addr, (size_t)host->h_length);
255 
256     /* set socket option SO_REUSEADDR */
257     if (reuse_addr)
258     {
259 	int on = 1;
260 	setsockopt(cnb->fdcon, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
261     }
262 
263     /* bind the local address to the socket */
264     if (bind(cnb->fdcon, (struct sockaddr *)&local_add, sizeof(local_add)) == -1)
265     {
266 	perror("bind() failed in cnb_listen()");
267 	fprintf(stderr,"check the network\n");
268 	if (! reuse_addr)
269 	{
270 	    fprintf(stderr,"    possibly waiting for previous tcp connection to gracefully shutdown\n");
271 	    fprintf(stderr,"    next time: do not use -r option to avoid waiting\n");
272 	    fprintf(stderr,"    now: wait or use another port\n");
273 	}
274 	goto fail;
275     }
276 
277     /* we are willing to accept connections on this socket */
278     if (listen(cnb->fdcon, 5) == -1 )
279     {
280 	perror("listen() failed in cnb_listen()");
281 	goto fail;
282     }
283 
284     fprintf(stderr, "\nNetBeans listens on %s:%s\n\n", hostname, address);
285     return OK;
286 fail:
287     close(cnb->fdcon);
288     cnb->fdcon = -1;
289     fprintf(stderr, "hostname: %s, port: %s\n", hostname, address);
290     return FAIL;
291 }
292 
293 #define DETACH_MSG    "DETACH\n"
294 /* Close NetBeans sockets */
295     void
cnb_close()296 cnb_close()
297 {
298     struct obstack obs;	/* use an obstack for temporary allocated memory */
299     cnbuf_T * buf;
300     cnbl_T *l;
301     cnbl_T *next;
302     pathmap_T * m;
303     int i;
304 
305     if (cnb == NULL || (cnb->state & NBS_CLOSING))
306 	return;
307     cnb->state |= NBS_CLOSING;
308 
309     (void)obstack_init(&obs);
310 
311     /* free the allocated cnb_event fields */
312     FREE(cnb_event.key);
313     FREE(cnb_event.lnum);
314     FREE(cnb_event.pathname);
315     FREE(cnb_event.text);
316     cnb_event.text_event = FALSE;
317     cnb_event.seqno = 0;
318 
319     if (cnb->fdcon != -1)
320     {
321 	close(cnb->fdcon);
322 	cnb->fdcon = -1;
323     }
324     if (cnb->fdata != -1)
325     {
326 	fprintf(stderr, "NetBeans data socket disconnected\n");
327 	cnb_showBalloon("NetBeans connection to Clewn has been closed", FALSE, &obs);
328 
329 	/* fussy: this write may be interrupted by a SIGPIPE which prevents
330 	 * the cnb structure to be freed since state is NBS_CLOSING
331 	 * and we never return from the write in this case */
332 	if (! (cnb->state & NBS_DISCONN))
333 	    write(cnb->fdata, DETACH_MSG, strlen(DETACH_MSG));
334 	shutdown(cnb->fdata, 2);	/* sends and receives are disallowed */
335 	close(cnb->fdata);
336 	cnb->fdata = -1;
337     }
338 
339     /* free memory allocated by each buffer */
340     for (i = 0; i < cnb->cnbuf_size; i++)
341     {
342 	buf = cnb->cnbuf + i;
343 	xfree(buf->name);
344 
345 	/* free all lines */
346 	for (l= buf->txt; l != NULL; l = next)
347 	{
348 	    next = l->next;
349 	    xfree(l->line);
350 	    xfree(l);
351 	}
352     }
353 
354     obstack_free(&obs, NULL);
355 
356     /* free the pathmap array */
357     for (m=cnb->remote_map; m; m++) {
358 	if (m->gdb_path == NULL)
359 	    break;
360 	xfree(m->gdb_path);
361 	xfree(m->vim_path);
362     }
363     FREE(cnb->remote_map);
364 
365     xfree(cnb->line);
366     xfree(cnb->cnbuf);
367     FREE(cnb);
368 }
369 
370 /* Perform the equivalent of closing Vim: ":confirm qall" */
371     void
cnb_saveAndExit()372 cnb_saveAndExit()
373 {
374     send_cmd(0, "raise", NULL);
375     send_function(0, "saveAndExit", NULL);
376 }
377 
378 /* Return TRUE when the data socket is opened */
379     int
cnb_state()380 cnb_state()
381 {
382     return (cnb != NULL && cnb->fdata != -1 && cnb->state & NBS_READY);
383 }
384 
385 /* Return TRUE when we are running a netbeans version that supports specialKeys */
386     int
cnb_specialKeys()387 cnb_specialKeys()
388 {
389     return (cnb->state & NBS_SPECIALKEYS);
390 }
391 
392 /* Map in vim a key with the corresponding type */
393     void
cnb_keymap(type,key)394 cnb_keymap(type, key)
395     int type;
396     int key;
397 {
398     char arg[] = "\" - \"";
399 
400     switch (type) {
401 	case KEYMAP_CONTROL:
402 	    arg[1] = 'C';
403 	    arg[3] = key;
404 	    break;
405 	case KEYMAP_UPPERCASE:
406 	    arg[1] = 'S';
407 	    arg[3] = key;
408 	    break;
409 	case KEYMAP_FUNCTION:
410 	    arg[1] = 'F';
411 	    if (key >= 10) {
412 		arg[2] = key / 10 + '0';
413 		arg[3] = key % 10 + '0';
414 	    }
415 	    else
416 		arg[2] = key % 10 + '0';
417 	    break;
418 	default:
419 	    return;
420     }
421     send_cmd(0, "specialKeys", arg);
422 }
423 
424 /* Create or empty the variables buffer */
425     void
cnb_create_varbuf(var_name)426 cnb_create_varbuf(var_name)
427     char *var_name;	/* variables file name */
428 {
429     struct obstack obs;	/* use an obstack for temporary allocated memory */
430     cnbuf_T *buf;
431 
432     if (cnb == NULL || cnb->pvar_buf == NULL || var_name == NULL)
433 	return;
434 
435     /* create or empty the variables buffer if not remote debugging */
436     if (! cnb->remote_map) {
437 	if ((buf = getbuf(*cnb->pvar_buf)) != NULL) {	/* empty the buffer */
438 	    (void)obstack_init(&obs);
439 	    cnb_clear(*cnb->pvar_buf, &obs);
440 	    obstack_free(&obs, NULL);
441 	}
442 	else						/* create it */
443 	    *cnb->pvar_buf = cnb_create_buf(var_name);
444     }
445 }
446 
447 /* set the gdb instance number (used in cnb_define_sign) */
448     void
cnb_set_instance(instance)449 cnb_set_instance(instance)
450     int instance;
451 {
452     if (cnb == NULL)
453 	return;
454 
455     cnb->instance = instance;
456 }
457 
458 /* Return the socket over which the connection is accepted */
459     int
cnb_get_connsock()460 cnb_get_connsock()
461 {
462     if (cnb == NULL)
463 	return -1;
464     return cnb->fdcon;
465 }
466 
467 /* Return the socket over which NetBeans messages are exchanged */
468     int
cnb_get_datasock()469 cnb_get_datasock()
470 {
471     if (cnb == NULL)
472 	return -1;
473     return cnb->fdata;
474 }
475 
476 /* Handle an event on the connection socket */
477     void
cnb_conn_evt()478 cnb_conn_evt()
479 {
480     struct sockaddr_in from;
481     char *remote;
482     int port;
483     socklen_t fromlen;
484     int s;
485 
486     if (cnb == NULL || cnb->fdcon == -1)
487 	return;
488 
489     fromlen = (socklen_t) sizeof(from);
490 
491     /* accept the connection */
492     if ((s = accept(cnb->fdcon, (struct sockaddr *)&from, &fromlen)) != -1)
493     {
494 	remote = inet_ntoa(from.sin_addr);
495 	port = (int)ntohs(from.sin_port);
496 
497 	if (cnb->fdata == -1)
498 	{
499 	    cnb->fdata = s;	/* data socket connected */
500 	    close(cnb->fdcon);
501 	    cnb->fdcon = -1;	/* connection socket not needed anymore */
502 	    fprintf(stderr, "NetBeans connected to %s:%d\n\n", remote, port);
503 	}
504 	else
505 	{
506 	    close(s);	/* reject connection, we already have a data socket */
507 	    fprintf(stderr, "connection attempt from %s:%d failed\n", remote, port);
508 	}
509     }
510     else if (errno != EINTR && errno != EWOULDBLOCK && errno != EAGAIN)
511 	perror("accept() failed in cnb_conn_evt()");
512 }
513 
514 /*
515  * Handle data received on the data socket.
516  * Return a pointer to a static nb_event_T structure,
517  * which is used when one of the NetBeans messages
518  * in the data received is EVT_KEYATPOS or EVT_BALLOONTEXT
519  */
520     nb_event_T *
cnb_data_evt()521 cnb_data_evt()
522 {
523     struct obstack obs;	/* use an obstack for temporary allocated memory */
524     char *evt;
525     char *start;
526     char *end;
527     int len;
528 
529     if (cnb == NULL || cnb->fdata == -1)
530 	return NULL;
531 
532     /* key has been consumed */
533     FREE(cnb_event.key);
534 
535     if ((len = read(cnb->fdata, cnb_buf, MAXMSGSIZE - 1)) == 0)
536     {
537 	fprintf(stderr, "disconnected by editor\n");
538 	cnb->state |= NBS_DISCONN;
539 	cnb_close();
540 	return NULL;
541     }
542     else if (len < 0 && errno != EINTR)
543     {
544 	perror("read() failed in cnb_data_evt()");
545 	cnb_close();
546 	return NULL;
547     }
548     else if (len > 0)
549     {
550 	cnb_buf[len] = NUL;
551 	if (cnb->debug)
552 	    fprintf(stderr, "%s", cnb_buf);
553 
554 	(void)obstack_init(&obs);
555 
556 	/* add to previous partial line */
557 	obstack_strcat(&obs, cnb->line);
558 	obstack_strcat0(&obs, cnb_buf);
559 	evt = (char *)obstack_finish(&obs);
560 
561 	/* handle multiple NetBeans messages */
562 	for (start = evt; *start; start = end)
563 	{
564 	    if ((end = strchr(start, (int)'\n')) != NULL)
565 	    {
566 		*end++ = NUL;
567 		if (parse_msg(start, &obs) == FAIL)
568 		{
569 		    FREE(cnb->line);
570 		    obstack_free(&obs, NULL);
571 		    cnb_close();
572 		    return NULL;
573 		}
574 	    }
575 	    else
576 		break;
577 	}
578 
579 	if (*start != NUL)	    /* a partial line */
580 	{
581 	    xfree(cnb->line);
582 	    cnb->line = clewn_strsave(start);
583 	    obstack_free(&obs, NULL);
584 
585 	    return &cnb_event;
586 	}
587 	FREE(cnb->line);
588 	obstack_free(&obs, NULL);
589     }
590 
591     return &cnb_event;
592 }
593 
594 /*
595  * Parse a NetBeans event of the form: 'bufid:event=seqno args'
596  * Accept also the authentication message: 'AUTH password'
597  * Return FAIL when the error is fatal and requires closing the connection.
598  */
599     static int
parse_msg(event,obs)600 parse_msg(event, obs)
601     char *event;
602     struct obstack *obs;
603 {
604     cnbuf_T *buf;
605     char *args;
606     char *pathname;
607     char *next;
608     char *end;
609     char *tmp;
610     int bufno;
611     int offset;
612 
613     /* parse NetBeans connection setup events */
614     if (! (cnb->state & NBS_READY))
615 	return conn_setup(event, obs);
616 
617     /* NetBeans ready and running */
618     cnb_event.seqno = (int)strtol(event, &tmp, 10);
619     next = tmp;
620 
621     /*
622      * A reply to a function.
623      */
624     if (next != event && (*next == ' ' || *next == NUL))
625     {
626 	if (*next == ' ')
627 	    next++;
628 	FREE(cnb_event.text);
629 	cnb_event.text_event = FALSE;
630 	cnb_event.text = clewn_strsave(next);
631 
632 	/* handle acknowledgements for var_buf */
633 	if ((buf = getbuf(*cnb->pvar_buf)) != NULL && cnb_event.seqno != 0
634 		&& cnb_event.seqno >= cnb->first_noaq && cnb_event.seqno <= cnb->last_noaq)
635 	{
636 	    if (*(next + 1) == '!')
637 	    {
638 		send_cmd(*cnb->pvar_buf, "stopDocumentListen", NULL);
639 		buf->state |= BUFS_KILLED;
640 		fprintf(stderr,
641 			"error updating variables, the buffer has been killed\n");
642 	    }
643 
644 	    /* all functions acknowledged */
645 	    if (cnb_event.seqno == cnb->last_noaq)
646 	    {
647 		cnb->first_noaq = 0;
648 		cnb->last_noaq = 0;
649 	    }
650 	}
651 	return OK;
652     }
653 
654     /*
655      * An event.
656      */
657     switch (get_evt(event, &bufno, &args))
658     {
659 	/*
660 	 * `fileOpened pathname open modified'
661 	 *	open	    boolean   'T' always
662 	 *	modified    boolean   'F' always
663 	 */
664 	case EVT_FILEOPENED:
665 	    if ((pathname = unquote(args, &next, obs)) != NULL)
666 	    {
667 		/* unvalidate balloon text */
668 		FREE(cnb_event.text);
669 
670 		if (*next == ' ' && *(next + 1) == 'T'
671 			&& strcmp(pathname, "(null)") != 0)
672 		{
673 		    *next = NUL;    /* terminate quoted pathname */
674 
675 		    /* create the buffer
676 		     * even when we already know it (this is historical as in previous
677 		     * versions of Vim, we got fileOpened only for buffers clewn created,
678 		     * it does not hurt anyway to record all buffers ever loaded by Vim
679 		     * and it is necessary now to learn about var_buf when doing remote
680 		     * debugging and var_buf is created remotely) */
681 		    if ((bufno = cnb_create_buf(pathname)) != -1)
682 		    {
683 			/* send to the editor the new bufID */
684 			send_cmd(bufno, "putBufferNumber", args);
685 
686 			/* learn about var_buf when it is created remotely */
687 			if (*cnb->pvar_buf == -1 && (tmp=strstr(pathname, VARIABLES_FNAME)) != NULL
688 				&& strcmp(tmp, VARIABLES_FNAME) == 0)
689 			    *cnb->pvar_buf = bufno;
690 
691 			/* keep listening on changes to var_buf */
692 			if (bufno != *cnb->pvar_buf)
693 			    send_cmd(bufno, "stopDocumentListen", NULL);
694 
695 			/* define the frame sign */
696 			(void)cnb_define_sign(bufno, FRAME_SIGN, SIGN_ANY, obs);
697 
698 			/* set buffer as initialized */
699 			if ((buf = getbuf(bufno)) != NULL)
700 			    buf->state |= BUFS_INITDONE;
701 		    }
702 		}
703 		return OK;
704 	    }
705 
706 	    if (cnb->debug)
707 		fprintf(stderr, "parse error in EVT_FILEOPENED in parse_msg()\n");
708 	    break;
709 
710 	    break;
711 
712 	/*
713 	 * `balloonText text'
714 	 *
715 	 * Used when 'ballooneval' is set and the mouse pointer rests
716 	 * on some text for a moment
717 	 */
718 	case EVT_BALLOONTEXT:
719 	    FREE(cnb_event.text);
720 	    if ((args = unquote(args, &next, obs)) == NULL)
721 	    {
722 		if (cnb->debug)
723 		    fprintf(stderr, "parse error in EVT_BALLOONTEXT in parse_msg()\n");
724 	    }
725 	    else {
726 		cnb_event.text = clewn_strsave(args);
727 		cnb_event.text_event = TRUE;
728 	    }
729 	    break;
730 
731 	/*
732 	 * `keyAtPos keyName offset lnum/col'
733 	 *
734 	 * Reports a special key being pressed with name "keyName"
735 	 * and also reports the line number and column of the cursor
736 	 */
737 	case EVT_KEYATPOS:
738 	    FREE(cnb_event.key);
739 	    if ((args = unquote(args, &next, obs)) != NULL)
740 	    {
741 		cnb_event.key = clewn_strsave(args);
742 
743 		/* skip offset and parse line number */
744 		if (*next++ == ' ' && (next = strchr(next, ' ')) != NULL
745 			&& (end = strchr(++next, '/')) != NULL)
746 		{
747 		    *end = NUL;
748 
749 		    /* get buffer name */
750 		    if ((buf = getbuf(bufno)) != NULL)
751 		    {
752 			xfree(cnb_event.lnum);
753 			cnb_event.lnum = clewn_strsave(next);
754 
755 			if (cnb->debug)
756 			    fprintf(stderr, "vim source file name: \"%s\"\n", buf->name);
757 
758 			xfree(cnb_event.pathname);
759 			if (cnb->remote_map)
760 			    cnb_event.pathname = pm_mapto_gdb(buf->name, obs);
761 			else
762 			    cnb_event.pathname = clewn_strsave(buf->name);
763 
764 			return OK;
765 		    }
766 		}
767 		FREE(cnb_event.key);
768 	    }
769 
770 	    if (cnb->debug)
771 		fprintf(stderr, "parse error in EVT_KEYATPOS in parse_msg()\n");
772 	    break;
773 
774 	/* buffer is wiped out and can't be reused */
775 	case EVT_KILLED:
776 	    if ((buf = getbuf(bufno)) != NULL)
777 	    {
778 		buf->state |= BUFS_KILLED;
779 		FREE(buf->name);
780 
781 		/* remove in bpinfo list the records corresponding to the signs in buf,
782 		 * the signs themselves are removed by Vim in free_buffer() */
783 		gdb_free_records(bufno);
784 	    }
785 	    break;
786 
787 	/* Text "next" has been inserted in Vim at position "offset" */
788 	case EVT_INSERT:
789 	    offset = (int)strtol(args, &tmp, 10);
790 	    next = tmp;
791 
792 	    if (next != args && *next++ == ' ' && *next == '"'
793 		    && (next = unquote(next, NULL, obs)) != NULL)
794 	    {
795 		line_insert(bufno, offset, next);
796 		if (cnb->debug)
797 		    line_debug(bufno);
798 	    }
799 	    else
800 	    {
801 		if (cnb->debug)
802 		    fprintf(stderr, "parse error in EVT_INSERT in parse_msg()\n");
803 	    }
804 	    break;
805 
806 	/* Text was deleted in Vim at position "offset" with byte length "len" */
807 	case EVT_REMOVE:
808 	    offset = (int)strtol(args, &tmp, 10);
809 	    next = tmp;
810 
811 	    if (next != args && *next++ == ' ')
812 	    {
813 		line_remove(bufno, offset, atoi(next));
814 		if (cnb->debug)
815 		    line_debug(bufno);
816 	    }
817 	    else
818 	    {
819 		if (cnb->debug)
820 		    fprintf(stderr, "parse error in EVT_REMOVE in parse_msg()\n");
821 	    }
822 	    break;
823 
824 	case EVT_UNMODIFIED:
825 	    if (bufno == *cnb->pvar_buf)
826 		cnb->modified = FALSE;
827 	    break;
828 
829 	/* ignored events */
830 	case EVT_KEYCOMMAND:	/* use instead EVT_KEYATPOS */
831 	case EVT_NEWDOTANDMARK:	/* use instead EVT_KEYATPOS */
832 	case EVT_GEOMETRY:
833 	case EVT_BALLOONEVAL:
834 	case EVT_SAVE:
835 	    break;
836 
837 	default:
838 	    if (cnb->debug)
839 		fprintf(stderr, "discarded event in parse_msg()\n");
840 	    break;
841     }
842     return OK;
843 }
844 
845 #define AUTH_MSG    "AUTH "
846 /* Parse messages received during connection setup
847  * Return FAIL when the error is fatal and requires closing the connection.
848  */
849     static int
conn_setup(event,obs)850 conn_setup(event, obs)
851     char *event;
852     struct obstack *obs;
853 {
854     char *args;
855     char *version;
856     int bufno;
857 
858     /* password: no quotes are used! */
859     if (strstr(event, AUTH_MSG) == event)
860     {
861 	if (strcmp(event + strlen(AUTH_MSG), cnb->passwd) == 0)
862 	    cnb->state |= NBS_AUTH;
863 	else
864 	{
865 	    fprintf(stderr, "got invalid NetBeans password\n");
866 	    return FAIL;
867 	}
868 	return OK;
869     }
870 
871     switch (get_evt(event, &bufno, &args))
872     {
873 	/* version including the quotes */
874 	case EVT_VERSION:
875 	    if ((args = unquote(args, NULL, obs)) != NULL)
876 	    {
877 		version = clewn_stripwhite(args);
878 		if (strcmp(version, NETBEANS_REQSTED_VERSION) >= 0)
879 		{
880 		    cnb->state |= NBS_VERSION;
881 		    if (strcmp(version, NETBEANS_SPECIALKEYS_VERSION) >= 0)
882 			cnb->state |= NBS_SPECIALKEYS;
883 		    if (strcmp(version, NETBEANS_GETANNO_VERSION) >= 0)
884 			cnb->state |= NBS_GETANNO;
885 		    if (strcmp(version, NETBEANS_CLOSE_BUGFIXED_VERSION) >= 0)
886 			cnb->state |= NBS_CLOSE_FIX;
887 
888 		    return OK;
889 		}
890 	    }
891 	    fprintf(stderr, "connection rejected: need NetBeans version greater than %s\n",
892 		    NETBEANS_REQSTED_VERSION);
893 	    return FAIL;
894 
895 	/* editor has finished its startup work and is ready for editing files */
896 	case EVT_STARTUPDONE:
897 	    if (cnb->state & NBS_AUTH && cnb->state & NBS_VERSION)
898 	    {
899 		cnb->state |= NBS_READY;
900 		send_cmd(0, "setExitDelay", "0"); /* don't need an exit delay */
901 
902 		if (cnb->debug)
903 		    fprintf(stderr, "NBS_READY in conn_setup()\n");
904 	    }
905 	    else
906 	    {
907 		fprintf(stderr, "connection rejected: missing password or version\n");
908 		return FAIL;
909 	    }
910 	    break;
911 
912 	default:
913 	    if (cnb->debug)
914 		fprintf(stderr, "discarded event in conn_setup()\n");
915 	    break;
916     }
917     return OK;
918 }
919 
920 /*
921  * Parse a received event: 'bufid:event=seqno args'
922  * seqno is checked for validity
923  * *parg is set to NULL when there is no argument
924  */
925     static int
get_evt(event,pbuf,parg)926 get_evt(event, pbuf, parg)
927     char *event;	/* event to parse */
928     int *pbuf;		/* pointer to a bufID */
929     char **parg;	/* pointer to event arguments */
930 {
931     int id = EVT_ERROR;	/* event id */
932     char *name;		/* event name */
933     char *ptr;
934     char *tmp;
935     int seqno;
936 
937     if (event != NULL && (name = strchr(event, ':')) != NULL)
938     {
939 	*name++ = NUL;
940 	*pbuf = (int)strtol(event, &tmp, 10);
941 	ptr = tmp;
942 
943 	/* check bufID validity */
944 	if (*ptr != NUL || *pbuf < 0 || *pbuf > cnb->lastbuf)
945 	{
946 	    if (cnb->debug)
947 		fprintf(stderr, "invalid bufID in get_evt()\n");
948 	    return EVT_ERROR;
949 	}
950 
951 	if ((ptr = strchr(name, '=')) != NULL)
952 	{
953 	    *ptr++ = NUL;
954 	    seqno = atoi(ptr);
955 
956 	    /* check seqno validity */
957 	    if (seqno != 0 && (seqno > cnb->seqno || seqno < 0))
958 	    {
959 		fprintf(stderr, "invalid seqno in get_evt()\n");
960 		return EVT_ERROR;
961 	    }
962 
963 	    /* strip <SPACE> at arguments start */
964 	    if ((*parg = strchr(ptr, ' ')) != NULL)
965 		while (**parg == ' ')
966 		    (*parg)++;
967 
968 	    /* get event id */
969 	    if (strcmp(name, "balloonText") == 0)
970 		id = EVT_BALLOONTEXT;
971 	    else if (strcmp(name, "fileOpened") == 0)
972 		id = EVT_FILEOPENED;
973 	    else if (strcmp(name, "balloonEval") == 0)
974 		id = EVT_BALLOONEVAL;
975 	    else if (strcmp(name, "geometry") == 0)
976 		id = EVT_GEOMETRY;
977 	    else if (strcmp(name, "insert") == 0)
978 		id = EVT_INSERT;
979 	    else if (strcmp(name, "keyCommand") == 0)
980 		id = EVT_KEYCOMMAND;
981 	    else if (strcmp(name, "keyAtPos") == 0)
982 		id = EVT_KEYATPOS;
983 	    else if (strcmp(name, "killed") == 0)
984 		id = EVT_KILLED;
985 	    else if (strcmp(name, "newDotAndMark") == 0)
986 		id = EVT_NEWDOTANDMARK;
987 	    else if (strcmp(name, "remove") == 0)
988 		id = EVT_REMOVE;
989 	    else if (strcmp(name, "save") == 0)
990 		id = EVT_SAVE;
991 	    else if (strcmp(name, "startupDone") == 0)
992 		id = EVT_STARTUPDONE;
993 	    else if (strcmp(name, "unmodified") == 0)
994 		id = EVT_UNMODIFIED;
995 	    else if (strcmp(name, "version") == 0)
996 		id = EVT_VERSION;
997 	    else
998 	    {
999 		if (cnb->debug)
1000 		    fprintf(stderr, "unknown event in get_evt()\n");
1001 	    }
1002 	}
1003     }
1004     return id;
1005 }
1006 
1007 /*
1008  * Send a NetBeans command.
1009  * ATTENTION: do not use the general purpose array cnb_buf[] for 'arg'
1010  */
1011     static void
send_cmd(bufno,cmd,arg)1012 send_cmd(bufno, cmd, arg)
1013     int bufno;	/* buffer id, generic messages use a bufID of zero */
1014     char *cmd;	/* command */
1015     char *arg;	/* command arguments, may be NULL */
1016 {
1017     if (cnb == NULL || cnb->fdata == -1 || ! (cnb->state & NBS_READY)
1018 	    || cmd == NULL || *cmd == NUL)
1019 	return;
1020 
1021     /* can't use allocated memory here as we may be called to "removeAnno"
1022      * when aborting after memory allocation failure */
1023     if (2 * NUMBUFLEN + strlen(cmd) + (arg != NULL ? (strlen(arg) + 1) : 0) + 3 + 1
1024 	    > MAXMSGSIZE)
1025 	return;
1026 
1027     if (arg != NULL)
1028 	sprintf(cnb_buf, "%d:%s!%d %s\n", bufno, cmd, ++(cnb->seqno), arg);
1029     else
1030 	sprintf(cnb_buf, "%d:%s!%d\n", bufno, cmd, ++(cnb->seqno));
1031     if (cnb->debug)
1032 	fprintf(stderr, cnb_buf);
1033 
1034     (void)write(cnb->fdata, cnb_buf, strlen(cnb_buf));
1035 }
1036 
1037 /*
1038  * Send a NetBeans function.
1039  * ATTENTION: do not use the general purpose array cnb_buf[] for 'arg'
1040  */
1041     static void
send_function(bufno,function,arg)1042 send_function(bufno, function, arg)
1043     int bufno;	    /* buffer id, generic messages use a bufID of zero */
1044     char *function; /* function */
1045     char *arg;	    /* command arguments, may be NULL */
1046 {
1047     if (cnb == NULL || cnb->fdata == -1 || ! (cnb->state & NBS_READY)
1048 	    || function == NULL || *function == NUL)
1049 	return;
1050 
1051     if (2 * NUMBUFLEN + strlen(function) + (arg != NULL ? (strlen(arg) + 1) : 0) + 3 + 1
1052 	    > MAXMSGSIZE)
1053 	return;
1054 
1055     if (arg != NULL)
1056 	sprintf(cnb_buf, "%d:%s/%d %s\n", bufno, function, ++(cnb->seqno), arg);
1057     else
1058 	sprintf(cnb_buf, "%d:%s/%d\n", bufno, function, ++(cnb->seqno));
1059     if (cnb->debug)
1060 	fprintf(stderr, cnb_buf);
1061 
1062     (void)write(cnb->fdata, cnb_buf, strlen(cnb_buf));
1063 
1064     /* handle acknowledgements for var_buf */
1065     if (bufno == *cnb->pvar_buf)
1066     {
1067 	if (cnb->first_noaq == 0)
1068 	    cnb->first_noaq = cnb->seqno;
1069 	cnb->last_noaq = cnb->seqno;
1070     }
1071 }
1072 
1073 /*
1074  * Send a command or function to NetBeans.
1075  * The format of line is: `bufID {command|function} arg1 arg2 ...'
1076  * bufID is a number
1077  */
1078     void
cnb_send_debug(type,line)1079 cnb_send_debug(type, line)
1080     int type;
1081     char *line;
1082 {
1083     char *token;
1084     char *args;
1085     char *tmp;
1086     int bufno;
1087 
1088     if (cnb == NULL || ! (cnb->state & NBS_READY))
1089 	return;
1090 
1091     bufno = (int)strtol(line, &tmp, 10);
1092     token = tmp;
1093 
1094     if ((bufno != 0 && getbuf(bufno) == NULL) || *token != ' ')
1095     {
1096 	fprintf(stderr, "invalid bufID in cnb_send_debug()\n");
1097 	return;
1098     }
1099 
1100     while (isspace(*token))
1101 	token++;
1102 
1103     if (*token == NUL)
1104     {
1105 	fprintf(stderr, "missing token in cnb_send_debug()\n");
1106 	return;
1107     }
1108 
1109     args = strchr(token, ' ');
1110     if (args != NULL)
1111     {
1112 	*args++ = NUL;
1113 	while (isspace(*args))
1114 	    args++;
1115 	if (*args == NUL)
1116 	    args = NULL;
1117     }
1118 
1119     if (type == DEBUG_ESC_CMD)		/* a command */
1120 	send_cmd(bufno, token, args);
1121     else if (type == DEBUG_ESC_FUN)	/* a function */
1122 	send_function(bufno, token, args);
1123 }
1124 
1125 #define MAX_BALLOON_SIZE 2000
1126 /* Show a balloon (popup window) at the mouse pointer position, containing "text". */
1127     void
cnb_showBalloon(text,doquote,obs)1128 cnb_showBalloon(text, doquote, obs)
1129     char *text;
1130     struct obstack *obs;
1131     int doquote;
1132 {
1133     char *quoted = NULL;
1134     char *wk_copy;
1135 
1136     if (text != NULL)
1137     {
1138 	wk_copy = obstack_strsave(obs, text);
1139 
1140 	if (strlen(wk_copy) > MAX_BALLOON_SIZE)
1141 	    strcpy(wk_copy + MAX_BALLOON_SIZE - 3, "...");
1142 
1143 	if (doquote)
1144 	    quoted = quote(wk_copy, obs);
1145 	else
1146 	    quoted = wk_copy;
1147 
1148 	/* Use bufID 1, even though it might not be defined or
1149 	 * might be 'killed' */
1150 	if (quoted != NULL)
1151 	    send_cmd(1, "showBalloon", quoted);
1152     }
1153 }
1154 
1155 /* Do a startAtomic command. */
1156     void
cnb_startAtomic(bufno)1157 cnb_startAtomic(bufno) int bufno; { send_cmd(bufno, "startAtomic", NULL); }
1158 
1159 /* Do a endAtomic command. */
1160     void
cnb_endAtomic(bufno)1161 cnb_endAtomic(bufno) int bufno; { send_cmd(bufno, "endAtomic", NULL); }
1162 
1163 /*
1164  * Edit a file and set cursor at lnum.
1165  * Return the buffer number or -1 when error.
1166  */
1167     int
cnb_editFile(name,lnum,sourcedir,source_cur,source_list,silent,obs)1168 cnb_editFile(name, lnum, sourcedir, source_cur, source_list, silent, obs)
1169     char *name;		/* file name */
1170     linenr_T lnum;	/* line number */
1171     char *sourcedir;	/* GDB source directories */
1172     char *source_cur;	/* GDB current source */
1173     char *source_list;	/* GDB source list */
1174     int silent;
1175     struct obstack *obs;
1176 {
1177     int bufno = -1;
1178     char *pathname;	/* file full path name */
1179     cnbuf_T *buf;
1180     char *str;
1181     char *quoted;
1182 
1183     if (cnb == NULL || ! (cnb->state & NBS_READY))
1184 	return -1;
1185 
1186     if (name == NULL || *name == NUL || lnum <= 0)
1187 	return -1;
1188 
1189     if (cnb->debug)
1190 	fprintf(stderr, "gdb source file name: \"%s\"\n", name);
1191 
1192     if (cnb->remote_map)
1193 	/* map source filenames when doing remote debugging */
1194 	pathname = pm_mapto_vim(name, sourcedir, source_cur, source_list, obs);
1195     else
1196 	/* get the first existing full path name in GDB source directories
1197 	 * matching this name */
1198 	pathname = get_fullpath(name, sourcedir, source_cur, source_list, obs);
1199 
1200     if (pathname == NULL)
1201     {
1202 	if (! silent && strlen(name) < MAXMSGSIZE - 100)
1203 	{
1204 	    sprintf(cnb_buf,
1205 		    "Clewn cannot find file \"%s\" in GDB source directories\n", name);
1206 	    fprintf(stderr, cnb_buf);
1207 	}
1208 	return -1;
1209     }
1210 
1211     /* create a new buffer if this name does not refer to an existing buffer */
1212     if ((bufno = cnb_lookup_buf(pathname)) == -1)
1213 	bufno = cnb_create_buf(pathname);
1214 
1215     if ((buf = getbuf(bufno)) != NULL)
1216     {
1217 	/* initialize the buffer */
1218 	if (! (buf->state & BUFS_INITDONE))
1219 	{
1220 	    if ((quoted = quote(pathname, obs)) != NULL)
1221 	    {
1222 		send_cmd(bufno, "editFile", quoted);
1223 
1224 		/* send to the editor the new bufID */
1225 		send_cmd(bufno, "stopDocumentListen", NULL);
1226 
1227 		/* define the frame sign */
1228 		(void)cnb_define_sign(bufno, FRAME_SIGN, SIGN_ANY, obs);
1229 
1230 		buf->state |= BUFS_INITDONE;
1231 	    }
1232 	    else
1233 		return -1;
1234 	}
1235 
1236 	sprintf(cnb_buf, "%ld/0", (long)lnum);
1237 
1238 	/* set the cursor position to "lnum/0" */
1239 	str = obstack_strsave(obs, cnb_buf);
1240 	send_cmd(bufno, "setDot", str);
1241 	send_cmd(bufno, "setVisible", "T"); /* need this to update Vim cmd line */
1242     }
1243     else
1244 	bufno = -1;
1245 
1246     return bufno;
1247 }
1248 
1249 #define COLOR_BLUE	802287
1250 #define COLOR_GREEN	4190027
1251 #define COLOR_ORANGE	15710005
1252 /*
1253  * Define a sign for this buffer according to its type.
1254  * Vim's NetBeans will not define duplicate signs, so it is safe
1255  * to call this function more than once with the same id.
1256  * Vim's NetBeans creates a new hilite group for each new defined sign.
1257  * As these are never removed (there is no such msg in NetBeans), this
1258  * is somehow a waste of memory.
1259  * Return the sequence sign type number in this buffer or -1 if error.
1260  */
1261     int
cnb_define_sign(bufno,id,type,obs)1262 cnb_define_sign(bufno, id, type, obs)
1263     int bufno;	    /* buffer number where this sign is defined */
1264     int id;	    /* sign id (1:frame or breakpoint number) */
1265     int type;	    /* sign type (frame or enabled/disabled bp) */
1266     struct obstack *obs;
1267 {
1268     char *prefix   = NULL;	/* keep compiler happy */
1269     char *typeName;		/* sign name (Vim use it to build the hilite group) */
1270     char *glyphFile;		/* 2 characters text */
1271     int fg = 0;			/* foreground color: black */
1272     int bg = 0;			/* background color; set to 0 to keep compiler happy */
1273     char text[NUMBUFLEN];
1274     char *arg;
1275     cnbuf_T *buf;
1276     int r;
1277     int typenr;
1278 
1279     if (cnb == NULL || ! (cnb->state & NBS_READY))
1280 	return -1;
1281 
1282     if ((buf = getbuf(bufno)) == NULL || id <= 0)
1283 	return -1;
1284 
1285     if (type == SIGN_BP_ENABLED || type == SIGN_BP_DISABLED)
1286     {
1287 	if (type == SIGN_BP_ENABLED)
1288 	{
1289 	    prefix = "BP";
1290 	    bg = COLOR_BLUE;
1291 	}
1292 	else if (type == SIGN_BP_DISABLED)
1293 	{
1294 	    prefix = "BPd";
1295 	    bg = COLOR_GREEN;
1296 	}
1297 
1298 	/* build typeName: "BP[d]x_nnn"
1299 	 * where:
1300 	 *  x is the gdb instance number
1301 	 *  nnn is the breakpoint number */
1302 	obstack_strcat(obs, prefix);
1303 	strcpy(text, gdb_itoa(cnb->instance));
1304 	obstack_strcat(obs, text);
1305 	obstack_strcat(obs, "_");
1306 
1307 	strcpy(text, gdb_itoa(id));
1308 	obstack_strcat0(obs, text);
1309 	typeName = (char *)obstack_finish(obs);
1310 
1311 	/* the sign text is two chars max */
1312 	if (id < 100)
1313 	    strcpy(text, gdb_itoa(id));
1314 	else
1315 	{
1316 	    if ((r = id % 100) < 10)
1317 	    {
1318 		text[0] = '0';
1319 		strcpy(text + 1, gdb_itoa(r));
1320 	    }
1321 	    else
1322 		strcpy(text, gdb_itoa(r));
1323 	}
1324 	glyphFile = text;
1325     }
1326     else    /* frame sign */
1327     {
1328 	typeName = "1";
1329 	glyphFile = "=>";
1330 	bg = COLOR_ORANGE;
1331     }
1332 
1333     if (type == SIGN_BP_ENABLED || type == SIGN_BP_DISABLED)
1334 	typenr = ++(buf->lastsign);
1335     else
1336     {
1337 	typenr = 1;
1338 	if (buf->lastsign == 0)
1339 	    buf->lastsign = 1;
1340     }
1341 
1342     sprintf(cnb_buf, "%d \"%s\" \"\" \"%s\" %d %d",
1343 	    typenr, typeName, glyphFile, fg, bg);
1344 
1345     arg = obstack_strsave(obs, cnb_buf);
1346     send_cmd(bufno, "defineAnnoType", arg);
1347 
1348     /*
1349      * Attempt to have Vim 6.2 corrupt memory bug (netbeans.c: 748:
1350      * buf_list[buf_list_used].fireChanges = 1;) write in the version text.
1351      * Otherwise, it may replace a quote with '#' as in:
1352      * "2:stopDocumentListen!3\n2:defineAnnoType!4 1 \"1\" \"\" \"=>\" 0 >15710005\n"
1353      * "2:stopDocumentListen!3\n2:defineAnnoType!4 1 \"1\" \"\" \"=># 0 >15710005\n"
1354      *
1355      * This may happen only for the first Vim instantiated netbeans buffer, so this
1356      * is only needed when bufno == 2 (bufno == 1 is the variables buffer) and when
1357      * setting the frame sign.
1358      */
1359     if (bufno <= 2 && id == FRAME_SIGN && type != SIGN_BP_ENABLED
1360 	    && type != SIGN_BP_DISABLED)
1361     {
1362 	send_cmd(bufno, "version",
1363 	    "\"Clewn version x.x                                           \
1364 									   \
1365 									  .\"");
1366     }
1367 
1368     return typenr;
1369 }
1370 
1371 /* Add a sign in a buffer. */
1372     void
cnb_buf_addsign(bufno,id,typenr,lnum,obs)1373 cnb_buf_addsign(bufno, id, typenr, lnum, obs)
1374     int bufno;	    /* buffer number */
1375     int id;	    /* sign id (1:frame or BP_SIGN_ID(bp_id)) */
1376     int typenr;	    /* sequence number: nth sign type number defined in this buffer */
1377     linenr_T lnum;  /* line number */
1378     struct obstack *obs;
1379 {
1380     char *arg;
1381 
1382     if (cnb == NULL || ! (cnb->state & NBS_READY))
1383 	return;
1384 
1385     if (getbuf(bufno) == NULL || id <= 0 || typenr <= 0 || lnum <= 0)
1386 	return;
1387 
1388     sprintf(cnb_buf, "%d %d %ld/0", id, typenr, (long)lnum);
1389 
1390     arg = obstack_strsave(obs, cnb_buf);
1391     send_cmd(bufno, "addAnno", arg);
1392 
1393     sprintf(cnb_buf, "%ld/0", (long)lnum);
1394 
1395     arg = obstack_strsave(obs, cnb_buf);
1396     send_cmd(bufno, "setDot", arg);
1397     send_cmd(bufno, "setVisible", "T"); /* need this to update Vim cmd line */
1398 
1399     if (id == FRAME_SIGN)
1400 	cnb->fr_buf = bufno;
1401 
1402     /* note the last position */
1403     cnb->cur_buf = bufno;
1404     cnb->cur_line = (int)lnum;
1405 }
1406 
1407 #define GETANNO_TIMEOUT 600
1408 /* Return the sign line number.
1409  * return 0 when error (0 is an illegal line number) */
1410     int
cnb_buf_getsign(bufno,id)1411 cnb_buf_getsign(bufno, id)
1412     int bufno;	    /* buffer number */
1413     int id;	    /* sign id (1:frame or BP_SIGN_ID(bp_id)) */
1414 {
1415     int wtime = GETANNO_TIMEOUT;    /* msecs */
1416     char arg[NUMBUFLEN];
1417     int lnum;
1418     int seqno;
1419     int rc;
1420 #ifndef HAVE_SELECT
1421     struct pollfd fds;
1422 #else
1423     struct timeval tv;
1424     struct timeval start_tv;
1425     fd_set rfds;
1426 # ifdef HAVE_GETTIMEOFDAY
1427     gettimeofday(&start_tv, NULL);
1428 # endif
1429 #endif
1430 
1431     /* require a recent version of the netbeans protocol */
1432     if (cnb == NULL || cnb->fdata == -1 || ! (cnb->state & NBS_READY) || ! (cnb->state & NBS_GETANNO))
1433 	return 0;
1434 
1435     if (getbuf(bufno) == NULL || id == FRAME_SIGN || id <= 0)
1436 	return 0;
1437 
1438     /* send the nebeans function */
1439     sprintf(arg, "%d", id);
1440     send_function(bufno, "getAnno", arg);
1441     seqno = cnb->seqno;
1442 
1443     /* listen for the reply to the function */
1444     lnum = 0;
1445     while (wtime > 0) {
1446 # ifndef HAVE_SELECT
1447 	fds.fd = cnb->fdata;
1448 	fds.events = POLLIN;
1449 
1450 	rc = poll(&fds, 1, wtime);
1451 # else
1452 	FD_ZERO(&rfds);
1453 	FD_SET(cnb->fdata, &rfds);
1454 
1455 	tv.tv_sec = 0;
1456 	tv.tv_usec = wtime * 1000;
1457 
1458 	rc = select(cnb->fdata + 1, &rfds, NULL, NULL, &tv);
1459 # endif
1460 
1461 	if ((rc == -1 && errno != EINTR) || rc == 0)
1462 	    break;
1463 	else if (rc > 0 && cnb_data_evt() != NULL && cnb_event.seqno == seqno) {
1464 	    if(cnb_event.text != NULL)
1465 		lnum = atoi(cnb_event.text);
1466 	    break;
1467 	}
1468 
1469 	/* compute remaining wait time */
1470 # if ! defined(HAVE_SELECT) || ! defined(HAVE_GETTIMEOFDAY)
1471 	/* guess: interrupted halfway, gdb processing 10 msecs */
1472 	wtime = wtime / 2 - 10L;
1473 # else
1474 	gettimeofday(&tv, NULL);
1475 	wtime -= (tv.tv_sec - start_tv.tv_sec) * 1000L
1476 			+ (tv.tv_usec - start_tv.tv_usec) / 1000L;
1477 	start_tv.tv_sec = tv.tv_sec;
1478 	start_tv.tv_usec = tv.tv_usec;
1479 # endif
1480     }
1481 
1482     return lnum;
1483 }
1484 
1485 /* Delete a sign in a buffer. */
1486     void
cnb_buf_delsign(bufno,id)1487 cnb_buf_delsign(bufno, id)
1488     int bufno;	    /* buffer number */
1489     int id;	    /* sign id (1:frame or BP_SIGN_ID(bp_id)) */
1490 {
1491     char arg[NUMBUFLEN];
1492 
1493     if (cnb == NULL || ! (cnb->state & NBS_READY))
1494 	return;
1495 
1496     if (id == FRAME_SIGN)
1497     {
1498 	bufno = cnb->fr_buf;
1499 	cnb->fr_buf = -1;
1500     }
1501 
1502     if (getbuf(bufno) == NULL || id <= 0)
1503 	return;
1504 
1505     sprintf(arg, "%d", id);
1506     send_cmd(bufno, "removeAnno", arg);
1507 }
1508 
1509 #define PATHMAP_PATH_SEPARATOR ':'
1510 #define PATHMAP_MAP_SEPARATOR '|'
1511 /* Return an array of path mappings.
1512  * The array is built from the the pathnames_map string:
1513  * "gdb_path:vim_path|..." */
1514     static pathmap_T *
pm_parse(pathnames_map)1515 pm_parse(pathnames_map)
1516     char * pathnames_map;
1517 {
1518     pathmap_T * pathmap;
1519     pathmap_T * m;
1520     char * map_list;
1521     char * ptr;
1522     char * end;
1523     char * map;
1524     int count;
1525 
1526     if (pathnames_map == NULL)
1527 	return NULL;
1528 
1529     /* count how many mappings */
1530     for (count=1, ptr=pathnames_map; *ptr; ptr++)
1531 	if (*ptr == PATHMAP_MAP_SEPARATOR)
1532 	    count++;
1533 
1534     /* allocate the array */
1535     if ((pathmap = (pathmap_T *) xcalloc((size_t)((count + 1) * sizeof(pathmap_T)))) == NULL)
1536 	return NULL;
1537 
1538     /* make a copy so we can mess with it */
1539     map_list = clewn_strsave(pathnames_map);
1540 
1541     /* build the array */
1542     for (m=pathmap, ptr=map_list; ; m++) {
1543 	map = end = NULL;
1544 	if ((end=strchr(ptr, PATHMAP_MAP_SEPARATOR)) != NULL)
1545 	    *end = NUL;
1546 	if ((map=strchr(ptr, PATHMAP_PATH_SEPARATOR)) != NULL)
1547 	    *map = NUL;
1548 
1549 	m->gdb_path = clewn_strsave(ptr);
1550 	if (map != NULL)
1551 	    m->vim_path = clewn_strsave(map + 1);
1552 	else
1553 	    m->vim_path = clewn_strsave("");
1554 
1555 	if (end == NULL)
1556 	    break;
1557 	ptr = end + 1;
1558     }
1559 
1560     xfree(map_list);
1561     return pathmap;
1562 }
1563 
1564 /*
1565  * For each mapping in the remote_map array, if gdb_path is a prefix
1566  * or is the empty string, then replace it with vim_path
1567  */
1568     static char *
pm_mapto_vim(name,sourcedir,source_cur,source_list,obs)1569 pm_mapto_vim(name, sourcedir, source_cur, source_list, obs)
1570     char *name;		/* file name */
1571     char *sourcedir;	/* GDB source directories */
1572     char *source_cur;	/* GDB current source */
1573     char *source_list;	/* GDB source list */
1574     struct obstack *obs;
1575 {
1576     char *pathname = name;
1577     char *ptr = sourcedir;
1578     pathmap_T * m;
1579     char *next;
1580     char *hay;
1581     char *found;
1582     char *end;
1583 
1584     /* first, get the full path name from gdb when the gdb variable 'directories'
1585      * contains the compilation directory "$cdir" */
1586     do {
1587 	if (sourcedir == NULL)
1588 	    break;
1589 
1590 	if ((next = strchr(ptr, ':')) != NULL)
1591 	    *next++ = NUL;
1592 
1593 	/* compilation directory */
1594 	if (strcmp(ptr, GDB_CDIR) == 0) {
1595 	    /* hay: file="NAME",fullname=" */
1596 	    obstack_strcat(obs, "file=\"");
1597 	    obstack_strcat(obs, name);
1598 	    obstack_strcat0(obs, "\",fullname=\"");
1599 	    hay = (char *)obstack_finish(obs);
1600 
1601 	    /* name is the current sourcefile: use gdb compilation directory */
1602 	    if (source_cur != NULL && (found=strstr(source_cur, hay)) != NULL ){
1603 		found += strlen(hay);
1604 		if ((end=strstr(found, "\"")) != NULL) {
1605 		    pathname = (char *)obstack_copy0(obs, found, end - found);
1606 		    break;
1607 		}
1608 	    }
1609 
1610 	    /* lookup for first occurence of name in source list */
1611 	    if (source_list != NULL && (found=strstr(source_list, hay)) != NULL ){
1612 		found += strlen(hay);
1613 		if ((end=strstr(found, "\"")) != NULL)
1614 		    pathname = (char *)obstack_copy0(obs, found, end - found);
1615 	    }
1616 
1617 	    break;
1618 	}
1619 
1620 	ptr = next;
1621 
1622     } while (ptr != NULL && *ptr != NUL);
1623 
1624     /* map the path name */
1625     for (m=cnb->remote_map; m != NULL && m->gdb_path != NULL; m++) {
1626 	if ((ptr=strstr(pathname, m->gdb_path)) == pathname
1627 		|| ((ptr=pathname) && *(m->gdb_path) == NUL))
1628 	{
1629 	    ptr += strlen(m->gdb_path);
1630 	    obstack_strcat(obs, m->vim_path);
1631 	    obstack_strcat0(obs, ptr);
1632 	    return (char *)obstack_finish(obs);
1633 	}
1634     }
1635 
1636     return pathname;
1637 }
1638 
1639 /*
1640  * For each mapping in the remote_map array, if vim_path is a prefix
1641  * or is the empty string, then replace it with gdb_path
1642  * Return an allocated string
1643  */
1644     static char *
pm_mapto_gdb(name,obs)1645 pm_mapto_gdb(name, obs)
1646     char *name;	/* file name */
1647     struct obstack *obs;
1648 {
1649     pathmap_T * m;
1650     char *ptr;
1651 
1652     for (m=cnb->remote_map; m != NULL && m->gdb_path != NULL; m++) {
1653 	if ((ptr=strstr(name, m->vim_path)) == name || ((ptr=name) && *(m->vim_path) == NUL)) {
1654 	    ptr += strlen(m->vim_path);
1655 	    obstack_strcat(obs, m->gdb_path);
1656 	    obstack_strcat0(obs, ptr);
1657 	    return clewn_strsave(obstack_finish(obs));
1658 	}
1659     }
1660 
1661     return clewn_strsave(name);
1662 }
1663 
1664 /*
1665  * Remove double quotes and convert backslashed chars.
1666  * When parameter end is not NULL, the address it is pointing to
1667  * is set to the first char after end of quoted string
1668  * Return an allocated string.
1669  */
1670     static char *
unquote(str,end,obs)1671 unquote(str, end, obs)
1672     char *str;	/* quoted string */
1673     char **end;	/* set to first char after end of quoted string */
1674     struct obstack *obs;
1675 {
1676     if (str == NULL || *str != '"')
1677 	return NULL;
1678 
1679     while (*++str != NUL)
1680     {
1681 	switch (*str)
1682 	{
1683 	    /* matching quote */
1684 	    case '"':
1685 		obstack_1grow(obs, NUL);
1686 
1687 		/* tag first char after end of quoted string */
1688 		if (end != NULL)
1689 		    *end = str + 1;
1690 		return (char *)obstack_finish(obs);
1691 
1692 	    case '\\':
1693 		/* remove escape char and illegal escape sequences */
1694 		switch (*++str)
1695 		{
1696 		    case '\\':
1697 			obstack_1grow(obs, '\\');
1698 			break;
1699 		    case 'n':
1700 			obstack_1grow(obs, '\n');
1701 			break;
1702 		    case 't':
1703 			obstack_1grow(obs, '\t');
1704 			break;
1705 		    case 'r':
1706 			obstack_1grow(obs, '\r');
1707 			break;
1708 		    case '"':
1709 			obstack_1grow(obs, '"');
1710 			break;
1711 		    case NUL:
1712 			return NULL;
1713 		    default:
1714 			break;
1715 		}
1716 		break;
1717 
1718 	    default:
1719 		obstack_1grow(obs, *str);
1720 	}
1721     }
1722 
1723     /* failed to get matching quote */
1724     return NULL;
1725 }
1726 
1727 /*
1728  * Quote the given string and escape newline, '\r', backslash, double quote
1729  * characters inside the string.
1730  * Return an allocated string.
1731  */
1732     static char *
quote(str,obs)1733 quote(str, obs)
1734     char *str;
1735     struct obstack *obs;
1736 {
1737     char escaped;
1738 
1739     if (str != NULL)
1740     {
1741 	obstack_1grow(obs, '"');
1742 
1743 	for (; *str; str++)
1744 	{
1745 	    switch (*str)
1746 	    {
1747 		case '"':
1748 		case '\\':
1749 		    escaped = *str;
1750 		    break;
1751 
1752 		case '\n':
1753 		    escaped = 'n';
1754 		    break;
1755 
1756 		case '\r':
1757 		    escaped = 'r';
1758 		    break;
1759 
1760 		default:
1761 		    escaped = NUL;
1762 		    break;
1763 	    }
1764 
1765 	    if (escaped != NUL)
1766 	    {
1767 		obstack_1grow(obs, '\\');
1768 		obstack_1grow(obs, escaped);
1769 	    }
1770 	    else
1771 		obstack_1grow(obs, *str);
1772 	}
1773 	obstack_grow0(obs, "\"", 1);
1774 	return (char *)obstack_finish(obs);
1775     }
1776     return NULL;
1777 }
1778 
1779 /*
1780  * This function may only be invoked by cnb_create_buf() and getbuf().
1781  * Find or create a buffer with the given number. Buffers are never
1782  * removed or freed. They are 'killed', which means they can't be used.
1783  * Buffer numbers start at 1 not 0.
1784  * Return this buffer or NULL if it has been 'killed' or when memory
1785  * allocation error or when this would require reallocating more than
1786  * BUFF_ALLOC_INCREMENT.
1787  */
1788     static cnbuf_T *
lowlevel_buf(bufno)1789 lowlevel_buf(bufno)
1790     int bufno;
1791 {
1792     cnbuf_T * buf;
1793 
1794     if (cnb == NULL || bufno <= 0 || bufno > cnb->cnbuf_size + BUFF_ALLOC_INCREMENT)
1795 	return NULL;
1796 
1797     /* grow buffer list*/
1798     if (bufno > cnb->cnbuf_size)
1799     {
1800 	cnb->cnbuf = (cnbuf_T *)xrealloc(cnb->cnbuf,
1801 			(cnb->cnbuf_size + BUFF_ALLOC_INCREMENT) * sizeof(cnbuf_T));
1802 
1803 	memset(cnb->cnbuf + cnb->cnbuf_size, 0, BUFF_ALLOC_INCREMENT * sizeof(cnbuf_T));
1804 
1805 	cnb->cnbuf_size += BUFF_ALLOC_INCREMENT;
1806     }
1807 
1808     buf = cnb->cnbuf + bufno - 1;
1809 
1810     /* a killed buffer can't be used anymore */
1811     if (buf->state & BUFS_KILLED)
1812 	return NULL;
1813 
1814     return buf;
1815 }
1816 
1817 /*
1818  * Get buffer bufno.
1819  * Return the buffer or NULL if error.
1820  */
1821     static cnbuf_T *
getbuf(bufno)1822 getbuf(bufno)
1823     int bufno;
1824 {
1825     if (cnb == NULL)
1826 	return NULL;
1827 
1828     if (bufno > 0 && bufno <= cnb->lastbuf)
1829 	return lowlevel_buf(bufno);
1830     return NULL;
1831 }
1832 
1833 /*
1834  * Create a new buffer.
1835  * Return the buffer number or -1 if error.
1836  */
1837     int
cnb_create_buf(name)1838 cnb_create_buf(name)
1839     char *name;		/* buffer name */
1840 {
1841     cnbuf_T *buf;
1842     int bufno;
1843     int j;
1844 
1845     if (cnb == NULL || name == NULL)
1846 	return -1;
1847 
1848     if (cnb->lastbuf > cnb->cnbuf_size)
1849     {
1850 	if (cnb->debug)
1851 	    fprintf(stderr, "last buffer out of bounds in cnb_create_buf()\n");
1852 	return -1;
1853     }
1854 
1855     /* look first if the buffer already exists:
1856      * this handles the case where asm files have been unlinked
1857      * and their buffers are being reused again (after a "file"
1858      * GDB command for example) */
1859     for (j = 0; j < cnb->lastbuf; j++)
1860     {
1861 	buf = cnb->cnbuf + j;
1862 
1863 	if (! (buf->state & BUFS_KILLED) && buf->name != NULL
1864 		&& strcmp(buf->name, name) == 0)
1865 	    return j + 1;
1866     }
1867 
1868     bufno = cnb->lastbuf;
1869 
1870     /* create a new buffer */
1871     if ((buf = lowlevel_buf(++bufno)) != NULL)
1872     {
1873 	buf->name = clewn_strsave(name);
1874 	cnb->lastbuf = bufno;
1875 	return cnb->lastbuf;
1876     }
1877     return -1;
1878 }
1879 
1880 /* Kill a buffer. */
1881     void
cnb_kill(bufno)1882 cnb_kill(bufno)
1883     int bufno;
1884 {
1885     cnbuf_T *buf;
1886 
1887     if ((buf = getbuf(bufno)) != NULL)
1888     {
1889 	buf->state |= BUFS_KILLED;
1890 	FREE(buf->name);
1891     }
1892 }
1893 
1894 /* Set the buffer as an asm buffer. */
1895     void
cnb_set_asm(bufno)1896 cnb_set_asm(bufno)
1897     int bufno;
1898 {
1899     cnbuf_T *buf;
1900 
1901     if ((buf = getbuf(bufno)) != NULL)
1902 	buf->state |= BUFS_ASM;
1903 }
1904 
1905 /* Unlink all asm buffers. */
1906     void
cnb_unlink_asm()1907 cnb_unlink_asm()
1908 {
1909     cnbuf_T *buf;
1910     int j;
1911 
1912     if (cnb == NULL)
1913 	return;
1914 
1915     if (cnb->lastbuf > cnb->cnbuf_size)
1916     {
1917 	if (cnb->debug)
1918 	    fprintf(stderr, "last buffer out of bounds in cnb_killall_asm()\n");
1919 	return;
1920     }
1921 
1922     for (j = 0; j < cnb->lastbuf; j++)
1923     {
1924 	buf = cnb->cnbuf + j;
1925 
1926 	if (! (buf->state & BUFS_KILLED) && (buf->state & BUFS_ASM) && buf->name != NULL) {
1927 	    if (cnb->state & NBS_READY && cnb->state & NBS_CLOSE_FIX) {
1928 		int idx;
1929 
1930 		if ((idx = cnb_lookup_buf(buf->name)) != -1) {
1931 		    send_cmd(idx, "close", NULL);
1932 		    cnb_kill(idx);
1933 		}
1934 	    }
1935 
1936 	    (void)unlink(buf->name);
1937 	}
1938     }
1939 }
1940 /*
1941  * Look up for name as an existing buffer name, starting with the
1942  * last buffer that was looked up last time.
1943  * Return the buffer number or -1 when not found.
1944  */
1945     int
cnb_lookup_buf(name)1946 cnb_lookup_buf(name)
1947     char *name;			/* buffer file path name */
1948 {
1949     static int last_used = 0;	/* last index used */
1950     cnbuf_T *buf;
1951     int i;
1952     int j;
1953 
1954     if (cnb == NULL)
1955 	return -1;
1956 
1957     if (cnb->lastbuf > cnb->cnbuf_size)
1958     {
1959 	if (cnb->debug)
1960 	    fprintf(stderr, "last buffer out of bounds in cnb_lookup_buf()\n");
1961 	return -1;
1962     }
1963 
1964     for (j = 0; j < cnb->lastbuf; j++)
1965     {
1966 	i = (j + last_used) % cnb->lastbuf;
1967 	buf = cnb->cnbuf + i;
1968 
1969 	if (! (buf->state & BUFS_KILLED)
1970 		&& buf->name != NULL && strcmp(buf->name , name) == 0)
1971 	{
1972 	    last_used = i;
1973 	    return i + 1;	/* buffer numbers start at 1 */
1974 	}
1975     }
1976     return -1;
1977 }
1978 
1979 /* Return buffer file name */
1980     char *
cnb_filename(bufno)1981 cnb_filename(bufno)
1982     int bufno;
1983 {
1984     cnbuf_T *buf;
1985 
1986     if ((buf = getbuf(bufno)) != NULL && ! (buf->state & BUFS_KILLED))
1987 	return buf->name;
1988     return NULL;
1989 }
1990 
1991 /* Return TRUE if this is a valid buffer number */
1992     int
cnb_isvalid_buffer(bufno)1993 cnb_isvalid_buffer(bufno)
1994     int bufno;
1995 {
1996     return (getbuf(bufno) != NULL ? TRUE : FALSE);
1997 }
1998 
1999 /* Return TRUE when bufno is an out of bounds buffer */
2000     int
cnb_outofbounds(bufno)2001 cnb_outofbounds(bufno)
2002     int bufno;
2003 {
2004     return (bufno <= 0 || bufno > cnb->lastbuf);
2005 }
2006 
2007 /*
2008  * Append a line at the last before last line of buffer bufno.
2009  *
2010  * The NetBeans Vim code handling "insert" functions (v6.2) assumes:
2011  *	- text must contain a new line otherwise it's a partial line and
2012  *	  what becomes of partial lines is ... (we don't want to know)
2013  *	- whatever the offset, the text is inserted as a new line before
2014  *	  the existing line containing this offset
2015  *	- Vim crashes when refering to the offset after the last character
2016  *
2017  * Another problem stems from the fact that the line_insert() function we
2018  * must use to update the linked list of lines ignores a new line as the last
2019  * character of a line (it does that because it is needed when listening to
2020  * "insert" events).
2021  */
2022     void
cnb_append(bufno,line,obs)2023 cnb_append(bufno, line, obs)
2024     int bufno;	/* buffer number */
2025     char *line; /* line to append */
2026     struct obstack *obs;
2027 {
2028     int offset  = 0;
2029     char *res;
2030     cnbuf_T *buf;
2031     cnbl_T *l;
2032     char *ptr;
2033     char *quoted;
2034 
2035     if (line == NULL || edit_file(bufno, obs) != OK)
2036 	return;
2037 
2038     if (bufno == *cnb->pvar_buf && cnb->modified)
2039     {
2040 	/* not needed any more
2041 	 * this buffer is set "bufhidden=hide" by autocommands
2042 	if (cnb->debug)
2043 	    fprintf(stderr, "cannot append to modified variables buffer, you must save it\n");
2044 	return;
2045 	*/
2046     }
2047 
2048     if ((buf = getbuf(bufno)) == NULL)
2049 	return;
2050 
2051     /* strip any new line */
2052     if ((ptr = strchr(line, '\n')) != NULL)
2053 	*ptr = NUL;
2054 
2055     /* add the terminating new line */
2056     obstack_strcat(obs, line);
2057     obstack_strcat0(obs, "\n");
2058     res = (char *)obstack_finish(obs);
2059 
2060     /* quote it */
2061     if ((quoted = quote(res, obs)) == NULL)
2062 	return;
2063 
2064     /* get offset of start of last line */
2065     for (l = buf->txt; l != NULL; )
2066     {
2067 	if (l->next == NULL)
2068 	    break;
2069 
2070 	offset += l->len;
2071 	offset++;		/* count the new line */
2072 	l = l->next;
2073     }
2074 
2075     sprintf(cnb_buf, "%d ", offset);
2076     obstack_strcat(obs, cnb_buf);
2077     obstack_strcat0(obs, quoted);
2078     res = (char *)obstack_finish(obs);
2079 
2080     /* insert line in linked list */
2081     if (l != NULL && l->len > 0)
2082     {
2083 	if (offset == 0)
2084 	{
2085 	    line_insert(bufno, l->len + 1, "\n");
2086 	    line_insert(bufno, l->len + 1, line);
2087 	}
2088 	else
2089 	{
2090 	    line_insert(bufno, offset, line);
2091 	    line_insert(bufno, offset + strlen(line), "\n");
2092 	}
2093     }
2094     else
2095     {
2096 	line_insert(bufno, offset, line);
2097 	line_insert(bufno, offset + strlen(line) + 1, "\n");
2098     }
2099 
2100     /* restore state */
2101     if (bufno == *cnb->pvar_buf)
2102 	cnb->modified = FALSE;
2103 
2104     if (cnb->debug)
2105 	line_debug(bufno);
2106 
2107     /* send "insert" function */
2108     send_function(bufno, "insert", res);
2109 }
2110 
2111 /* Clear a buffer of its content */
2112     void
cnb_clear(bufno,obs)2113 cnb_clear(bufno, obs)
2114     int bufno;	    /* buffer number */
2115     struct obstack *obs;
2116 {
2117     int count = 0;
2118     char *res;
2119     cnbuf_T *buf;
2120     cnbl_T *l;
2121     cnbl_T *next;
2122 
2123     if ((buf = getbuf(bufno)) == NULL || ! (buf->state & BUFS_INITDONE))
2124 	return;
2125 
2126     /* count the file length */
2127     for (l = buf->txt; l != NULL; l = l->next)
2128     {
2129 	count += l->len;
2130 	count++;		/* count the new line */
2131     }
2132 
2133     /* the last line (when there is one) is empty and is not accounted for by vim */
2134     --count;
2135     if (count < 0)
2136 	count = 0;
2137 
2138     /* free all lines */
2139     for (l= buf->txt; l != NULL; l = next)
2140     {
2141 	next = l->next;
2142 	xfree(l->line);
2143 	xfree(l);
2144     }
2145     buf->txt = NULL;
2146 
2147     /* tell vim to remove all contents */
2148     if (count != 0) {
2149 	sprintf(cnb_buf, "%d %d", 0, count);
2150 	res = obstack_strsave(obs, cnb_buf);
2151 	send_function(bufno, "remove", res);
2152     }
2153 
2154     /* set back cursor to last known position */
2155     if (bufno == *cnb->pvar_buf && getbuf(cnb->cur_buf) != NULL)
2156     {
2157 	sprintf(cnb_buf, "%d/0", cnb->cur_line);
2158 
2159 	res = obstack_strsave(obs, cnb_buf);
2160 	send_cmd(cnb->cur_buf, "setDot", res);
2161 	send_cmd(cnb->cur_buf, "setVisible", "T"); /* need this to update Vim cmd line */
2162     }
2163 }
2164 
2165 /* Replace with line in bufno at line lnum. */
2166     void
cnb_replace(bufno,line,lnum,obs)2167 cnb_replace(bufno, line, lnum, obs)
2168     int bufno;	/* buffer number */
2169     char *line; /* line to append */
2170     int lnum;	/* line number */
2171     struct obstack *obs;
2172 {
2173     int offset      = 0;
2174     char *oldline;
2175     char *twonl;
2176     char *res;
2177     cnbl_T *l;
2178     char *quoted;
2179     cnbuf_T *buf;
2180     char *ptr;
2181     int len;
2182 
2183     if (line == NULL || cnb == NULL || ! (cnb->state & NBS_READY))
2184 	return;
2185 
2186     if (bufno == *cnb->pvar_buf && cnb->modified)
2187     {
2188 	/* not needed any more
2189 	 * this buffer is set "bufhidden=hide" by autocommands
2190 	if (cnb->debug)
2191 	    fprintf(stderr, "cannot replace line in modified variables buffer, you must save it\n");
2192 	return;
2193 	*/
2194     }
2195 
2196     if ((buf = getbuf(bufno)) == NULL || ! (buf->state & BUFS_INITDONE))
2197 	return;
2198 
2199     /* strip any new line */
2200     if ((ptr = strchr(line, '\n')) != NULL)
2201 	*ptr = NUL;
2202 
2203     /* add two terminating new lines */
2204     obstack_strcat(obs, line);
2205     obstack_strcat0(obs, "\n\n");
2206     res = (char *)obstack_finish(obs);
2207 
2208     /* quote it */
2209     if ((quoted = quote(res, obs)) == NULL)
2210 	return;
2211 
2212     /* get offset of start of line lnum */
2213     for (l = buf->txt; l != NULL && --lnum > 0; )
2214     {
2215 	if (l->next == NULL)
2216 	    break;
2217 
2218 	offset += l->len;
2219 	offset++;		/* count the new line */
2220 	l = l->next;
2221     }
2222 
2223     if (l == NULL || lnum != 0)
2224     {
2225 	if (cnb->debug)
2226 	    fprintf(stderr, "invalid lnum in cnb_replace()\n");
2227 	return;
2228     }
2229     len = l->len;
2230 
2231     sprintf(cnb_buf, "%d ", offset);
2232     obstack_strcat(obs, cnb_buf);
2233     obstack_strcat0(obs, quoted);
2234     res = (char *)obstack_finish(obs);
2235 
2236     sprintf(cnb_buf, "%d %d", offset, len);
2237     oldline = obstack_strsave(obs, cnb_buf);
2238 
2239     sprintf(cnb_buf, "%d 2", offset + strlen(line) + 1);
2240     twonl = obstack_strsave(obs, cnb_buf);
2241 
2242     /* replace line in linked list */
2243     if (len != 0)
2244 	line_remove(bufno, offset, len);
2245     line_insert(bufno, offset, line);
2246 
2247     /* restore state */
2248     if (bufno == *cnb->pvar_buf)
2249 	cnb->modified = FALSE;
2250 
2251     if (cnb->debug)
2252 	line_debug(bufno);
2253 
2254     /* remove old line */
2255     if (len != 0)
2256 	send_function(bufno, "remove", oldline);
2257 
2258     /* add new line */
2259     send_function(bufno, "insert", res);
2260 
2261     /* remove last two new line */
2262     send_function(bufno, "remove", twonl);
2263 
2264     /* set back cursor to last known position */
2265     if (bufno == *cnb->pvar_buf && getbuf(cnb->cur_buf) != NULL)
2266     {
2267 	sprintf(cnb_buf, "%d/0", cnb->cur_line);
2268 
2269 	res = obstack_strsave(obs, cnb_buf);
2270 	send_cmd(cnb->cur_buf, "setDot", res);
2271 	send_cmd(cnb->cur_buf, "setVisible", "T"); /* need this to update Vim cmd line */
2272     }
2273 }
2274 
2275 /*
2276  * Search for an object in the variables buffer.
2277  * When found, the address referenced by plnum contains the object line number
2278  * in the buffer.
2279  * Return the (non allocated) object line or NULL when not found.
2280  */
2281     char *
cnb_search_obj(object,plnum)2282 cnb_search_obj(object, plnum)
2283     char *object;	/* object name to search for */
2284     int *plnum;		/* found at line number */
2285 {
2286     int lnum = 0;
2287     cnbuf_T *buf;
2288     cnbl_T *l;
2289     char *ptr;
2290 
2291     if (object == NULL || *object == NUL || cnb == NULL || ! (cnb->state & NBS_READY))
2292 	return NULL;
2293 
2294     if ((buf = getbuf(*cnb->pvar_buf)) == NULL || ! (buf->state & BUFS_INITDONE))
2295 	return NULL;
2296 
2297     /* get offset of start of line lnum */
2298     for (l = buf->txt; l != NULL; l = l->next)
2299     {
2300 	lnum++;
2301 
2302 	/* an object line matches the pattern: "^\s*object:"*/
2303 	if ((ptr = l->line) != NULL)
2304 	{
2305 	    while (isspace(*ptr))
2306 		ptr++;
2307 
2308 	    if (strstr(ptr, object) == ptr && *(ptr + strlen(object)) == ':')
2309 	    {
2310 		*plnum = lnum;
2311 		return l->line;	    /* found it */
2312 	    }
2313 	}
2314     }
2315 
2316     return NULL;
2317 }
2318 
2319 /*
2320  * Edit a file if not already done.
2321  * The buffer must have already been created.
2322  * Return FAIL when error.
2323  */
2324     static int
edit_file(bufno,obs)2325 edit_file(bufno, obs)
2326     int bufno;	    /* buffer number */
2327     struct obstack *obs;
2328 {
2329     cnbuf_T *buf;
2330     char *quoted;
2331 
2332     if (cnb == NULL || ! (cnb->state & NBS_READY))
2333 	return FAIL;
2334 
2335     if ((buf = getbuf(bufno)) != NULL)
2336     {
2337 	/* initialize the buffer */
2338 	if (! (buf->state & BUFS_INITDONE))
2339 	{
2340 	    buf->txt = (cnbl_T *)xcalloc(sizeof(cnbl_T));
2341 
2342 	    if (buf->name != NULL && (quoted = quote(buf->name, obs)) != NULL)
2343 	    {
2344 		send_cmd(bufno, "editFile", quoted);
2345 		send_cmd(bufno, "startDocumentListen", NULL);
2346 
2347 		buf->state |= BUFS_INITDONE;
2348 	    }
2349 	    else
2350 		return FAIL;
2351 	}
2352 	return OK;
2353     }
2354     return FAIL;
2355 }
2356 
2357 /*
2358  * Return the address of the link where a line is referenced for buffer bufno and
2359  * offset. When offset cannot be found, return address of last NULL link.
2360  * Set address referenced by pstart to the offset of the first character in this line.
2361  * Return NULL when bufno refers to an invalid buffer.
2362  */
2363     static cnbl_T **
line_get(bufno,offset,pstart)2364 line_get(bufno, offset, pstart)
2365     int bufno;	    /* buffer number */
2366     int offset;
2367     int *pstart;
2368 {
2369     int count = 0;
2370     cnbuf_T *buf;
2371     cnbl_T **pline;
2372 
2373     if ((buf = getbuf(bufno)) == NULL)
2374 	return NULL;
2375 
2376     for (pline = &(buf->txt); *pline != NULL; pline = &((*pline)->next))
2377     {
2378 	if (offset >= count && offset < count + (*pline)->len + 1)
2379 	    break;
2380 
2381 	count += (*pline)->len;
2382 	count++;		/* count the new line */
2383     }
2384 
2385     *pstart = count;
2386     return pline;
2387 }
2388 
2389 /*
2390  * Insert txt at offset.
2391  * assumes new lines are standalone characters in txt
2392  */
2393     static void
line_insert(bufno,offset,txt)2394 line_insert(bufno, offset, txt)
2395     int bufno;	/* buffer number */
2396     int offset;	/* where to insert */
2397     char *txt;  /* text to insert, possibly a standalone '\n' */
2398 {
2399     cnbl_T **pline;
2400     cnbl_T *l;
2401     char *end;
2402     char *p;
2403     int start;
2404     int len;
2405     int first;
2406 
2407     if (txt == NULL)
2408 	return;
2409 
2410     if ((pline = line_get(bufno, offset, &start)) == NULL)
2411     {
2412 	if (cnb->debug)
2413 	    fprintf(stderr, "invalid buffer in line_insert()\n");
2414 	return;
2415     }
2416 
2417     /*
2418      * Insert a new line
2419      */
2420     if (strchr(txt, '\n') != NULL)
2421     {
2422 	if (strlen(txt) != 1)
2423 	{
2424 	    if (cnb->debug)
2425 		fprintf(stderr, "error: a non standalone new line in line_insert()\n");
2426 	    return;
2427 	}
2428 
2429 	/* insert a new line at end of buffer or at a start of line */
2430 	if (*pline == NULL || offset == start)
2431 	{
2432 	    l = (cnbl_T *)xcalloc(sizeof(cnbl_T));
2433 
2434 	    /* link it to the list */
2435 	    l->next = *pline;
2436 	    *pline = l;
2437 	}
2438 	else
2439 	{
2440 	    l = *pline;	    /* assert l != NULL */
2441 
2442 	    l->len = (l->line != NULL ? strlen(l->line) : 0);
2443 	    if ((first = offset - start) <= 0 || first > l->len)
2444 	    {
2445 		if (cnb->debug)
2446 		    fprintf(stderr, "offset out of bounds in line_insert()\n");
2447 		return;
2448 	    }
2449 
2450 	    /* new line at end of line */
2451 	    if (offset == start + l->len)   /* first == l->len */
2452 	    {
2453 		if (l->next == NULL)
2454 		{
2455 		    /* ignore new line at last position in buffer */
2456 		    return;
2457 		}
2458 
2459 		l = (cnbl_T *)xcalloc(sizeof(cnbl_T));
2460 
2461 		/* link it to the list */
2462 		l->next = (*pline)->next;
2463 		(*pline)->next = l;
2464 	    }
2465 	    /* new line in the middle of a line */
2466 	    else /* assert (l->len - first) > 0 */
2467 	    {
2468 		/* remove (l->len - first) last characters */
2469 		end = clewn_strsave(l->line + first);
2470 		line_remove(bufno, offset, (l->len - first));
2471 
2472 		/* insert a new line at end */
2473 		line_insert(bufno, offset, "\n");
2474 
2475 		/* insert back (l->len - first) last characters */
2476 		line_insert(bufno, offset + 1, end);
2477 
2478 		xfree(end);
2479 	    }
2480 	}
2481     }
2482 
2483     /*
2484      * Insert txt
2485      */
2486     else if (*txt != NUL)
2487     {
2488 	/* inserting at end */
2489 	if ((l = *pline) == NULL)
2490 	{
2491 	    l = (cnbl_T *)xcalloc(sizeof(cnbl_T));
2492 	    *pline = l;
2493 	}
2494 
2495 	len = strlen(txt);
2496 	l->len = (l->line != NULL ? strlen(l->line) : 0);
2497 
2498 	if (l->line == NULL)
2499 	{
2500 	    l->line = clewn_strsave(txt);
2501 	    l->len = len;
2502 	}
2503 
2504 	else if ((first = offset - start) < 0 || first > l->len)
2505 	{
2506 	    if (cnb->debug)
2507 		fprintf(stderr, "offset out of bounds in line_insert()\n");
2508 	    return;
2509 	}
2510 
2511 	else
2512 	{
2513 	    p = (char *)xmalloc(l->len + len + 1);
2514 	    strncpy(p, l->line, first);
2515 	    strcpy(p + first, txt);
2516 	    strcpy(p + first + len, l->line + first);
2517 
2518 	    xfree(l->line);
2519 	    l->line = p;
2520 	    l->len += len;
2521 	}
2522     }
2523 
2524     if (bufno == *cnb->pvar_buf)
2525 	cnb->modified = TRUE;
2526 }
2527 
2528 /* Remove nb characters at offset. */
2529     static void
line_remove(bufno,offset,nb)2530 line_remove(bufno, offset, nb)
2531     int bufno;	    /* buffer number */
2532     int offset;	    /* where to insert */
2533     int nb;	    /* number of characters to remove */
2534 {
2535     cnbl_T **pline;
2536     cnbl_T *l;
2537     char *p;
2538     int start;
2539     int first;
2540     int last;
2541 
2542     if (nb <= 0)
2543 	return;
2544 
2545     if ((pline = line_get(bufno, offset, &start)) == NULL)
2546     {
2547 	if (cnb->debug)
2548 	    fprintf(stderr, "invalid buffer in line_remove()\n");
2549 	return;
2550     }
2551 
2552     if ((l = *pline) == NULL)
2553     {
2554 	if (cnb->debug)
2555 	    fprintf(stderr, "cannot remove characters after buffer end in line_remove()\n");
2556 	return;
2557     }
2558 
2559     l->len = (l->line != NULL ? strlen(l->line) : 0);
2560     if ((first = offset - start) < 0 || first > l->len)
2561     {
2562 	if (cnb->debug)
2563 	    fprintf(stderr, "offset out of bounds in line_remove()\n");
2564 	return;
2565     }
2566 
2567     if ((last = l->len - first - nb) < - 1)
2568     {
2569 	if (cnb->debug)
2570 	    fprintf(stderr, "attempt to remove too many chars in line_remove()\n");
2571 	return;
2572     }
2573 
2574     if (last == -1)		    /* remove the new line */
2575     {
2576 	/* remove the whole line */
2577 	if (first == 0)
2578 	{
2579 	    xfree(l->line);
2580 	    *pline = l->next;
2581 	    xfree(l);
2582 	}
2583 	else
2584 	{
2585 	    if (cnb->debug)
2586 		fprintf(stderr, "cannot remove new line in non empty line in line_remove()\n");
2587 	    return;
2588 	}
2589     }
2590     else if (nb == l->len)	/* an empty line */
2591     {
2592 	xfree(l->line);
2593 	l->line = NULL;
2594 	l->len = 0;
2595     }
2596     else
2597     {
2598 	p = (char *)xmalloc(first + last + 1);
2599 	strncpy(p, l->line, first);
2600 	strcpy(p + first, l->line + first + nb);
2601 
2602 	xfree(l->line);
2603 	l->line = p;
2604 	l->len = first + last;
2605     }
2606 
2607     if (bufno == *cnb->pvar_buf)
2608 	cnb->modified = TRUE;
2609 }
2610 
2611 /* Print buffer to "documentListen.debug". */
2612     static void
line_debug(bufno)2613 line_debug(bufno)
2614     int bufno;	    /* buffer number */
2615 {
2616     cnbuf_T *buf;
2617     cnbl_T *l;
2618     FILE *fd;
2619 
2620     if ((buf = getbuf(bufno)) == NULL
2621 	    || (fd = fopen("documentListen.debug", "w+")) == NULL)
2622 	return;
2623 
2624     for (l = buf->txt; l != NULL; l = l->next)
2625     {
2626 	if (l->line != NULL)
2627 	    fprintf(fd, l->line);
2628 	fprintf(fd, "\n");
2629     }
2630     fclose(fd);
2631 }
2632 
2633