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