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: clewn.c 225 2008-10-19 12:58:47Z xavier $
21 */
22
23 #include <config.h>
24
25 #include <signal.h>
26 #include <stdio.h>
27 #include <sys/types.h>
28 #include <regex.h>
29 #include <readline/readline.h>
30 #include <readline/history.h>
31
32
33 #ifdef HAVE_SYS_WAIT_H
34 # include <sys/wait.h>
35 #endif
36
37 #if defined(HAVE_SYS_SELECT_H) && \
38 (!defined(HAVE_SYS_TIME_H) || defined(SYS_SELECT_WITH_SYS_TIME))
39 # include <sys/select.h>
40 #endif
41
42 #ifndef HAVE_SELECT
43 # ifdef HAVE_SYS_POLL_H
44 # include <sys/poll.h>
45 # else
46 # ifdef HAVE_POLL_H
47 # include <poll.h>
48 # endif
49 # endif
50 #endif
51
52 #ifdef HAVE_TERMIOS_H
53 # include <termios.h>
54 #else
55 # include <termio.h>
56 #endif
57
58 /* sun's sys/ioctl.h redefines symbols from termio world */
59 #if defined(HAVE_SYS_IOCTL_H) && !defined(sun)
60 # include <sys/ioctl.h>
61 #endif
62
63 #include "obstack.h"
64 #include "clewn.h"
65 #include "gdb.h"
66 #include "misc.h"
67
68 /*
69 * EMX doesn't have a global way of making open() use binary I/O.
70 * Use O_BINARY for all open() calls.
71 */
72 #if defined(__EMX__) || defined(__CYGWIN32__)
73 # define O_EXTRA O_BINARY
74 #else
75 # define O_EXTRA 0
76 #endif
77
78 #define CLEWN_HELP "Clewn version %s using readline library revision %s.\n\
79 \n\
80 Usage: clewn [-vc gvim_cmd] [-va gvim_args]\n\
81 [-gc gdb_cmd] [-ga gdb_args]\n\
82 [-p project] [-x pathnames_map]\n\
83 [-a] [-nb[:<host>[:<port>[:<passwd>]]]] [-d] [-r]\n\
84 \n\
85 -vc gvim_cmd gvim shell command or gvim pathname (default 'gvim')\n\
86 -va gvim_args gvim command line arguments\n\
87 -gc gdb_cmd gdb shell command or gdb pathname (default 'gdb')\n\
88 -ga gdb_args gdb command line arguments\n\
89 -p project project file name\n\
90 -x pathnames_map remote debugging\n\
91 -a enable assembly support\n\
92 -nb:<host>:<port>:<passwd> NetBeans connection parameters\n\
93 <host> default {$__NETBEANS_HOST|localhost}\n\
94 <port> default {$__NETBEANS_SOCKET|3219}\n\
95 <passwd> default {$__NETBEANS_VIM_PASSWORD|changeme}\n\
96 -d enable NetBeans debug mode\n\
97 -r do not set SO_REUSEADDR socket option\n\
98 \n\
99 Clewn listens on host:port, with host being a name or the IP address of one\n\
100 of the local network interfaces in standard dot notation.\n\n"
101
102 #define INPUTRC_FNAME "inputrc"
103 #define CLEWN_HISTORY_FILE "/.clewn_history"
104 #define CLEWN_KEYS_FILE "/.clewn_keys"
105 #define HISTORY_DFLT_SIZE 50
106 #define GVIM_DEFAULT_ARG " -c \"run clewn.vim\" -nb -g "
107
108 int p_asm = 0; /* assembly support when non zero */
109
110 static int module_state = -1; /* initial state (not OK, nor FAIL) */
111 static char clewn_buf[MAX_BUFFSIZE]; /* general purpose buffer */
112
113 static char * p_vim = NULL;
114 static char * p_vc = "gvim";/* gvim shell command or gvim pathname */
115 static char * p_va = ""; /* gvim command line arguments */
116 static char * p_gdb = NULL;
117 static char * p_gc = "gdb"; /* gdb shell command or gdb pathname */
118 static char * p_ga = ""; /* gdb command line arguments */
119 static char * p_project = NULL;/* project file name */
120 static char * p_pmap = NULL;/* remote debugging pathnames map */
121
122 static int clewn_debug = FALSE; /* debug flag */
123 static int showBalloon = FALSE; /* enable tooltips */
124 static char * p_gvar = VARIABLES_FNAME; /* variables buffer extension */
125 static char *clewn_tempdir = NULL; /* Clewn temporary directory */
126 static char *clewn_dir; /* Clewn directory */
127
128 static gdb_T *gdb = NULL; /* the gdb_T instance */
129 static int got_sigint = FALSE; /* TRUE when got SIGINT */
130 static int got_sigchld = FALSE; /* TRUE when got SIGCHLD */
131 static int got_tab = FALSE; /* TRUE when got a <TAB> */
132 static char *preprompt; /* previous prompt line when prompt is multi line */
133 static char *cplt_prmpt; /* current prompt for completion */
134 static int nl_output; /* when TRUE, no need to output a newline */
135 static char *key_command; /* command mapped from NetBeans key */
136 static int inside_readline = FALSE; /* TRUE when cli_getc() is called by readline() */
137
138 #define FUNMAP_SIZE 20
139 #define CLEWN_KEYMAP_SIZE (256 + FUNMAP_SIZE)
140 #define INTBITS (sizeof(int) * 8)
141
142 #define FUNCTION_INDEX(n) (((n) > 0 && (n) <= FUNMAP_SIZE) \
143 ? (256 + (n) - 1) : -1)
144
145 #define IS_KEY_FLAG(t,i) (((i) >= 0 && (i) < CLEWN_KEYMAP_SIZE) \
146 ? ((t)[(i) / INTBITS] & (1 << ((i) % INTBITS))) : FALSE)
147
148 #define SET_KEY_LINE(t,i) (((i) >= 0 && (i) < CLEWN_KEYMAP_SIZE) \
149 ? ((t)[(i) / INTBITS] |= (1 << ((i) % INTBITS))) : FALSE)
150
151 static char * keymap[CLEWN_KEYMAP_SIZE]; /* GDB commands array indexed by key */
152 static int keyline[CLEWN_KEYMAP_SIZE / INTBITS + 1]; /* %line flag bit map */
153 static int keytext[CLEWN_KEYMAP_SIZE / INTBITS + 1]; /* %text flag bit map */
154
155 /* The gdb keyword */
156 typedef struct
157 {
158 int type;
159 char *keyword; /* keyword */
160 char *tail; /* optional tail */
161 } token_T;
162
163 static token_T tokens[] =
164 {
165 {CMD_DETACH, "det", "ach" },
166 {CMD_SHELL, "she", "ll" },
167 {CMD_STEPI, "si", "" },
168 {CMD_STEPI, "stepi", "" },
169 {CMD_STEPI, "ni", "" },
170 {CMD_STEPI, "nexti", "" },
171 {CMD_EXECF, "fil", "e" },
172 {CMD_EXECF, "ex", "ec-file" },
173 {CMD_EXECF, "cor", "e-file" },
174 {CMD_BREAK, "b", "reak" },
175 {CMD_BREAK, "tb", "reak" },
176 {CMD_BREAK, "hb", "reak" },
177 {CMD_BREAK, "thb", "reak" },
178 {CMD_DISPLAY, "disp", "lay" },
179 {CMD_CREATEVAR, "cr", "eatevar" },
180 {CMD_UP_SILENT, "up-", "silently" },
181 {CMD_UP, "up", "" },
182 {CMD_DOWN_SILENT,"down-", "silently" },
183 {CMD_DOWN, "do", "wn" },
184 {CMD_FRAME, "f", "rame" },
185 {CMD_DISABLE, "disab", "le" },
186 {CMD_DELETE, "del", "ete" },
187 {CMD_SLECT_FRAME,"sel", "ect-frame" },
188 {CMD_SYMF, "sy", "mbol-file" },
189 {CMD_SYMF, "add-sy", "mbol-file" },
190 {CMD_SYMF, "so", "urce" },
191 {CMD_RESTART, "cl_", "restart" },
192 {CMD_QUIT, "q", "uit" },
193 {CMD_ANY, NULL, NULL }
194 };
195
196 /* The gdb pattern */
197 typedef struct
198 {
199 int id; /* pattern id */
200 char *str; /* string pattern */
201 regex_t *regprog; /* compiled regexp */
202 } pattern_T;
203
204 static pattern_T patterns[] =
205 {
206 {PAT_DIR, "^ *Source directories searched: *(.*)$", NULL},
207 {PAT_CHG_ANNO, "^ *set +a(n|nn|nno|nnot|nnota|nnotat|nnotate) +.*$", NULL},
208 {PAT_ADD, "^ *0x0*([0-9a-fA-F]+)", NULL},
209 {PAT_PID, "^ *a(t|tt|tta|ttac|ttach) +([0-9]+) *$", NULL},
210 #if defined(GDB_LVL2_SUPPORT) || defined(GDB_LVL3_SUPPORT)
211 {PAT_SOURCE, "^ *([^:]*):([^:]*):[^:]*:[^:]*:0x0*([0-9a-fA-F]+)$", NULL},
212 {PAT_QUERY, "^.*\\(y or n\\) *$", NULL},
213 {PAT_YES, "^ *(y|ye|yes) *$", NULL},
214 {PAT_SFILE, "^Symbols from *\"([^\"]+)", NULL},
215 {PAT_BP_CONT, "^ *(c) *|^ *(con)(t|ti|tin|tinu|tinue) *", NULL},
216 {PAT_ASM_FUNC, "^([^ ^(]+)(\\(.*\\))* .*in section .text$", NULL},
217 {PAT_ASM_FUNC_P, "^.*<([^ ^+]+)(\\+[0-9]+)*>$", NULL},
218 {PAT_FRAME, "^#[0-9]+ +0x0*([0-9a-fA-F]+)", NULL},
219 {PAT_HEIGHT, "^ *set +h(e|ei|eig|eigh|eight) +([0-9]+) *$", NULL},
220 #endif
221 #ifdef GDB_LVL2_SUPPORT
222 /* MUST add '.*' at end of each info (possibly last) breakpoint field
223 * pattern because GDB adds the hit count, in a new line, after
224 * printing the last field and within its annotation context */
225 {PAT_BP_ASM, "^<([^ ]+)\\+[0-9]+>.*$|^<([^ ]+)>.*$", NULL},
226 {PAT_BP_SOURCE, "^.* ([^ ]+):([0-9]+).*$", NULL},
227 {PAT_DISPLAY, "^ *dis(p|pl|pla|play) *$", NULL},
228 {PAT_DISPINFO, "^([0-9]+):", NULL},
229 {PAT_CREATEVAR, "^ *c(r|re|rea|reat|reate|reatev|reateva|reatevar) +(.*)$", NULL},
230 #endif
231 #ifdef GDB_LVL3_SUPPORT
232 {PAT_CRVAR_FMT, "^ *c(r|re|rea|reat|reate|reatev|reateva|reatevar) *(/[tdxo])* +(.*)$", NULL},
233 {PAT_INFO_FRAME, "^Stack level ([0-9]+), frame at ", NULL},
234 #endif
235 {0, NULL, NULL}
236 };
237
238 #if defined(GDB_LVL2_SUPPORT) || defined(GDB_LVL3_SUPPORT)
239 static char *inputrc; /* readline inputrc file name */
240
241 /* gdb readline inputrc file content */
242 static char *inputrc_array[] = {
243 "set show-all-if-ambiguous on\n",
244 "Control-u: unix-line-discard\n",
245 NULL
246 };
247 #endif
248
249 /* default key mappings with netbeans version < 2.3 (no specialKeys command) */
250 static char * old_keys_mapping[] =
251 {
252 "z:\032: # interrupt",
253 "B:info breakpoints:",
254 "L:info locals:",
255 "A:info args:",
256 "S:step:",
257 "I:stepi:",
258 "n:next:",
259 "N:nexti: # upper case",
260 "F:finish:",
261 "R:run:",
262 "Q:quit:",
263 "C:continue:",
264 "W:where:",
265 "u:up:",
266 "d:down:",
267 "b:break:%line # set breakpoint at current line",
268 "e:clear:%line # clear breakpoint at current line",
269 "k:break *:%text # set breakpoint at address at mouse position",
270 "h:clear *:%text # clear breakpoint at address at mouse position",
271 "p:print:%text # print value of word at mouse position",
272 "a:print *:%text # print value referenced by text at mouse position",
273 "j:createvar:%text # add variable (expression) at mouse position",
274 NULL
275 };
276
277 /* default key mappings with netbeans version >= 2.3 (with specialKeys command) */
278 static char * keys_mapping[] =
279 {
280 "C-Z:\032: # interrupt",
281 "B:info breakpoints:",
282 "L:info locals:",
283 "A:info args:",
284 "S:step:",
285 "I:stepi:",
286 "C-N:next:",
287 "X:nexti: # upper case",
288 "F:finish:",
289 "R:run:",
290 "Q:quit:",
291 "C:continue:",
292 "W:where:",
293 "C-U:up:",
294 "C-D:down:",
295 "C-B:break:%line # set breakpoint at current line",
296 "C-E:clear:%line # clear breakpoint at current line",
297 "C-K:break *:%text # set breakpoint at address at mouse position",
298 "C-H:clear *:%text # clear breakpoint at address at mouse position",
299 "C-P:print:%text # print value of word at mouse position",
300 "C-X:print *:%text # print value referenced by text at mouse position",
301 "C-J:createvar:%text # add variable (expression) at mouse position",
302 NULL
303 };
304
305 /* storage for mtrace hooks */
306 #if defined(GDB_MTRACE) && defined(HAVE_MTRACE)
307 __ptr_t (*s_malloc) (size_t, const void *);
308 void (*s_free) (void *, const void *);
309 __ptr_t (*s_realloc) (void *, size_t, const void *);
310 #endif /* GDB_MTRACE */
311
312 /* readline interface */
313 static int cli_init __ARGS((void));
314 #ifdef HAVE_RL_COMMAND_FUNC_T
315 static rl_command_func_t cli_complete;
316 #else
317 static int cli_complete __ARGS((void));
318 #endif
319 static int cli_getc __ARGS((FILE *));
320
321 /* Gdb process mgmt */
322 static RETSIGTYPE catch_sigint __ARGS((int));
323 static RETSIGTYPE catch_sigterm __ARGS((int));
324 static RETSIGTYPE catch_sigchld __ARGS((int));
325 static RETSIGTYPE catch_sigpipe __ARGS((int));
326 static void clewn_abort __ARGS((void));
327 static void clewn_getout __ARGS((void));
328 static void start_gdb_process __ARGS((void));
329 static int module_init __ARGS((void));
330 static void module_end __ARGS((void));
331 static void clear_gdb_T __ARGS((void));
332 static void exec_gdb __ARGS((void));
333 static int exec_gvim __ARGS((void));
334 static void clewn_keymap __ARGS((void));
335 static void write_project __ARGS((void));
336
337 /* Utilities */
338 static int clewn_mktmpdir __ARGS((void));
339 static void clewn_deltempdir __ARGS((void));
340 static void print_regerror __ARGS((int, regex_t *));
341 static void clear_readline __ARGS((void));
342 static void clewn_add_history __ARGS((char *));
343 static void parse_keyline(char *);
344 static void read_keysfile __ARGS((char *));
345 static void process_nb_event __ARGS((void));
346 static char * get_keymap __ARGS((nb_event_T *));
347
348 /*
349 * Main
350 */
351 int
main(int argc,char ** argv)352 main(int argc, char ** argv)
353 {
354 char * nbconn = NULL; /* NetBeans connection parameters */
355 char intr[2] = {KEY_INTERUPT, NUL};
356 int reuse_addr = TRUE;
357 char *line;
358
359 #if defined(GDB_MTRACE) && defined(HAVE_MTRACE)
360 mtrace();
361 mv_hooks();
362 #endif
363 /* register an abort function for when allocating memory fails */
364 xatabort(clewn_abort);
365 obstack_alloc_failed_handler = clewn_abort;
366
367 /* initialize readline */
368 rl_readline_name = "Clewn"; /* allow conditional parsing of ~/.inputrc */
369 rl_startup_hook = cli_init; /* change the binding of the <TAB> key */
370 rl_getc_function = cli_getc; /* to get a character from the input stream */
371 rl_initialize();
372 if (rl_outstream == NULL)
373 {
374 fprintf(stderr, "cannot initialize readline\n");
375 clewn_abort();
376 }
377
378 /*
379 * Get program options
380 */
381 argv++;
382 argc--;
383
384 while (argc > 0)
385 {
386 char *opt = *argv;
387
388 if (*opt == '-')
389 {
390 switch (*(opt + 1))
391 {
392 case 'v':
393 if (*(opt + 2) == 'c'|| *(opt + 2) == 'a') {
394 argv++;
395 argc--;
396 if (argc <= 0 || ((*argv)[0] == '-' && *(opt + 2) == 'c')) {
397 fprintf(stderr, "Cannot parse mandatory parameter of \"%s\"\n", opt);
398 clewn_abort();
399 }
400 /* -vc gvim_cmd - gvim shell command or gvim pathname (default 'gvim') */
401 if (*(opt + 2) == 'c')
402 p_vc = *argv;
403 /* -va gvim_args - gvim command line arguments */
404 else if (*(opt + 2) == 'a')
405 p_va = *argv;
406 }
407 else if (*(opt + 2) == NUL) { /* "-v" print help */
408 fprintf(stderr, CLEWN_HELP, PACKAGE_VERSION, rl_library_version);
409 clewn_abort();
410 }
411 else {
412 fprintf(stderr, "Unknown command line argument \"%s\"\n", opt);
413 clewn_abort();
414 }
415 break;
416
417 case 'g':
418 if (*(opt + 2) == 'c'|| *(opt + 2) == 'a') {
419 argv++;
420 argc--;
421 if (argc <= 0 || ((*argv)[0] == '-' && *(opt + 2) == 'c')) {
422 fprintf(stderr, "Cannot parse mandatory parameter of \"%s\"\n", opt);
423 clewn_abort();
424 }
425 /* -gc gdb_cmd - gdb shell command or gdb pathname (default 'gdb') */
426 if (*(opt + 2) == 'c')
427 p_gc = *argv;
428 /* -ga gdb_args - gdb command line arguments */
429 else if (*(opt + 2) == 'a')
430 p_ga = *argv;
431 }
432 else {
433 fprintf(stderr, "Unknown command line argument \"%s\"\n", opt);
434 clewn_abort();
435 }
436 break;
437
438 case 'p': /* "-p" project file name */
439 argv++;
440 argc--;
441 if (argc <= 0 || (*argv)[0] == '-') {
442 fprintf(stderr, "Cannot parse mandatory parameter of \"%s\"\n", opt);
443 clewn_abort();
444 }
445 p_project = *argv;
446 break;
447
448 case 'a': /* "-a" set assembly support */
449 p_asm = 1;
450 break;
451
452 case 't': /* "-t" enable displaying GDB state in a tooltip */
453 showBalloon = TRUE;
454 break;
455
456 case 'r': /* "-r" do not set SO_REUSEADDR socket option */
457 reuse_addr = FALSE;
458 break;
459
460 case 'x': /* "-x" remote debugging */
461 argv++;
462 argc--;
463 if (argc <= 0 || (*argv)[0] == '-') {
464 fprintf(stderr, "Cannot parse mandatory parameter of \"%s\"\n", opt);
465 clewn_abort();
466 }
467 p_pmap = *argv;
468 break;
469
470 case 'd': /* "-d" enable NetBeans debug output */
471 clewn_debug = TRUE;
472 break;
473
474 case 'n': /* "-nb:host:port:passwd" NetBeans parameters */
475 if (*(opt + 2) == 'b' && *(opt + 3) == ':')
476 nbconn = opt + 4;
477 break;
478
479 case 'h': /* "-h" print help */
480 fprintf(stderr, CLEWN_HELP, PACKAGE_VERSION, rl_library_version);
481 clewn_abort();
482
483 default:
484 fprintf(stderr, "Unknown command line argument \"%s\"\n", opt);
485 clewn_abort();
486 }
487 }
488 else
489 {
490 fprintf(stderr, "Unknown command line argument \"%s\"\n", opt);
491 clewn_abort();
492 }
493
494 argv++;
495 argc--;
496 }
497
498 signal(SIGINT, catch_sigint);
499 signal(SIGTERM, catch_sigterm);
500 signal(SIGCHLD, catch_sigchld);
501 signal(SIGPIPE, catch_sigpipe);
502
503 #if defined(GDB_MTRACE) && defined(HAVE_MTRACE)
504 /* the mtrace log file may get corrupted when forking a child
505 * because mtrace uses output buffering and flushes its buffer
506 * in the child when closing on exec
507 * do few dummies alloc to force the flush before the fork
508 * and avoid that problem
509 * */
510 {
511 #define DUMMY_ALLOC_COUNT 100 /* tune this value */
512 int i;
513 char * dummy;
514
515 for (i = 0; i < DUMMY_ALLOC_COUNT; i++)
516 if ((dummy = (char *)xmalloc(1)) != NULL)
517 xfree(dummy);
518 }
519 #endif
520
521 /* start GDB, do not output unneeded newlines */
522 nl_output = TRUE;
523 start_gdb_process();
524 nl_output = TRUE;
525
526 if (! GDB_STATE(gdb, GS_UP))
527 clewn_getout();
528
529 /* remote debugging and project file are not supported on level 2 */
530 if (gdb->mode == GDB_MODE_LVL2) {
531 p_pmap = NULL;
532 p_project = NULL;
533 }
534
535 /* listen on a NetBeans socket */
536 if (cnb_open(nbconn, &(gdb->var_buf), clewn_debug, reuse_addr, p_pmap) != OK)
537 clewn_getout();
538
539 /* the gdb instance number is used to define signs in vim
540 * with a different name for each instance of gdb */
541 cnb_set_instance(gdb->instance);
542
543 /* create variables buffer */
544 cnb_create_varbuf(gdb->var_name);
545
546 /* fork gvim if not doing remote debugging */
547 if (! p_pmap && exec_gvim() != OK)
548 clewn_getout();
549
550 while (IS_NETBEANS_CONNECTED && GDB_STATE(gdb, GS_UP))
551 {
552 /*
553 * 1 - Wait for GDB prompt
554 */
555 (void)cli_getc(NULL);
556
557 /* handle interrupt */
558 if (got_sigint)
559 {
560 got_sigint = FALSE;
561 gdb_docmd((gdb_handle_T *)gdb, intr); /* send KEY_INTERUPT to GDB */
562 continue;
563 }
564
565 /* should never happen (but make sure we do have a prompt) */
566 if (gdb->prompt == NULL)
567 gdb->prompt = clewn_strsave("(gdb) ");
568
569 /*
570 * 2 - Launch readline
571 */
572 /* output readline on a new line so as not to overwrite last line */
573 if (! nl_output)
574 fputs("\n", rl_outstream);
575 nl_output = TRUE;
576
577 inside_readline = TRUE;
578
579 if ((line = readline(gdb->prompt)) == NULL)
580 break;
581
582 inside_readline = FALSE;
583
584 if (gdb->note == ANO_PMT_FORMORE)
585 {
586 if (got_sigint || (strlen(line) == 1 && *line == 'q'))
587 got_sigint = TRUE; /* abort listing */
588 else
589 {
590 free(line);
591 gdb_docmd((gdb_handle_T *)gdb, ""); /* continue */
592 continue;
593 }
594 }
595
596 if (got_tab)
597 got_tab = FALSE;
598 else if (got_sigint)
599 {
600 got_sigint = FALSE;
601 gdb_docmd((gdb_handle_T *)gdb, intr); /* send KEY_INTERUPT to GDB */
602 }
603 else
604 {
605 clewn_add_history(line);
606
607 /* when debug mode and first char is an escape character,
608 * handle the line as a NetBeans command or function */
609 if (clewn_debug && (*line == DEBUG_ESC_CMD || *line == DEBUG_ESC_FUN))
610 cnb_send_debug((int)*line, line + 1);
611 else
612 gdb_docmd((gdb_handle_T *)gdb, line); /* send cmd to GDB */
613 }
614
615 free(line);
616
617 /* warn the user that the data socket is not connected */
618 if (cnb_get_datasock() < 0)
619 {
620 fputs("************************************************\n", rl_outstream);
621 fputs("The netbeans socket to Vim is not connected yet.\n", rl_outstream);
622 fputs("Please check that the netbeans_intg feature is compiled in your Vim version\n", rl_outstream);
623 fputs("by running the Vim command \":version\", and checking that this command\n", rl_outstream);
624 fputs("displays \"+netbeans_intg\".\n", rl_outstream);
625 fputs("************************************************\n", rl_outstream);
626 }
627
628 }
629
630 clewn_getout();
631 return 0;
632 }
633
634 /* Change the binding of the <TAB> key */
635 static int
cli_init()636 cli_init()
637 {
638 rl_bind_key((int)TAB, cli_complete);
639 return 0;
640 }
641
642 /* Handle completion */
643 static int
644 #ifdef HAVE_RL_COMMAND_FUNC_T
cli_complete(whatisthis,key)645 cli_complete(whatisthis, key)
646 int whatisthis;
647 int key;
648 #else
649 cli_complete()
650 #endif
651 {
652 char * cmd;
653
654 #ifdef HAVE_RL_COMMAND_FUNC_T
655 whatisthis = key = 0; /* keep compiler happy */
656 #endif
657
658 /* store the prompt for the completion result */
659 xfree(cplt_prmpt);
660 cplt_prmpt = clewn_strsave(gdb->prompt);
661
662 /* send readline content up to cursor and <TAB> to GDB */
663 cmd = clewn_strnsave(rl_line_buffer, rl_point + 1);
664 *(cmd + rl_point) = NUL; /* need rl_point first characters, not more */
665 strcat(cmd, "\t");
666 gdb_docmd((gdb_handle_T *)gdb, cmd); /* send it to GDB */
667 xfree(cmd);
668
669 got_tab = TRUE;
670 return 0;
671 }
672
673 /*
674 * Blocking poll/select on readline instream, GDB pseudo tty and NetBeans sockets.
675 * Return one character from instream.
676 * Return NL when got SIGINT or a GDB command mapped from a received NetBeans key.
677 * Return <SPACE> when got <TAB>.
678 * Return EOF when instream is NULL and got a GDB prompt.
679 * Return EOF when IO error or end of file.
680 */
681 static int
cli_getc(instream)682 cli_getc(instream)
683 FILE *instream; /* not NULL when called by readline */
684 {
685 int fd = -1; /* instream file descriptor */
686 int fdcon; /* connection socket file descriptor */
687 int fdata; /* data socket file descriptor */
688 pid_t wait_pid;
689 int ret;
690 #ifndef HAVE_SELECT
691 struct pollfd fds[4];
692 int nfd;
693 int gdb_idx;
694 int fdcon_idx;
695 int fdata_idx;
696 #else
697 int maxfd;
698 fd_set rfds;
699 #endif
700
701 if (instream != NULL && (fd = fileno(instream)) == -1)
702 return EOF;
703
704 do
705 {
706 ret = -1;
707
708 /* Source the project file when the netbeans session is up,
709 * so that we can set the breakpoints in vim */
710 if (gdb->project_file != NULL
711 && gdb->project_state == PROJ_INIT
712 && ! IS_OOBACTIVE(gdb)
713 && inside_readline
714 && cnb_state())
715 {
716 gdb->project_state = PROJ_SOURCEIT;
717 gdb_docmd((gdb_handle_T *)gdb, "");
718 }
719
720 /* GDB still running */
721 if (got_sigchld || gdb->pid == -1)
722 {
723 got_sigchld = FALSE;
724
725 if (gdb->pid != -1) /* got SIGCHLD */
726 {
727 wait_pid = waitpid(gdb->pid, NULL, WNOHANG);
728
729 if ((wait_pid == (pid_t)-1 && errno == ECHILD)
730 || wait_pid == gdb->pid)
731 {
732 gdb->pid = -1; /* got SIGCHLD from GDB */
733 fprintf(stderr, "GDB terminated \n");
734 }
735 }
736
737 if (gdb->pid == -1) /* shutdown and exit */
738 {
739 /* restart gdb if not quitting */
740 if (! GDB_STATE(gdb, GS_QUITTING)) {
741 gdb_close(gdb);
742 start_gdb_process();
743 }
744
745 if (! GDB_STATE(gdb, GS_UP))
746 clewn_getout();
747
748 }
749 }
750
751 /* got a prompt from GDB */
752 if (instream == NULL && gdb->prompt != NULL)
753 {
754 return EOF;
755 }
756
757 /* got SIGINT or an interrupt in the form of a NetBeans key command */
758 if (got_sigint || (key_command != NULL && *key_command == KEY_INTERUPT))
759 {
760 /* being called by readline */
761 if (instream != NULL)
762 {
763 rl_point = rl_end; /* at the end of the line */
764 rl_insert_text("Quit");
765 rl_redisplay();
766 }
767
768 /* consume the key command and emulate an interrupt */
769 if (key_command != NULL && *key_command == KEY_INTERUPT)
770 {
771 got_sigint = TRUE;
772 FREE(key_command);
773 }
774 return (int)NL;
775 }
776
777 /* got <TAB>:
778 * tell readline we are done
779 * stay on same line, but set nl_output so that further lines if
780 * any are printed below this line */
781 if (instream != NULL && got_tab)
782 {
783 rl_done = 1;
784 nl_output = FALSE;
785 return (int)' ';
786 }
787
788 /* insert winput_cmd in readline */
789 if (gdb->winput_cmd != NULL)
790 {
791 /* being called by readline */
792 /* we get a prompt from GDB when the completion
793 * result is a list (no need to provide one then) */
794 if (instream != NULL)
795 {
796 #ifdef HAVE_RL_REPLACE_LINE
797 rl_replace_line(gdb->winput_cmd, 0);
798 rl_point = rl_end; /* at the end of the line */
799 #else
800 rl_insert_text(gdb->winput_cmd);
801 #endif
802 rl_redisplay();
803 FREE(gdb->winput_cmd);
804 }
805 else
806 {
807 /* provide the prompt to enable readline in main */
808 /* assert: gdb->prompt == NULL */
809 if (gdb->cli_cmd.state == CS_QUERY)
810 {
811 /* a completion query: provide last line as prompt
812 * overwrite (identical) last line with readline prompt */
813 nl_output = TRUE;
814 gdb->prompt = clewn_strsave(gdb->line);
815 }
816 else
817 {
818 clear_readline();
819
820 /* a single completion result: provide last prompt
821 * as prompt and stay on same line */
822 nl_output = TRUE;
823 gdb->prompt = cplt_prmpt;
824 cplt_prmpt = NULL;
825 }
826 return EOF;
827 }
828 }
829
830 /* a GDB command mapped from a received NetBeans key */
831 if (instream != NULL && key_command != NULL)
832 {
833 rl_insert_text(key_command);
834 rl_redisplay();
835 FREE(key_command);
836 return (int)NL;
837 }
838
839 #ifndef HAVE_SELECT
840 nfd = 0;
841
842 if (fd >= 0)
843 {
844 fds[nfd].fd = fd;
845 fds[nfd].events = POLLIN;
846 nfd++;
847 }
848
849 if (GDB_STATE(gdb, GS_UP))
850 {
851 fds[nfd].fd = gdb->fd;
852 fds[nfd].events = POLLIN;
853 gdb_idx = nfd;
854 nfd++;
855 }
856
857 if ((fdcon = cnb_get_connsock()) >= 0)
858 {
859 fds[nfd].fd = fdcon;
860 fds[nfd].events = POLLIN;
861 fdcon_idx = nfd;
862 nfd++;
863 }
864
865 if ((fdata = cnb_get_datasock()) >= 0)
866 {
867 fds[nfd].fd = fdata;
868 fds[nfd].events = POLLIN;
869 fdata_idx = nfd;
870 nfd++;
871 }
872
873 if (nfd > 0)
874 ret = poll(fds, nfd, -1);
875
876 if (GDB_STATE(gdb, GS_UP) && ret > 0 && fds[gdb_idx].revents & POLLIN)
877 {
878 ret--;
879 if (gdb->parse_output != NULL)
880 (void)gdb->parse_output(gdb);
881 }
882
883 if (fdcon >= 0 && ret > 0 && fds[fdcon_idx].revents & POLLIN)
884 {
885 if (instream != NULL)
886 clear_readline();
887
888 ret--;
889 cnb_conn_evt();
890
891 if (instream != NULL) /* update readline display */
892 rl_forced_update_display();
893 }
894
895 if (fdata >= 0 && ret > 0 && fds[fdata_idx].revents & POLLIN)
896 {
897 if (instream != NULL)
898 clear_readline();
899
900 ret--;
901 process_nb_event();
902
903 clewn_keymap(); /* initialize keys mappings (only once) */
904
905 if (instream != NULL) /* update readline display */
906 rl_forced_update_display();
907 }
908
909 #else /* HAVE_SELECT */
910 maxfd = -1;
911 FD_ZERO(&rfds);
912
913 if (fd >= 0)
914 {
915 FD_SET(fd, &rfds);
916 if (maxfd < fd)
917 maxfd = fd;
918 }
919
920 if (GDB_STATE(gdb, GS_UP))
921 {
922 FD_SET(gdb->fd, &rfds);
923 if (maxfd < gdb->fd)
924 maxfd = gdb->fd;
925 }
926
927 if ((fdcon = cnb_get_connsock()) >= 0)
928 {
929 FD_SET(fdcon, &rfds);
930 if (maxfd < fdcon)
931 maxfd = fdcon;
932 }
933
934 if ((fdata = cnb_get_datasock()) >= 0)
935 {
936 FD_SET(fdata, &rfds);
937 if (maxfd < fdata)
938 maxfd = fdata;
939 }
940
941 if (maxfd >= 0)
942 ret = select(maxfd + 1, &rfds, NULL, NULL, NULL);
943
944 if (GDB_STATE(gdb, GS_UP) && ret > 0 && FD_ISSET(gdb->fd, &rfds))
945 {
946 ret--;
947 if (gdb->parse_output != NULL)
948 (void)gdb->parse_output(gdb);
949 }
950
951 if (fdcon >= 0 && ret > 0 && FD_ISSET(fdcon, &rfds))
952 {
953 if (instream != NULL)
954 clear_readline();
955
956 ret--;
957 cnb_conn_evt();
958
959 if (instream != NULL) /* update readline display */
960 rl_forced_update_display();
961 }
962
963 if (fdata >= 0 && ret > 0 && FD_ISSET(fdata, &rfds))
964 {
965 if (instream != NULL)
966 clear_readline();
967
968 ret--;
969 process_nb_event();
970
971 clewn_keymap(); /* initialize keys mappings (only once) */
972
973 if (instream != NULL) /* update readline display */
974 rl_forced_update_display();
975 }
976 #endif
977 } while ((ret == 0 || (ret < 0 && errno == EINTR)) && IS_NETBEANS_CONNECTED);
978
979 if (ret > 0)
980 {
981 if (instream != NULL)
982 return rl_getc(instream);
983 }
984 else if (IS_NETBEANS_CONNECTED)
985 perror("select() failed in cli_getc()");
986
987 return EOF;
988 }
989
990 /* Handle SIGINT signal */
991 static RETSIGTYPE
catch_sigint(sig)992 catch_sigint(sig)
993 int sig;
994 {
995 if (sig == SIGINT) {} /* keep compiler happy */
996
997 got_sigint = TRUE;
998 signal(SIGINT, catch_sigint);
999 }
1000
1001 /* Handle SIGTERM signal */
1002 static RETSIGTYPE
catch_sigterm(sig)1003 catch_sigterm(sig)
1004 int sig;
1005 {
1006 if (sig == SIGTERM) {} /* keep compiler happy */
1007
1008 signal(SIGCHLD, SIG_IGN);
1009 signal(SIGPIPE, SIG_IGN);
1010 signal(SIGTERM, SIG_IGN);
1011
1012 clewn_getout(); /* terminate GDB */
1013 }
1014
1015 /* Handle SIGCHLD signal */
1016 static RETSIGTYPE
catch_sigchld(sig)1017 catch_sigchld(sig)
1018 int sig;
1019 {
1020 if (sig == SIGCHLD) {} /* keep compiler happy */
1021
1022 got_sigchld = TRUE;
1023 signal(SIGCHLD, catch_sigchld);
1024 }
1025
1026 /* Handle SIGPIPE signal */
1027 static RETSIGTYPE
catch_sigpipe(sig)1028 catch_sigpipe(sig)
1029 int sig;
1030 {
1031 if (sig == SIGPIPE) {} /* keep compiler happy */
1032
1033 signal(SIGCHLD, SIG_IGN);
1034 signal(SIGPIPE, SIG_IGN);
1035 signal(SIGTERM, SIG_IGN);
1036
1037 clear_readline();
1038 if (clewn_debug)
1039 fprintf(stderr, "disconnected by peer in write()\n");
1040 clewn_getout(); /* terminate GDB */
1041 }
1042
1043 /** Abort clewn */
1044 static void
clewn_abort()1045 clewn_abort()
1046 {
1047 /* we MUST NOT call any function that allocates memory */
1048 if (gdb != NULL)
1049 gdb_close(gdb); /* terminate GDB */
1050
1051 #if defined(GDB_MTRACE) && defined(HAVE_MTRACE)
1052 muntrace();
1053 #endif
1054 exit(EXIT_FAILURE);
1055 }
1056
1057 #define EXIT_TIMEOUT 10000
1058 /** Terminate clewn */
1059 static void
clewn_getout()1060 clewn_getout()
1061 {
1062 int wtime = EXIT_TIMEOUT; /* msecs */
1063 int fdata = cnb_get_datasock();
1064 int rc;
1065 # ifndef HAVE_SELECT
1066 struct pollfd fds;
1067 # else
1068 struct timeval tv;
1069 struct timeval start_tv;
1070 fd_set rfds;
1071
1072 signal(SIGINT, SIG_IGN);
1073 signal(SIGTERM, SIG_IGN);
1074 signal(SIGCHLD, SIG_IGN);
1075 signal(SIGPIPE, SIG_IGN);
1076
1077 /* prevent recursion */
1078 if (gdb == NULL)
1079 return;
1080
1081 rl_cleanup_after_signal(); /* reset terminal so we can printf */
1082 cnb_saveAndExit();
1083
1084 # ifdef HAVE_GETTIMEOFDAY
1085 gettimeofday(&start_tv, NULL);
1086 # endif
1087 # endif
1088
1089 /* Give vim a chance to close the netbeans socket before EXIT_TIMEOUT,
1090 * in conformance with the netbeans protocol */
1091 while (fdata >=0 && wtime > 0) {
1092 # ifndef HAVE_SELECT
1093 fds.fd = fdata;
1094 fds.events = POLLIN;
1095
1096 rc = poll(&fds, 1, 200);
1097 # else
1098 FD_ZERO(&rfds);
1099 FD_SET(fdata, &rfds);
1100
1101 tv.tv_sec = 0; /* do a select every 200 ms, to update the printf */
1102 tv.tv_usec = 200000;
1103
1104 rc = select(fdata + 1, &rfds, NULL, NULL, &tv);
1105 # endif
1106
1107 if ((rc == -1 && errno == EINTR) || rc == 0) {
1108 /* compute remaining wait time */
1109 # if ! defined(HAVE_SELECT) || ! defined(HAVE_GETTIMEOFDAY)
1110 /* guess: interrupted halfway, gdb processing 10 msecs */
1111 wtime = wtime / 2 - 10L;
1112 # else
1113 gettimeofday(&tv, NULL);
1114 wtime -= (tv.tv_sec - start_tv.tv_sec) * 1000L
1115 + (tv.tv_usec - start_tv.tv_usec) / 1000L;
1116 start_tv.tv_sec = tv.tv_sec;
1117 start_tv.tv_usec = tv.tv_usec;
1118 # endif
1119 }
1120 else if (rc < 0)
1121 break;
1122 else if (read(fdata, clewn_buf, MAX_BUFFSIZE) <= 0)
1123 break;
1124 else
1125 wtime = EXIT_TIMEOUT;
1126
1127 printf("\rWaiting for gvim to close netbeans socket, exit in %02d s", wtime / 1000);
1128 fflush(stdout);
1129 }
1130
1131 printf("\r \n");
1132 if (wtime <= 0)
1133 printf("Vim is still up and running\n");
1134
1135 gdb_close(gdb); /* terminate GDB */
1136 module_end(); /* release module resources */
1137 clear_gdb_T();
1138 FREE(gdb);
1139 cnb_close(); /* close NetBeans sockets */
1140
1141 #if defined(GDB_MTRACE) && defined(HAVE_MTRACE)
1142 muntrace();
1143 #endif
1144 exit(EXIT_SUCCESS);
1145 }
1146
1147 /** Send a cmd to gdb */
1148 void
gdb_docmd(gdb_inst,cmd)1149 gdb_docmd(gdb_inst, cmd)
1150 gdb_handle_T *gdb_inst;
1151 char *cmd; /* gdb cmd */
1152 {
1153 gdb_T *this = (gdb_T *)gdb_inst;
1154
1155 if (this == NULL || ! GDB_STATE(this, GS_UP))
1156 return;
1157
1158 /* accept one cmd at a time, allow intr */
1159 if (cmd != NULL && *cmd != NUL && *(cmd + strlen(cmd) - 1) == KEY_INTERUPT)
1160 this->oob.state |= OS_INTR;
1161 else if (this->oob.state & OS_CMD)
1162 {
1163 if (cmd != NULL && *cmd != NUL)
1164 fprintf(stderr, "GDB busy: command discarded, please retry\n");
1165 return;
1166 }
1167 else
1168 this->oob.idx = -1; /* needed when last oob was aborted with OS_QUITs */
1169 this->oob.state |= OS_CMD;
1170
1171 /* reset both prompts */
1172 FREE(this->prompt);
1173 FREE(preprompt);
1174
1175 /* call mode specific docmd */
1176 if (this->gdb_docmd != NULL)
1177 this->gdb_docmd(this, cmd);
1178 }
1179
1180 /** Set the cmd to be inserted at start of readline */
1181 void
gdb_setwinput(gdb_inst,cmd)1182 gdb_setwinput(gdb_inst, cmd)
1183 gdb_handle_T *gdb_inst;
1184 char *cmd; /* cmd to insert */
1185 {
1186 gdb_T *this = (gdb_T *)gdb_inst;
1187
1188 if (this == NULL)
1189 return;
1190
1191 if (cmd == NULL)
1192 cmd = "";
1193
1194 if (strchr(cmd, (int)NL) != NULL) /* assert no NL in cmd */
1195 return;
1196
1197 xfree(this->winput_cmd);
1198 this->winput_cmd = clewn_strsave(cmd);
1199 }
1200
1201 /** Return TRUE if we are opening the gdb input-line window */
1202 int
gdb_iswinput(gdb_inst)1203 gdb_iswinput(gdb_inst)
1204 gdb_handle_T *gdb_inst;
1205 {
1206 return (gdb_inst != NULL ? ((gdb_T *)gdb_inst)->winput_cmd != NULL : FALSE);
1207 }
1208
1209 /* Start a gdb process */
1210 static void
start_gdb_process()1211 start_gdb_process()
1212 {
1213 if (module_init() != OK)
1214 return;
1215
1216 gdb->project_file = p_project;
1217 gdb->project_state = PROJ_INIT;
1218
1219 exec_gdb();
1220
1221 /* gdb instance count */
1222 ++gdb->instance;
1223
1224 /* done later, after netbeans init, for the first gdb instance */
1225 if (gdb->instance != 1) {
1226 /* the gdb instance number is used to define signs in vim
1227 * with a different name for each instance of gdb */
1228 cnb_set_instance(gdb->instance);
1229
1230 /* empty the variables buffer */
1231 cnb_create_varbuf(gdb->var_name);
1232
1233 /* free mode specific data within gdb_T */
1234 if (gdb->clear_gdb_T != NULL)
1235 gdb->clear_gdb_T(gdb);
1236
1237 /* unlink all asm buffers */
1238 cnb_unlink_asm();
1239 }
1240 }
1241
1242 /*
1243 * Initialize this module: set inputrc file, define signs, compile regexp.
1244 * Return OK when succcess, FAIL otherwise.
1245 */
1246 static int
module_init()1247 module_init()
1248 {
1249 FILE *fd;
1250 char *fname = NULL;
1251 char *histsize;
1252 pattern_T *pat;
1253 int errcode;
1254 int len;
1255 #if defined(GDB_LVL2_SUPPORT) || defined(GDB_LVL3_SUPPORT)
1256 char **p;
1257 FILE * stream;
1258 #endif
1259
1260 if (module_state == -1)
1261 {
1262 module_state = FAIL; /* do it only once */
1263
1264 /* build gvim and gdb exec strings */
1265 gdb_cat(&p_vim, p_vc);
1266 gdb_cat(&p_vim, GVIM_DEFAULT_ARG);
1267 if (p_project != NULL)
1268 gdb_cat(&p_vim, "-c \"let clewn_project=1\" ");
1269 gdb_cat(&p_vim, p_va);
1270
1271 gdb_cat(&p_gdb, p_gc);
1272 gdb_cat(&p_gdb, " ");
1273 gdb_cat(&p_gdb, p_ga);
1274
1275 /* create a gdb_T instance */
1276 gdb = (gdb_T *)xcalloc(sizeof(gdb_T));
1277 gdb->version = PACKAGE_VERSION;
1278
1279 clear_gdb_T();
1280
1281 if (clewn_mktmpdir() != OK) /* create the tmp directory */
1282 goto fin;
1283
1284 /* get clewn_dir */
1285 if ((clewn_dir = getenv("CLEWNDIR")) == NULL)
1286 clewn_dir = getenv("HOME");
1287
1288 /* load readline history */
1289 if (clewn_dir != NULL && *clewn_dir != NUL)
1290 {
1291 gdb_cat(&fname, clewn_dir);
1292 gdb_cat(&fname, CLEWN_HISTORY_FILE);
1293 (void)read_history(fname);
1294 FREE(fname);
1295 }
1296
1297 /* set history max size */
1298 if ((histsize = getenv("HISTSIZE")) != NULL && (len = atoi(histsize)) > 0)
1299 stifle_history(len);
1300 else
1301 stifle_history(HISTORY_DFLT_SIZE);
1302
1303 #if defined(GDB_LVL2_SUPPORT) || defined(GDB_LVL3_SUPPORT)
1304 /* Set gdb readline inputrc file contents
1305 * We don't know yet if we are going to use GDB/MI, so we need to
1306 * setup inputrc just in case, even though it might never be used */
1307 if ((stream = clewn_opentmpfile(INPUTRC_FNAME, &inputrc, 1)) != NULL)
1308 {
1309 for (p = inputrc_array; *p; p++)
1310 fputs(*p, stream);
1311 fclose(stream);
1312 setenv("INPUTRC", inputrc, TRUE);
1313 }
1314 #endif
1315
1316 /* Compile patterns */
1317 for (pat = patterns; pat->str != NULL; pat++)
1318 {
1319 pat->regprog = (regex_t *)xcalloc(sizeof(regex_t));
1320
1321 if ((errcode = regcomp(pat->regprog, pat->str, REG_EXTENDED)) != 0)
1322 {
1323 print_regerror(errcode, pat->regprog);
1324 regfree(pat->regprog);
1325 FREE(pat->regprog);
1326 goto fin;
1327 }
1328 }
1329
1330 /* create variables file */
1331 if (p_gvar != NULL && *p_gvar != NUL)
1332 {
1333 if ((fd = clewn_opentmpfile(p_gvar, &(gdb->var_name), 1)) != NULL)
1334 fclose(fd);
1335 }
1336
1337 module_state = OK;
1338 }
1339 fin:
1340 return module_state;
1341 }
1342
1343 /* Release module resources */
1344 static void
module_end()1345 module_end()
1346 {
1347 char *fname = NULL;
1348 pattern_T *pat;
1349 int i;
1350
1351 module_state = -1;
1352
1353 xfree(p_vim);
1354 xfree(p_gdb);
1355
1356 # if defined(GDB_LVL2_SUPPORT) || defined(GDB_LVL3_SUPPORT)
1357 if (inputrc != NULL)
1358 FREE(inputrc);
1359 # endif
1360
1361 clewn_deltempdir(); /* remove temporary directory and its content */
1362
1363 xfree(preprompt);
1364 xfree(cplt_prmpt);
1365 xfree(key_command);
1366
1367 /* patterns an tokens */
1368 for (pat = patterns; pat->str != NULL; pat++)
1369 {
1370 if (pat->regprog != NULL)
1371 {
1372 regfree(pat->regprog);
1373 FREE(pat->regprog);
1374 }
1375 }
1376
1377 for (i = 0; i < CLEWN_KEYMAP_SIZE; i++)
1378 xfree(keymap[i]);
1379
1380 /* write readline history */
1381 if (clewn_dir != NULL && *clewn_dir != NUL)
1382 {
1383 gdb_cat(&fname, clewn_dir);
1384 gdb_cat(&fname, CLEWN_HISTORY_FILE);
1385 (void)write_history(fname);
1386 xfree(fname);
1387 }
1388 }
1389
1390 /* Initialize a gdb_T structure */
1391 static void
clear_gdb_T()1392 clear_gdb_T()
1393 {
1394 if (gdb != NULL)
1395 {
1396 gdb->instance = 0;
1397 gdb->fd = -1;
1398 gdb->pid = (pid_t)-1;
1399 #if defined(GDB_LVL2_SUPPORT) || defined(GDB_LVL3_SUPPORT)
1400 gdb->height = 0;
1401 #endif
1402 gdb->state = GS_INIT;
1403 FREE(gdb->status);
1404 gdb->recurse = 0;
1405
1406 gdb->cmd_type = CMD_ANY;
1407 #if defined(GDB_LVL2_SUPPORT) || defined(GDB_LVL3_SUPPORT)
1408 gdb->cli_cmd.state = CS_START;
1409 gdb->cli_cmd.cnt = 0;
1410 FREE(gdb->cli_cmd.gdb);
1411 FREE(gdb->cli_cmd.readline);
1412 FREE(gdb->cli_cmd.echoed);
1413 #endif
1414
1415 FREE(gdb->firstcmd); /* not used by clewn */
1416 FREE(gdb->prompt);
1417 FREE(gdb->pwd);
1418 FREE(gdb->args);
1419 FREE(gdb->winput_cmd);
1420 FREE(gdb->directories);
1421 FREE(gdb->sfile);
1422
1423 #if defined(GDB_LVL2_SUPPORT) || defined(GDB_LVL3_SUPPORT)
1424 gdb->note = ANO_NONE;
1425 gdb->annoted = FALSE;
1426 gdb->newline = FALSE;
1427 FREE(gdb->annotation);
1428 #endif
1429 FREE(gdb->line);
1430 FREE(gdb->pc);
1431 FREE(gdb->frame_pc);
1432 FREE(gdb->oob_result);
1433 FREE(gdb->asm_add);
1434 FREE(gdb->asm_func);
1435
1436 #if defined(GDB_LVL2_SUPPORT) || defined(GDB_LVL3_SUPPORT)
1437 gdb->bp_state = 0;
1438 gdb_free_bplist(&(gdb->tmplist));
1439 #endif
1440 FREE(gdb->record);
1441 gdb->cont = FALSE;
1442 gdb_free_bplist(&(gdb->bpinfo));
1443 gdb->frame_curlvl = -1;
1444 gdb->frame_lnum = (linenr_T) -1;
1445 FREE(gdb->frame_fname);
1446
1447 gdb->fr_buf = -1;
1448 gdb->var_buf = -1;
1449 FREE(gdb->var_name);
1450 FREE(gdb->balloon_txt);
1451
1452 gdb->oob.state = 0;
1453 gdb->oob.idx = -1;
1454
1455 gdb->pool.buf = -1;
1456 FREE(gdb->pool.name);
1457 gdb->pool.hilite = FALSE;
1458
1459 /* free mode specific data within gdb_T */
1460 if (gdb->clear_gdb_T != NULL)
1461 gdb->clear_gdb_T(gdb);
1462
1463 gdb->oobfunc = NULL;
1464 gdb->parse_output = NULL;
1465 gdb->gdb_docmd = NULL;
1466 gdb->var_delete = NULL;
1467 gdb->clear_gdb_T = NULL;
1468 }
1469 }
1470
1471 /* Spawn a gdb process */
1472 static void
exec_gdb()1473 exec_gdb()
1474 {
1475 char *err = NULL;
1476 int fd = -1; /* slave pty file descriptor */
1477 char *tty; /* pty name */
1478 # ifdef HAVE_TERMIOS_H
1479 struct termios tio;
1480 # else
1481 struct termio tio;
1482 # endif
1483
1484 /* process already running */
1485 if (gdb->pid != (pid_t)-1 && waitpid(gdb->pid, NULL, WNOHANG) == 0)
1486 return;
1487
1488 /* Open pty */
1489 if ((gdb->fd = OpenPTY(&tty)) < 0
1490 || (fd = open(tty, O_RDWR|O_NOCTTY|O_EXTRA, 0)) < 0
1491 || SetupSlavePTY(fd) == -1)
1492 {
1493 err = "Cannot open gdb pty\n";
1494 goto err;
1495 }
1496
1497 /* Set terminal attributes */
1498 # ifdef HAVE_TERMIOS_H
1499 if (tcgetattr(fd, &tio) == 0)
1500 # else
1501 if (ioctl(fd, TCGETA, &tio) >= 0)
1502 # endif
1503 {
1504 tio.c_oflag &= ~ONLCR; /* don't map NL to CR-NL on output */
1505 tio.c_cc[VINTR] = KEY_INTERUPT;
1506 # ifdef HAVE_TERMIOS_H
1507 if (tcsetattr(fd, TCSAFLUSH, &tio) != 0)
1508 # else
1509 if (ioctl(fd, TCSETA, &tio) < 0)
1510 # endif
1511 {
1512 err = "Cannot set gdb pty\n";
1513 goto err;
1514 }
1515 }
1516 else
1517 {
1518 err = "Cannot get gdb pty\n";
1519 goto err;
1520 }
1521
1522 # if defined(GDB_LVL2_SUPPORT) || defined(GDB_LVL3_SUPPORT)
1523 # if defined(TIOCGWINSZ) && defined(TIOCSWINSZ)
1524 {
1525 struct winsize win;
1526
1527 /* set tty height */
1528 if (ioctl(0, TIOCGWINSZ, &win) >= 0)
1529 {
1530 /* Prevent use of a low height size because of "prompt-for-continue" messages.
1531 * The annotations lines printed by GDB are part of the lines count that
1532 * is used by GDB for "prompt-for-continue" messages, however we do not
1533 * display them. */
1534 if (win.ws_row < MIN_SCREEN_HEIGHT)
1535 {
1536 fprintf(stderr, "Please retry with a larger screen,\n");
1537 fprintf(stderr, "the screen height must be at least %d lines.\n", MIN_SCREEN_HEIGHT);
1538 fprintf(stderr, "On a console, try the unix command: \"stty rows 16\" for example.\n");
1539 goto err;
1540 }
1541
1542 /* see the comment in gdb_parse_output_cli() about
1543 * the "--More--" prompt */
1544 win.ws_row = LPP_LINES;
1545 if (ioctl(fd, TIOCSWINSZ, &win) >= 0)
1546 gdb->height = win.ws_row;
1547 }
1548 }
1549 # endif
1550 # endif
1551
1552 /* Fork */
1553 if ((gdb->pid = fork()) == (pid_t)-1)
1554 {
1555 err = "Cannot fork gdb\n";
1556 goto err;
1557 }
1558 /* The child */
1559 else if (gdb->pid == (pid_t)0)
1560 {
1561 /* Grab control of terminal (from `The GNU C Library' (glibc-2.3.1)) */
1562 setsid();
1563 # ifdef TIOCSCTTY
1564 if (ioctl(fd, TIOCSCTTY, (char *)NULL) == -1)
1565 _exit(1);
1566 # else
1567 { int newfd;
1568 char *fdname = ttyname(fd);
1569
1570 /* This might work (it does on Linux) */
1571 if (fdname)
1572 {
1573 if (fd != 0)
1574 close (0);
1575 if (fd != 1)
1576 close (1);
1577 if (fd != 2)
1578 close (2);
1579 newfd = open(fdname, O_RDWR);
1580 close(newfd);
1581 }
1582 }
1583 # endif
1584
1585 close(0); dup(fd);
1586 close(1); dup(fd);
1587 close(2); dup(fd);
1588
1589 if (fd > 2)
1590 close(fd);
1591
1592 close(gdb->fd); /* close master pty */
1593
1594 # ifdef GDB_MI_SUPPORT
1595 if (p_gdbmi)
1596 {
1597 /* MI mi2 is available starting with GDB 6.0 */
1598 execlp(p_gc, p_gc, "--interpreter=mi2", NULL);
1599 _exit(EXIT_FAILURE);
1600 }
1601 # endif
1602 # if defined(GDB_LVL2_SUPPORT) || defined(GDB_LVL3_SUPPORT)
1603 clewn_exec(p_gdb);
1604 # endif /* defined(GDB_LVL2_SUPPORT) || defined(GDB_LVL3_SUPPORT) */
1605
1606 _exit(EXIT_FAILURE);
1607 }
1608 /* The parent */
1609 else
1610 {
1611 close(fd);
1612
1613 # ifdef GDB_MI_SUPPORT
1614 if (p_gdbmi)
1615 {
1616 gdb->state |= GS_UP;
1617 if (gdb_setup_mi(gdb) != OK)
1618 {
1619 gdb_cat(&err, "Cannot start GDB program \"");
1620 gdb_cat(&err, p_gc);
1621 gdb_cat(&err, "\" (MI)\n");
1622 if (err != NULL)
1623 {
1624 fprintf(stderr, err);
1625 xfree(err);
1626 }
1627 gdb->state = GS_INIT;
1628 }
1629 }
1630 # else
1631 # if defined(GDB_LVL2_SUPPORT) || defined(GDB_LVL3_SUPPORT)
1632 fprintf(stderr, "guessing level, please wait...\r");
1633
1634 gdb->state |= GS_UP;
1635 if (gdb_setup_cli(gdb) != OK)
1636 gdb->state = GS_INIT;
1637
1638 # endif
1639 # endif
1640
1641 return;
1642 }
1643 err:
1644 if (gdb->fd >= 0) {
1645 close(gdb->fd);
1646 gdb->fd = -1;
1647 }
1648 if (fd >= 0)
1649 close(fd);
1650 if (err != NULL)
1651 fprintf(stderr, err);
1652 return;
1653 }
1654
1655 /* Spawn a gdb process; return OK when sucess, FAIL otherwise */
1656 static int
exec_gvim()1657 exec_gvim()
1658 {
1659 char dummy;
1660 int pipefd[2]; /* pipe between parent and child */
1661 int pipe_error;
1662 pid_t pid;
1663
1664 /* setup a pipe between the child and the parent, so that the parent
1665 * knows when the child has done the setsid() call and is allowed to exit */
1666 pipe_error = (pipe(pipefd) < 0);
1667
1668 /* Fork */
1669 if ((pid = fork()) == (pid_t)-1)
1670 {
1671 fprintf(stderr, "Cannot fork gvim\n");
1672 return FAIL;
1673 }
1674 /* The child */
1675 else if (pid == (pid_t)0)
1676 {
1677 setsid();
1678
1679 if (! pipe_error)
1680 {
1681 close(pipefd[0]);
1682 close(pipefd[1]);
1683 }
1684
1685 close(0);
1686 close(1);
1687 close(2);
1688 cnb_close(); /* close sockets */
1689
1690 clewn_exec(p_vim);
1691
1692 _exit(EXIT_FAILURE);
1693 }
1694 /* The parent */
1695 else
1696 {
1697 if (! pipe_error)
1698 {
1699 /* the read returns when the child closes the pipe (or when
1700 * the child dies for some reason) */
1701 close(pipefd[1]);
1702 (void)read(pipefd[0], &dummy, (size_t)1);
1703 close(pipefd[0]);
1704 }
1705
1706 return OK;
1707 }
1708 }
1709
1710 /* Initialize key mappings */
1711 static void
clewn_keymap()1712 clewn_keymap()
1713 {
1714 static int keymap_done = 0;
1715 char *fname = NULL;
1716 int key;
1717 char ** line;
1718 char * ptr;
1719 char ** default_keymap;
1720
1721 /* map the keys in vim - do it only once and when the data socket is opened */
1722 if (! keymap_done && cnb_state()) {
1723 keymap_done = 1;
1724
1725 /* use the default table, depending on netbeans version */
1726 if (cnb_specialKeys())
1727 default_keymap = keys_mapping;
1728 else
1729 default_keymap = old_keys_mapping;
1730
1731 /* read the default keys mapping */
1732 for (line = default_keymap; *line != NULL; line++)
1733 {
1734 /* make a copy so that it's writeable */
1735 if ((ptr = clewn_strsave(*line)) != NULL)
1736 {
1737 parse_keyline(ptr);
1738 xfree(ptr);
1739 }
1740 }
1741
1742 /* read the key mappings user configuration file*/
1743 if (clewn_dir != NULL && *clewn_dir != NUL)
1744 {
1745 gdb_cat(&fname, clewn_dir);
1746 gdb_cat(&fname, CLEWN_KEYS_FILE);
1747 read_keysfile(fname);
1748 xfree(fname);
1749 }
1750
1751 /* if the netbeans version supports specialKeys */
1752 if (cnb_specialKeys()) {
1753 /* control chars */
1754 for (key='A'; key<='_'; key++)
1755 if (keymap[key - '@'] != NULL)
1756 cnb_keymap(KEYMAP_CONTROL, key);
1757
1758 /* upper case chars */
1759 for (key='A'; key<='Z'; key++)
1760 if (keymap[key] != NULL)
1761 cnb_keymap(KEYMAP_UPPERCASE, key);
1762
1763 /* function keys */
1764 for (key=1; key<=FUNMAP_SIZE; key++)
1765 if (keymap[FUNCTION_INDEX(key)] != NULL)
1766 cnb_keymap(KEYMAP_FUNCTION, key);
1767 }
1768 }
1769 }
1770
1771 /* Write project file */
1772 static void
write_project()1773 write_project()
1774 {
1775 char * res = NULL;
1776 int bp_count = 0;
1777 FILE *stream;
1778 bpinfo_T *p;
1779 char * source_file;
1780 int lnum;
1781
1782 if (gdb->project_file == NULL) /* no project file */
1783 return;
1784
1785 if (gdb->sfile == NULL) { /* no debuggee */
1786 fprintf(stderr, "Error: no target file, the project is not written to: %s\n", gdb->project_file);
1787 goto end;
1788 }
1789
1790 if ((stream = fopen(gdb->project_file, "w+")) != NULL) {
1791 fputs("# Project file automatically generated by clewn.\n", stream);
1792
1793 if (gdb->pwd != NULL) /* current working directory */
1794 fputs(gdb->pwd, stream);
1795
1796 gdb_cat(&res, "file ");
1797 gdb_cat(&res, gdb->sfile);
1798 gdb_cat(&res, "\n");
1799 fputs(res, stream); /* debuggee */
1800 FREE(res);
1801
1802 if (gdb->args != NULL) /* debuggee command line args */
1803 fputs(gdb->args, stream);
1804
1805 for (p = gdb->bpinfo; p != NULL; p = p->next) {
1806 if ((source_file=cnb_filename(p->buf)) != NULL) {
1807 bp_count++;
1808
1809 /* get the new sign position in the vim buffer */
1810 lnum = cnb_buf_getsign(p->buf, BP_SIGN_ID(p->id));
1811 if (lnum == 0)
1812 lnum = (int)p->lnum; /* use the original value */
1813
1814 gdb_cat(&res, "break ");
1815 gdb_cat(&res, source_file);
1816 gdb_cat(&res, ":");
1817 gdb_cat(&res, gdb_itoa(lnum));
1818 gdb_cat(&res, "\n");
1819 fputs(res, stream); /* breakpoint */
1820 FREE(res);
1821
1822 /* handle disabled breakpoints */
1823 if (! p->enabled) {
1824 gdb_cat(&res, "disable ");
1825 gdb_cat(&res, gdb_itoa(bp_count));
1826 gdb_cat(&res, "\n");
1827 fputs(res, stream); /* disabled breakpoint */
1828 FREE(res);
1829 }
1830 }
1831 }
1832
1833 fclose(stream);
1834 }
1835 else
1836 fprintf(stderr, "Error: cannot write project in %s\n", gdb->project_file);
1837
1838 end:
1839 /* gdb_close may be called more than one time on exit,
1840 * on the second occurence, the breakpoint list has been freed,
1841 * prevent this to happen by setting the project_file to NULL */
1842 gdb->project_file = NULL;
1843 }
1844
1845 #define CG_QUIT "quit\n"
1846 #define CG_YES "yes\n"
1847 #define CG_POLL 100
1848 /* Close gdb process */
1849 void
gdb_close(this)1850 gdb_close(this)
1851 gdb_T *this;
1852 {
1853 bpinfo_T *p;
1854 pid_t pid;
1855 int rc;
1856
1857 if (this->state & GS_CLOSING) /* prevent recursive calls */
1858 return;
1859 this->state |= GS_CLOSING;
1860
1861 /* write the project file */
1862 write_project();
1863
1864 /* remove all signs */
1865 gdb_fr_unlite(this);
1866 for (p = gdb->bpinfo; p != NULL; p = p->next)
1867 cnb_buf_delsign(p->buf, BP_SIGN_ID(p->id));
1868
1869 /* free breakpoints table */
1870 gdb_free_bplist(&(this->bpinfo));
1871
1872 /* a) attempt to gracefully terminate gdb process
1873 * b) if this fails, SIGTERM it
1874 * c) if this fails, too bad, just return */
1875 if (this->pid != (pid_t)-1)
1876 {
1877 pid = waitpid(this->pid, NULL, WNOHANG);
1878
1879 if ((pid == (pid_t)-1 && errno == ECHILD) || pid == this->pid)
1880 ; /* nop */
1881 else /* still running */
1882 {
1883 char c = KEY_INTERUPT;
1884 int killed = FALSE;
1885 int t;
1886
1887 /* a) write an interrupt followed by a 'quit' cmd */
1888 write(this->fd, &c, 1);
1889 if (gdb_read(this, clewn_buf, MAX_BUFFSIZE, 1000) >= 0)
1890 {
1891 write(this->fd, CG_QUIT, strlen(CG_QUIT));
1892 while ((rc = gdb_read(this, clewn_buf, MAX_BUFFSIZE, 100)) > 0)
1893 ;
1894
1895 if (rc != -1)
1896 write(this->fd, CG_YES, strlen(CG_YES));
1897 }
1898
1899 /* make sure gdb is terminated: poll for waitpid() */
1900 for (t = 0; !killed; t += CG_POLL)
1901 {
1902 /* 1 second elapsed since start of polling for waitpid */
1903 if (t >= 1000 )
1904 {
1905 # ifdef SIGTERM
1906 /* b) kill it now */
1907 kill(this->pid, SIGTERM);
1908 # endif
1909 killed = TRUE;
1910 }
1911
1912 clewn_sleep(CG_POLL);
1913 pid = waitpid(this->pid, NULL, WNOHANG);
1914 if ((pid == (pid_t)-1 && errno == ECHILD) || pid == this->pid)
1915 break;
1916 }
1917 }
1918 clear_readline();
1919 fprintf(stderr, "GDB terminated\n");
1920 }
1921
1922 if (this->fd >= 0) {
1923 close(this->fd);
1924 this->fd = -1;
1925 }
1926
1927 /* keep the GS_QUITTING state of gdb; this state sets the difference
1928 * between a 'quit' command, and a 'restart' command */
1929 this->state = GS_INIT | (this->state & GS_QUITTING);
1930 this->pid = (pid_t)-1;
1931 }
1932
1933 /* Highlite asm_add line; return TRUE when asm_add found in asm buffer */
1934 int
gdb_as_frset(this,obs)1935 gdb_as_frset(this, obs)
1936 gdb_T *this;
1937 struct obstack *obs;
1938 {
1939 int bufno;
1940 char *fname;
1941 linenr_T lnum;
1942
1943 if (this->asm_func == NULL || this->asm_add == NULL)
1944 return FALSE;
1945
1946 /* Search all buffers whose name start with this->asm_func for ptrn */
1947 for (bufno = 1; ! cnb_outofbounds(bufno); bufno++)
1948 {
1949 if ((fname = cnb_filename(bufno)) != NULL
1950 && strstr(fname, this->asm_func) != NULL
1951 && (lnum = searchfor(fname, this->asm_add)) > 0)
1952 {
1953 this->pool.buf = bufno;
1954 this->pool.lnum = lnum;
1955
1956 if (this->pool.hilite)
1957 gdb_fr_set(this, NULL, NULL, obs);
1958
1959 FREE(this->asm_add);
1960 return TRUE;
1961 }
1962 }
1963 return FALSE;
1964 }
1965
1966 /*
1967 * Highlight line within frame
1968 * Return -1 when failing to load the buffer, 0 otherwise
1969 */
1970 int
gdb_fr_set(this,file,line,obs)1971 gdb_fr_set(this, file, line, obs)
1972 gdb_T *this;
1973 char *file;
1974 linenr_T *line;
1975 struct obstack *obs;
1976 {
1977 int buf = -1;
1978 linenr_T lnum;
1979
1980 /* Do not set frame hilite when this breakpoint has a 'commands'
1981 * with a 'continue' statement */
1982 if (this->cont)
1983 {
1984 this->cont = FALSE;
1985 return 0;
1986 }
1987
1988 if (line == NULL) /* in asm window */
1989 {
1990 buf = this->pool.buf;
1991 lnum = this->pool.lnum;
1992 }
1993 else /* in source file */
1994 lnum = *line;
1995
1996 if (cnb_isvalid_buffer(buf) || file != NULL)
1997 {
1998 if ((buf = gdb_edit_file(buf, file, lnum, 0, obs)) > 0)
1999 gdb_fr_lite(this, buf, lnum, obs);
2000 else
2001 return -1;
2002 }
2003 return 0;
2004 }
2005
2006 /* Highlite frame */
2007 void
gdb_fr_lite(this,buf,lnum,obs)2008 gdb_fr_lite(this, buf, lnum, obs)
2009 gdb_T *this;
2010 int buf; /* buffer number where to highlite */
2011 linenr_T lnum; /* line number */
2012 struct obstack *obs;
2013 {
2014 if (! cnb_isvalid_buffer(buf) || lnum <= 0)
2015 return;
2016
2017 /*
2018 * Remove previous frame sign:
2019 * GDB sends ANO_FRAME_INVALID annotations whenever stepping, running, etc...
2020 * and these annotations invoke gdb_fr_unlite() that turn off the previous frame sign.
2021 * But when moving along the stack frame with GDB 'up', 'down', 'frame' commands,
2022 * we don't get annotations and must turn off the previous frame sign.
2023 */
2024 if (this->fr_buf > 0)
2025 gdb_unlite(FRAME_SIGN);
2026
2027 this->fr_buf = buf;
2028 this->lnum = lnum;
2029
2030 /* add new frame highlite */
2031 cnb_buf_addsign(buf, FRAME_SIGN, FRAME_SIGN, lnum, obs);
2032 }
2033
2034 /* Unlite frame */
2035 void
gdb_fr_unlite(this)2036 gdb_fr_unlite(this)
2037 gdb_T *this;
2038 {
2039 if (! cnb_isvalid_buffer(this->fr_buf))
2040 return;
2041
2042 this->fr_buf = -1;
2043 this->lnum = 0;
2044 cnb_buf_delsign(0, FRAME_SIGN);
2045 }
2046
2047 /* Print an error message */
2048 void
EMSG(m)2049 EMSG (m)
2050 char *m;
2051 {
2052 fputs(m, rl_outstream);
2053 fputs("\n", rl_outstream);
2054 }
2055
2056 /*
2057 * Find the line number of the FRAME_SIGN in this buffer.
2058 * Return 0 as the line number when error.
2059 */
2060 linenr_T
find_fr_sign(buf)2061 find_fr_sign(buf)
2062 int buf;
2063 {
2064 if (gdb == NULL || ! cnb_isvalid_buffer(gdb->fr_buf) || buf != gdb->fr_buf)
2065 return 0;
2066
2067 return gdb->lnum;
2068 }
2069
2070 /* Unlite a sign. */
2071 void
gdb_unlite(id)2072 gdb_unlite(id)
2073 int id; /* sign id */
2074 {
2075 bpinfo_T *p;
2076
2077 if (gdb == NULL || id <= 0)
2078 return;
2079
2080 if (id == FRAME_SIGN)
2081 cnb_buf_delsign(0, id);
2082 else
2083 {
2084 /* look for buf number in bp table */
2085 for (p = gdb->bpinfo; p != NULL; p = p->next)
2086 if (BP_SIGN_ID(p->id) == id)
2087 {
2088 cnb_buf_delsign(p->buf, id);
2089 break;
2090 }
2091 }
2092 return;
2093 }
2094
2095 /*
2096 * Define a breakpoint sign. There is one sign type per breakpoint
2097 * sign in order to have breakpoints numbers as the sign text.
2098 * Returns sign type number or -1 if error.
2099 */
2100 int
gdb_define_bpsign(bp,obs)2101 gdb_define_bpsign(bp, obs)
2102 bpinfo_T *bp; /* the breakpoint */
2103 struct obstack *obs;
2104 {
2105 int typenr;
2106
2107 if (bp == NULL)
2108 return -1;
2109
2110 /* do not define it twice, use the previous sequence number if any */
2111 if (bp->enabled && bp->typenr_en > 0)
2112 return bp->typenr_en;
2113 else if (! bp->enabled && bp->typenr_dis > 0)
2114 return bp->typenr_dis;
2115
2116 if ((typenr = cnb_define_sign(bp->buf, bp->id,
2117 (bp->enabled ? SIGN_BP_ENABLED : SIGN_BP_DISABLED), obs)) > 0)
2118 {
2119 if (bp->enabled)
2120 bp->typenr_en = typenr;
2121 else
2122 bp->typenr_dis = typenr;
2123 }
2124
2125 return typenr;
2126 }
2127
2128 /* Append to or replace last line on rl_outstream */
2129 void
gdb_write_buf(this,chunk,add)2130 gdb_write_buf(this, chunk, add)
2131 gdb_T *this;
2132 char *chunk; /* a chunk may contain one, many or no NL */
2133 int add; /* TRUE when chunk is added */
2134 {
2135 if (chunk == NULL || this == NULL)
2136 return;
2137
2138 /* do not echo the prompt as it is already done by readline */
2139 if (this->note == ANO_PREPROMPT
2140 || this->note == ANO_PRECMDS
2141 || this->note == ANO_PREQUERY
2142 || this->note == ANO_PREOVERLOAD)
2143 {
2144 /* stores the prompt in case it is a multi line prompt */
2145 xfree(preprompt);
2146 preprompt = clewn_strsave(chunk);
2147 return;
2148 }
2149
2150 /* hack to handle ANO_PRECMDS missing from GDB
2151 * avoid printing a superfluous ">" line after completion
2152 * in a "commands" or "define" */
2153 if (this->note == ANO_NONE
2154 && this->cli_cmd.state == CS_CHOICE
2155 && strcmp(chunk, ">") == 0)
2156 return;
2157
2158 /* do not echo user input as it is already done by readline */
2159 if (this->note == ANO_PROMPT
2160 || this->note == ANO_CMDS
2161 || this->note == ANO_QUERY
2162 || this->note == ANO_OVERLOAD)
2163 return;
2164
2165 if (add)
2166 {
2167 if (nl_output) /* newline already output by readline */
2168 nl_output = FALSE;
2169 else
2170 fputs("\n", rl_outstream);
2171 }
2172 else
2173 fputs("\r", rl_outstream); /* overwrite last line */
2174
2175 if (preprompt == NULL)
2176 {
2177 /* write chunk */
2178 fputs(chunk, rl_outstream);
2179 }
2180 else
2181 {
2182 /* a multi line prompt: print the previous line from the multi
2183 * line prompt instead of this last prompt line
2184 * a multi line prompt occurs after GDB command 'run' when
2185 * debuggee is already started */
2186 if (gdb->prompt == NULL)
2187 {
2188 fputs(preprompt, rl_outstream);
2189
2190 /* now store chunk as the previous line
2191 * from the multi line prompt */
2192 xfree(preprompt);
2193 preprompt = clewn_strsave(chunk);
2194 }
2195 else
2196 {
2197 fputs(chunk, rl_outstream);
2198 FREE(preprompt);
2199 }
2200 }
2201 }
2202
2203 /*
2204 * Fill up buff with a NUL terminated string of max size - 1 bytes from gdb.
2205 * Return bytes read count, -1 if error or zero for nothing to read.
2206 */
2207 int
gdb_read(this,buff,size,wtime)2208 gdb_read(this, buff, size, wtime)
2209 gdb_T *this;
2210 char *buff; /* where to write */
2211 int size; /* buff size */
2212 int wtime; /* msecs time out, -1 wait forever */
2213 {
2214 int len;
2215 int rc;
2216 # ifndef HAVE_SELECT
2217 struct pollfd fds;
2218
2219 fds.fd = this->fd;
2220 fds.events = POLLIN;
2221 # else
2222 struct timeval tv;
2223 struct timeval start_tv;
2224 fd_set rfds;
2225
2226 FD_ZERO(&rfds);
2227 FD_SET(this->fd, &rfds);
2228
2229 # ifdef HAVE_GETTIMEOFDAY
2230 if (wtime >= 0)
2231 gettimeofday(&start_tv, NULL);
2232 # endif
2233 # endif
2234
2235 if (size <= 0 || buff == NULL || ! GDB_STATE(this, GS_UP))
2236 return -1;
2237
2238 /* make sure there is some data to read */
2239 while (1)
2240 {
2241 if (this->state & GS_SIGCHLD)
2242 goto close;
2243
2244 # ifndef HAVE_SELECT
2245 if ((rc = poll(&fds, 1, wtime)) > 0)
2246 # else
2247 if (wtime >= 0)
2248 {
2249 tv.tv_sec = wtime / 1000;
2250 tv.tv_usec = (wtime % 1000) * (1000000/1000);
2251 }
2252
2253 if ((rc = select(this->fd + 1, &rfds, NULL, NULL, (wtime >= 0) ? &tv : NULL)) > 0)
2254 # endif
2255 break;
2256
2257 if (rc == -1 && errno == EINTR)
2258 {
2259 if (wtime >= 0)
2260 {
2261 /* compute remaining wait time */
2262 # if ! defined(HAVE_SELECT) || ! defined(HAVE_GETTIMEOFDAY)
2263 /* guess: interrupted halfway, gdb processing 10 msecs */
2264 wtime = wtime / 2 - 10L;
2265 # else
2266 gettimeofday(&tv, NULL);
2267 wtime -= (tv.tv_sec - start_tv.tv_sec) * 1000L
2268 + (tv.tv_usec - start_tv.tv_usec) / 1000L;
2269 # endif
2270 if (wtime < 0)
2271 return 0;
2272 }
2273 }
2274 else if (rc == 0)
2275 return 0;
2276 else
2277 goto close;
2278 }
2279
2280 /* read the data */
2281 if ((len = read(this->fd, (char *)buff, size - 1)) < 0)
2282 goto close;
2283
2284 buff[len] = NUL;
2285 return len;
2286 close:
2287 gdb_close(this);
2288 return -1;
2289 }
2290
2291 /*
2292 * Edit a file.
2293 * Use buf number if positive, otherwise fname.
2294 * Return the buffer number or -1 when error.
2295 */
2296 int
gdb_edit_file(buf,fname,lnum,silent,obs)2297 gdb_edit_file(buf, fname, lnum, silent, obs)
2298 int buf; /* asm buffer number to edit */
2299 char *fname; /* file name */
2300 linenr_T lnum; /* line number */
2301 int silent;
2302 struct obstack *obs;
2303 {
2304 if (gdb == NULL)
2305 return -1;
2306
2307 if (! cnb_isvalid_buffer(buf) && (fname == NULL || *fname == NUL))
2308 return -1;
2309
2310 if (cnb_isvalid_buffer(buf) && (fname = cnb_filename(buf)) == NULL)
2311 return -1;
2312
2313 #ifdef GDB_LVL3_SUPPORT
2314 return cnb_editFile(fname, lnum, gdb->directories, gdb->lvl3.source_cur,
2315 gdb->lvl3.source_list, silent, obs);
2316 #else
2317 return cnb_editFile(fname, lnum, gdb->directories, NULL, NULL, silent, obs);
2318 #endif
2319 }
2320
2321 /* Do the OOB_COMPLETE part of an oob cmd and send the next one */
2322 void
gdb_oob_send(this,obs)2323 gdb_oob_send(this, obs)
2324 gdb_T *this;
2325 struct obstack *obs;
2326 {
2327 int keep = FALSE; /* when TRUE, do not switch to next oob function */
2328 char *res = NULL;
2329 int *pi = &(this->oob.idx);
2330 int s_a = (this->state & GS_ALLOWED);
2331
2332 /* prevent recursive calls to parse_output() since breakpoint
2333 * or frame highlighting may cause Vim to query the user when
2334 * changes have been made in the previous buffer */
2335 this->state &= ~GS_ALLOWED;
2336
2337 if (this->oobfunc == NULL)
2338 return;
2339
2340 if (*pi == -1)
2341 {
2342 this->oob.state &= ~OS_INTR;
2343 if (this->oob.state & OS_QUIT)
2344 goto quit;
2345 }
2346
2347 if (*pi >= 0 && (this->oobfunc)[*pi].oob != NULL) /* assert != NULL */
2348 {
2349 if ((this->oobfunc)[*pi].oob(this, OOB_COMPLETE, NULL, obs) != NULL)
2350 keep = TRUE;
2351
2352 if (this->oob.state & OS_QUIT)
2353 goto quit;
2354 }
2355
2356 if (! keep)
2357 ++(*pi);
2358
2359 while ((this->oobfunc)[*pi].oob != NULL && !(this->oob.state & OS_INTR))
2360 {
2361 if ((res = (this->oobfunc)[*pi].oob(this, OOB_CMD, NULL, obs)) != NULL)
2362 {
2363 this->oob.cnt = 0;
2364
2365 /* send the command to GDB */
2366 write(this->fd, (char *)res, strlen(res));
2367
2368 this->state &= ~GS_ALLOWED;
2369 if (s_a)
2370 this->state |= GS_ALLOWED;
2371 return;
2372 }
2373
2374 ++(*pi);
2375 }
2376
2377 *pi = -1;
2378 quit:
2379 this->oob.state &= ~OS_CMD;
2380 this->oob.state &= ~OS_QUIT;
2381
2382 this->state &= ~GS_ALLOWED;
2383 if (s_a)
2384 this->state |= GS_ALLOWED;
2385 }
2386
2387 /* Receive out of band response to idx cmd */
2388 void
gdb_oob_receive(this,chunk,obs)2389 gdb_oob_receive(this, chunk, obs)
2390 gdb_T *this;
2391 char *chunk; /* response (possibly incomplete) */
2392 struct obstack *obs;
2393 {
2394 char *res = NULL;
2395 int s_a = (this->state & GS_ALLOWED);
2396
2397 /* prevent recursive calls to parse_output() since breakpoint
2398 * or frame highlighting may cause Vim to query the user when
2399 * changes have been made in the previous buffer */
2400 this->state &= ~GS_ALLOWED;
2401
2402 if (this->oobfunc == NULL)
2403 return;
2404
2405 if(IS_OOBACTIVE(this))
2406 {
2407 /* silently discard when interrupted */
2408 if (!(this->oob.state & OS_INTR) && chunk != NULL)
2409 {
2410 if (this->parser != PS_PREPROMPT && this->parser != PS_PROMPT
2411 && (this->oobfunc)[this->oob.idx].oob != NULL) /* assert != NULL */
2412 {
2413 this->oob.cnt++;
2414 (void)(this->oobfunc)[this->oob.idx].oob(this, OOB_COLLECT, chunk, obs);
2415
2416 this->state &= ~GS_ALLOWED;
2417 if (s_a)
2418 this->state |= GS_ALLOWED;
2419 return;
2420 }
2421 }
2422
2423 /* keep the last prompt */
2424 if (this->parser == PS_PREPROMPT)
2425 {
2426 gdb_cat(&res, this->line);
2427 gdb_cat(&res, chunk);
2428 xfree(this->line);
2429 this->line = res;
2430 }
2431 }
2432
2433 this->state &= ~GS_ALLOWED;
2434 if (s_a)
2435 this->state |= GS_ALLOWED;
2436 }
2437
2438 /*
2439 * Remove in the bpinfo list the records corresponding to the signs in bufno,
2440 * the signs themselves are removed by Vim in free_buffer()
2441 */
2442 void
gdb_free_records(bufno)2443 gdb_free_records(bufno)
2444 int bufno;
2445 {
2446 bpinfo_T *p, **pt;
2447
2448 if (gdb == NULL)
2449 return;
2450
2451 for (pt = &(gdb->bpinfo); *pt != NULL; )
2452 {
2453 p = *pt;
2454 if (p->buf == bufno)
2455 {
2456 *pt = p->next; /* unlink record */
2457 xfree(p);
2458 }
2459 else
2460 pt = &(p->next);
2461 }
2462 }
2463
2464 /* Free a bpinfo_T list and set address referenced by plist to NULL */
2465 void
gdb_free_bplist(plist)2466 gdb_free_bplist (plist)
2467 bpinfo_T ** plist;
2468 {
2469 bpinfo_T *p, *next;
2470
2471 if (plist == NULL)
2472 return;
2473
2474 for (p = *plist; p != NULL; p = next)
2475 {
2476 next = p->next;
2477 xfree(p);
2478 }
2479
2480 *plist = NULL;
2481 }
2482
2483 /* Get the GDB command type */
2484 void
gdb_cmd_type(this,cmd)2485 gdb_cmd_type(this, cmd)
2486 gdb_T *this;
2487 char *cmd;
2488 {
2489 token_T *tok;
2490 char *ptr;
2491 char *last;
2492 char *tail;
2493
2494 this->cmd_type = CMD_ANY;
2495
2496 if (cmd == NULL)
2497 return;
2498
2499 while (isspace(*cmd))
2500 cmd++;
2501
2502 if (*cmd == NUL)
2503 return;
2504
2505 /* find the token 'keyword:tail' that matches cmd */
2506 for (tok = tokens; tok->keyword != NULL; tok++)
2507 {
2508 if (strstr(cmd, tok->keyword) == cmd)
2509 {
2510 last = ptr = cmd + strlen(tok->keyword);
2511
2512 /* get first space after token */
2513 while (*last && ! isspace(*last))
2514 last++;
2515
2516 /* get a copy of last part */
2517 if (last != ptr)
2518 {
2519 tail = clewn_strnsave(ptr, last - ptr);
2520
2521 if (strstr(tok->tail, tail) != tok->tail)
2522 {
2523 xfree(tail);
2524 continue; /* tail do not match */
2525 }
2526 xfree(tail);
2527 }
2528
2529 /* match */
2530 this->cmd_type = tok->type;
2531 break;
2532 }
2533 }
2534 }
2535
2536 /* Create a private temporary directory */
2537 static int
clewn_mktmpdir()2538 clewn_mktmpdir()
2539 {
2540 char *(tempdirs[]) = {TEMPDIRNAMES};
2541 char itmp[TEMPNAMELEN];
2542 char *tmpdir;
2543 char *buf;
2544 mode_t umask_save;
2545 DIR *dir;
2546 int ret;
2547 unsigned i;
2548 long nr;
2549 long off;
2550
2551 if (clewn_tempdir == NULL)
2552 {
2553 /* try the entries in TEMPDIRNAMES to create the temp directory */
2554 for (i = 0; i < sizeof(tempdirs) / sizeof(char *); i++)
2555 {
2556 if ((*(tempdirs[i]) == '$' && (tmpdir = getenv(tempdirs[i] + 1)) != NULL)
2557 || (*(tempdirs[i]) != '$' && (tmpdir = tempdirs[i])))
2558 {
2559 /* leave room for "/c1dddddd" and check directory exists*/
2560 if (strlen(tmpdir) < TEMPNAMELEN - 10
2561 && (dir = opendir(tmpdir)) != NULL)
2562 {
2563 closedir(dir);
2564 nr = (long)getpid() % 1000000L;
2565
2566 /* try up to 10000 different values */
2567 for (off = nr; off < nr + 10000L; off++)
2568 {
2569 sprintf(itmp, "%s/c%ld", tmpdir, off);
2570
2571 umask_save = umask(077);
2572 ret = mkdir(itmp, 0700);
2573 (void)umask(umask_save);
2574
2575 if (ret == 0)
2576 {
2577 /* expand to full path */
2578 buf = (char *)xmalloc(MAXPATHL + 1);
2579
2580 if (! clewn_fullpath(itmp, buf, MAXPATHL, FALSE))
2581 strcpy(buf, itmp);
2582
2583 if (*buf != NUL && *(buf + strlen(buf) - 1) != '/')
2584 strcat(buf, "/");
2585
2586 clewn_tempdir = clewn_strsave(buf);
2587 xfree(buf);
2588
2589 return clewn_tempdir != NULL ? OK : FAIL;
2590 }
2591 else if (errno != EEXIST)
2592 break; /* probably can't create any dir here, try another place */
2593 }
2594 }
2595 }
2596 }
2597 }
2598 else
2599 return OK;
2600
2601 return FAIL;
2602 }
2603
2604 /*
2605 * Create and open a file in the temporary directory whose name
2606 * is prefixed by 'filename'. The new name is 'filename{nb}' where
2607 * 'nb' are digits appended to 'filename' so that it's a new file.
2608 * When 'fullpath' is not NULL and operation was successful, the
2609 * address pointed to by 'fullpath' is set to an allocated full
2610 * pathname of this file.
2611 * Return a pointer to the stream, NULL when failed.
2612 */
2613 FILE *
clewn_opentmpfile(filename,fullpath,instance)2614 clewn_opentmpfile(filename, fullpath, instance)
2615 char *filename;
2616 char **fullpath;
2617 int instance;
2618 {
2619 FILE *stream = NULL;
2620 char *res = NULL;
2621 int i = instance;
2622 struct stat st;
2623 char *buf;
2624
2625 if (clewn_tempdir != NULL && filename != NULL && *filename != NUL)
2626 {
2627 /* find a non existing file name in this directory */
2628 do
2629 {
2630 buf = gdb_itoa(i);
2631 if (i == 1)
2632 *buf = NUL;
2633 i++;
2634
2635 FREE(res);
2636 gdb_cat(&res, clewn_tempdir);
2637 gdb_cat(&res, buf);
2638 gdb_cat(&res, filename);
2639 } while (stat((char *)res, &st) == 0);
2640
2641 /* open the file */
2642 if ((stream = fopen(res, "w+")) != NULL && fullpath != NULL)
2643 *fullpath = res;
2644 else
2645 xfree(res);
2646 }
2647 return stream;
2648 }
2649
2650 /* Remove Clewn temporary directory and its content */
2651 static void
clewn_deltempdir()2652 clewn_deltempdir()
2653 {
2654 char itmp[TEMPNAMELEN];
2655 DIR *dp;
2656 struct dirent *ep;
2657
2658 if (clewn_tempdir != NULL)
2659 {
2660 if ((dp = opendir(clewn_tempdir)) != NULL)
2661 {
2662 while ((ep = readdir(dp)) != NULL)
2663 {
2664 sprintf(itmp, "%s%s", clewn_tempdir, ep->d_name);
2665 (void)unlink(itmp);
2666 }
2667
2668 (void)closedir(dp);
2669 (void)rmdir(clewn_tempdir);
2670 }
2671
2672 FREE(clewn_tempdir);
2673 }
2674 }
2675
2676 /*
2677 * Append src to string pointed to by pdest or copy src to a new allocated
2678 * string when *pdest is NULL.
2679 * *pdest is reallocated to make room for src.
2680 * Append an empty string when src is NULL.
2681 */
2682 void
gdb_cat(pdest,src)2683 gdb_cat(pdest, src)
2684 char **pdest; /* string address to append to */
2685 char *src; /* string to append */
2686 {
2687 int ldest = (*pdest != NULL ? strlen(*pdest) : 0);
2688 int lsrc = (src != NULL ? strlen(src) : 0);
2689 char *res;
2690
2691 if (lsrc != 0 || *pdest == NULL)
2692 {
2693 res = (char *)xmalloc(ldest + lsrc + 1);
2694
2695 if (ldest == 0)
2696 {
2697 if (lsrc != 0)
2698 strcpy(res, src);
2699 else
2700 strcpy(res, "");
2701 }
2702 else
2703 {
2704 strcpy(res, *pdest);
2705 strcat(res, src); /* assert src != NULL */
2706 }
2707
2708 xfree(*pdest);
2709 *pdest = res;
2710 }
2711 }
2712
2713 /* Print the regexp error message string to stderr. */
2714 static void
print_regerror(errcode,compiled)2715 print_regerror(errcode, compiled)
2716 int errcode;
2717 regex_t *compiled;
2718 {
2719 size_t length = regerror(errcode, compiled, NULL, 0);
2720 char *buffer;
2721
2722 buffer = (char *)xmalloc(length);
2723 (void)regerror(errcode, compiled, buffer, length);
2724 fprintf(stderr, buffer);
2725 xfree(buffer);
2726 }
2727
2728 /*
2729 * Return an allocated string that is the sub-match indexed by subid ([0-9])
2730 * using compiled pattern id. The string is allocated in an obstack when
2731 * obs is not NULL.
2732 * Return NULL if str does not match (or no such sub-match in pattern).
2733 */
2734 char *
gdb_regexec(str,id,subid,obs)2735 gdb_regexec(str, id, subid, obs)
2736 char *str; /* string to match against */
2737 int id; /* pattern id */
2738 int subid; /* sub-match index */
2739 struct obstack *obs; /* obstack to use for allocating memory */
2740 {
2741 regmatch_t match[10];
2742 pattern_T *pat;
2743
2744 if (str == NULL || *str == NUL || subid < 0 || subid > 9)
2745 return NULL;
2746
2747 for (pat = patterns; pat->str != NULL; pat++)
2748 {
2749 if (pat->id == id)
2750 {
2751 if (pat->regprog != NULL && regexec(pat->regprog, str, 10, match, 0) == 0
2752 && match[subid].rm_eo != -1
2753 && match[subid].rm_so != -1)
2754 {
2755 if (obs != NULL)
2756 return (char *)obstack_copy0(obs, str + match[subid].rm_so,
2757 (int)(match[subid].rm_eo - match[subid].rm_so));
2758 else
2759 return clewn_strnsave(str + match[subid].rm_so,
2760 (int)(match[subid].rm_eo - match[subid].rm_so));
2761 }
2762 break;
2763 }
2764 }
2765 return NULL;
2766 }
2767
2768 /* Return an integer as a string */
2769 char *
gdb_itoa(i)2770 gdb_itoa(i)
2771 int i; /* integer to stringify */
2772 {
2773 static char buf[NUMBUFLEN];
2774
2775 sprintf(buf, "%ld", (long)i);
2776 return buf;
2777 }
2778
2779 char *
clewn_stripwhite(string)2780 clewn_stripwhite(string)
2781 char *string;
2782 {
2783 char *s;
2784 char *t;
2785
2786 for (s = string; isspace(*s); s++)
2787 ;
2788
2789 if (*s == NUL)
2790 return s;
2791
2792 t = s + strlen(s) - 1;
2793 while (t > s && isspace(*t))
2794 t--;
2795 *++t = NUL;
2796
2797 return s;
2798 }
2799
2800 /* Clear realine and set cursor at line start */
2801 static void
clear_readline()2802 clear_readline()
2803 {
2804 int len = 0;
2805
2806 if (gdb->prompt != NULL) /* find out how many spaces are needed */
2807 len = strlen(gdb->prompt);
2808 if (rl_line_buffer != NULL)
2809 len += strlen(rl_line_buffer);
2810
2811 len = (len < MAX_BUFFSIZE ? len: MAX_BUFFSIZE -1);
2812 memset((void *)clewn_buf, (int)' ', (size_t)len);
2813 clewn_buf[len] = NUL;
2814
2815 fprintf(rl_outstream, "\r%s\r", clewn_buf); /* clear the line */
2816 fflush(rl_outstream);
2817 }
2818
2819 /* Add line to readline history after having stripped whites and
2820 * removed a previous identical entry in the history */
2821 static void
clewn_add_history(line)2822 clewn_add_history(line)
2823 char *line;
2824 {
2825 HIST_ENTRY * entry;
2826 char *str;
2827 int offset;
2828
2829 /* make a copy */
2830 str = clewn_strsave(line);
2831
2832 line = clewn_stripwhite(str);
2833 if (*line == NUL)
2834 {
2835 xfree(str);
2836 return;
2837 }
2838
2839 for (offset = 0; offset + history_base < history_length
2840 && (offset = history_search_pos(line, 1, offset)) >= 0; )
2841 {
2842 if((entry = history_get(offset + history_base)) != NULL
2843 && entry->line != NULL
2844 && strcmp(line, entry->line) == 0
2845 && (entry = remove_history(offset)) != NULL)
2846 {
2847 if (entry->line != NULL)
2848 free(entry->line);
2849 free(entry);
2850 }
2851 else
2852 offset++;
2853 }
2854
2855 add_history(line);
2856 xfree(str);
2857 }
2858
2859 /*
2860 * Parse a line of key mapping
2861 * Key definitions are of the form `KEY:GDB COMMAND:{%line|%text}'
2862 * where:
2863 * %text: the text below the mouse is added to the GDB command
2864 * %line: 'fname:lnum' are added to the GDB command, where fname is the
2865 * current buffer full pathname, and lnum the line number at
2866 * cursor position
2867 *
2868 * All characters following `#' up to the next new line are ignored.
2869 * Leading blanks on each line are ignored. Empty lines are ignored.
2870 *
2871 * Supported key names:
2872 * key functions: F1 to F20, example: `F11:continue'
2873 * all ASCII printable characters except '#'
2874 * control characters written as C-...
2875 */
2876 static void
parse_keyline(line)2877 parse_keyline(line)
2878 char * line;
2879 {
2880 char *end;
2881 int key;
2882 int fnum;
2883
2884 while (isspace(*line)) /* skip blanks */
2885 line++;
2886
2887 key = *line++;
2888 if (key > 0 && key < 256 && isalnum(key))
2889 {
2890 if (key == '#') /* a comment */
2891 return;
2892
2893 else if (key == 'F' && *line != ':') /* a function key */
2894 {
2895 fnum = (int)strtol((char *)line, &end, 10);
2896
2897 /* advance ptr to next separator */
2898 if (*end != ':')
2899 return;
2900 line = end;
2901
2902 /* set key to fnum function key index in table */
2903 if ((key = FUNCTION_INDEX(fnum)) == -1)
2904 return;
2905 }
2906
2907 else if (key == 'C' && *line == '-') { /* a control character */
2908 line++;
2909 key = *line++;
2910 if (key < 'A' || key > '_')
2911 return;
2912 key = key - '@';
2913 }
2914
2915 /* parse GDB command, it must be surrounded by ':' and not empty */
2916 if (*line++ != ':')
2917 return;
2918 /* remove a key mapping when the GDB command is empty */
2919 else if (*line == ':') {
2920 FREE(keymap[key]);
2921 return;
2922 }
2923 else if ((end = strchr(line, ':')) == NULL)
2924 return;
2925
2926 *end++ = NUL; /* terminate command string */
2927
2928 /* set GDB command in table */
2929 xfree(keymap[key]);
2930 keymap[key] = clewn_strsave(line);
2931
2932 /* parse %line or %text */
2933 if (strstr(end, "%line") == end)
2934 SET_KEY_LINE(keyline, key);
2935 else if (strstr(end, "%text") == end)
2936 SET_KEY_LINE(keytext, key);
2937 }
2938 }
2939
2940 /*
2941 * Read the key mappings file and build the keymap array and
2942 * bit map flags.
2943 */
2944 static void
read_keysfile(fname)2945 read_keysfile(fname)
2946 char *fname;
2947 {
2948 FILE *fd;
2949 char *ptr;
2950
2951 if ((fd = fopen(fname, "r")) == NULL)
2952 return;
2953
2954 /* read the key mappings file */
2955 while ((ptr = fgets(clewn_buf, MAX_BUFFSIZE, fd)) != NULL)
2956 parse_keyline(ptr);
2957
2958 fclose(fd);
2959 }
2960
2961 /* Process a netbeans event */
2962 static void
process_nb_event()2963 process_nb_event()
2964 {
2965 nb_event_T * event;
2966
2967 if ((event = cnb_data_evt()) != NULL)
2968 {
2969 /* process a keyAtPos event */
2970 if (event->key != NULL) {
2971 xfree(key_command);
2972 key_command = get_keymap(event);
2973 }
2974
2975 /* process a balloonText event */
2976 else if (event->text_event && gdb != NULL && ! IS_OOBACTIVE(gdb)) {
2977
2978 xfree(gdb->balloon_txt);
2979 if ((gdb->balloon_txt = clewn_strsave(event->text)) != NULL) {
2980
2981 /* need a static array since gdb->oobfunc is referenced later
2982 * by gdb_oob_receive */
2983 static oobfunc_T print_value_oobfunc[] = {
2984 {gdb_print_value},
2985 {NULL}
2986 };
2987 struct obstack obs;
2988
2989 (void)obstack_init(&obs);
2990
2991 /* the standard oobfunc table will be reset
2992 * after the prompt processing in process_annotation() */
2993 gdb->oobfunc = print_value_oobfunc;
2994 gdb_oob_send(gdb, &obs);
2995
2996 obstack_free(&obs, NULL);
2997 }
2998 }
2999
3000 event->text_event = FALSE; /* maybe lost when IS_OOBACTIVE */
3001 }
3002 }
3003
3004 /*
3005 * Use keyinfo to build the corresponding GDB command that is
3006 * found in keymap[].
3007 * If this key is flagged as `%line': build `command pathname:lnum'
3008 * If this key is flagged as `%text': build `command text`
3009 * Return an allocated GDB command.
3010 */
3011 static char *
get_keymap(keyinfo)3012 get_keymap(keyinfo)
3013 nb_event_T *keyinfo;
3014 {
3015 char *res = NULL;
3016 char *ptr;
3017 char *end;
3018 int fnum;
3019 int key;
3020
3021 if (keyinfo == NULL || (ptr = keyinfo->key) == NULL || *ptr == NUL)
3022 return NULL;
3023
3024 key = *ptr++;
3025
3026 if (key == 'F' && *ptr != NUL) /* a function key */
3027 {
3028 fnum = (int)strtol((char *)ptr, &end, 10);
3029
3030 if (*end != NUL)
3031 return NULL;
3032
3033 /* set key to fnum function key index in table */
3034 if ((key = FUNCTION_INDEX(fnum)) == -1)
3035 return NULL;
3036 }
3037
3038 else if (key == 'S' && *ptr == '-') {/* an uppercase key */
3039 key = *++ptr;
3040 if (key < 'A' || key > 'Z')
3041 return NULL;
3042 }
3043
3044 else if (key == 'C' && *ptr == '-') {/* a control character */
3045 key = *++ptr;
3046 if (key < 'A' || key > '_')
3047 return NULL;
3048 key = key - '@';
3049 }
3050
3051 else if (*ptr != NUL) /* only one character keys */
3052 return NULL;
3053
3054 if (keymap[key] == NULL) /* key is not mapped */
3055 return NULL;
3056
3057 /* build `command pathname:lnum'*/
3058 if (IS_KEY_FLAG(keyline, key))
3059 {
3060 gdb_cat(&res, keymap[key]);
3061 gdb_cat(&res, " \"");
3062 gdb_cat(&res, keyinfo->pathname);
3063 gdb_cat(&res, ":");
3064 gdb_cat(&res, keyinfo->lnum);
3065 gdb_cat(&res, "\"");
3066 return res;
3067 }
3068 /* build `command text'*/
3069 else if (IS_KEY_FLAG(keytext, key))
3070 {
3071 if (keyinfo->text != NULL)
3072 {
3073 gdb_cat(&res, keymap[key]);
3074 gdb_cat(&res, " ");
3075 gdb_cat(&res, keyinfo->text);
3076 return res;
3077 }
3078 }
3079 else
3080 return clewn_strsave(keymap[key]);
3081
3082 return NULL;
3083 }
3084
3085 /*
3086 * Search for a line in file fname that matches regexp PAT_ADD
3087 * and contains needle. Abort when addresses are greater than needle.
3088 * Return the line number or -1 when error.
3089 */
3090 linenr_T
searchfor(fname,needle)3091 searchfor(fname, needle)
3092 char *fname;
3093 char *needle;
3094 {
3095 struct obstack obs; /* use an obstack for speed */
3096 char *add;
3097 FILE *fd;
3098 linenr_T lnum;
3099 int compare;
3100
3101
3102 if (needle != NULL && fname != NULL && (fd = fopen(fname, "r")) != NULL)
3103 {
3104 lnum = 0;
3105 (void)obstack_init(&obs);
3106
3107 while (fgets(clewn_buf, MAX_BUFFSIZE, fd) != NULL)
3108 {
3109 lnum++;
3110 if ((add = gdb_regexec(clewn_buf, PAT_ADD, 1, &obs)) != NULL)
3111 {
3112 if ((compare = strcmp(add, needle)) == 0)
3113 {
3114 obstack_free(&obs, NULL);
3115 fclose(fd);
3116 return lnum;
3117 }
3118 else if (compare > 0) /* addresses are greater, abort */
3119 {
3120 obstack_free(&obs, NULL);
3121 fclose(fd);
3122 return -1;
3123 }
3124 }
3125 }
3126 obstack_free(&obs, NULL);
3127 fclose(fd);
3128 }
3129 return -1;
3130 }
3131
3132 /* Show the text in a balloon. */
3133 void
gdb_showBalloon(text,obs)3134 gdb_showBalloon(text, obs)
3135 char * text;
3136 struct obstack *obs;
3137 {
3138 cnb_showBalloon(text, FALSE, obs);
3139 }
3140
3141 /* Show the status in a ballon. */
3142 void
gdb_status(this,status,obs)3143 gdb_status(this, status, obs)
3144 gdb_T *this;
3145 char *status; /* gdb status */
3146 struct obstack *obs;
3147 {
3148 char *p, *q;
3149
3150 if (showBalloon && this != NULL && GDB_STATE(this, GS_UP))
3151 {
3152 obstack_strcat(obs, "gdb ");
3153 if (this->sfile != NULL)
3154 {
3155 obstack_strcat(obs, "- ");
3156 /* get the tail of this pathname */
3157 for (p = q = this->sfile; *q; q++)
3158 if (*q == '/')
3159 p = q + 1;
3160 obstack_strcat(obs, p);
3161 }
3162
3163 obstack_strcat(obs, " [");
3164 obstack_strcat(obs, status);
3165 obstack_strcat0(obs, "]");
3166 p = (char *)obstack_finish(obs);
3167 cnb_showBalloon(p, TRUE, obs);
3168 }
3169 }
3170
3171 /* Display a cmd line busy msg */
3172 void
gdb_msg_busy(str)3173 gdb_msg_busy(str)
3174 char *str;
3175 {
3176 #define BUSY_LINE_SIZE 82
3177 static char *prop[] = { "/", "-", "\\", "|" };
3178 static char busy[BUSY_LINE_SIZE];
3179 static int cnt;
3180 char *p;
3181
3182 /* set busy string */
3183 if (str != NULL && strcmp(str, "FIN") == 0)
3184 {
3185 for (p = busy; *p && p < busy + BUSY_LINE_SIZE - 6; p++)
3186 *p = ' ';
3187 *p++ = ' '; *p++ = ' '; *p++ = ' '; *p++ = ' '; *p++ = '\r';
3188 *p = NUL;
3189 busy[0] = '\r';
3190 fprintf(stderr, busy);
3191 fflush(stderr);
3192 rl_forced_update_display();
3193 }
3194 else if (str != NULL)
3195 {
3196 busy[0] = '\r';
3197 strncpy(busy + 1, str, BUSY_LINE_SIZE - 2);
3198 busy[BUSY_LINE_SIZE - 1] = NUL;
3199 }
3200 else
3201 {
3202 fprintf(stderr, busy);
3203 fprintf(stderr, " [");
3204 fprintf(stderr, prop[(++cnt % 4)]);
3205 fprintf(stderr, "]");
3206 fflush(stderr);
3207 }
3208 }
3209
3210