386BSD 0.0 development
[unix-history] / usr / src / usr.bin / gdb / xgdb / xgdb.c
CommitLineData
708cba75
WJ
1/*-
2 * This code is derived from software copyrighted by the Free Software
3 * Foundation.
4 *
5 * Modified 1991 by Donn Seeley at UUNET Technologies, Inc.
6 * Modified 1990 by Van Jacobson at Lawrence Berkeley Laboratory.
7 *
8 * static char rcsid[] = "$Header: xgdb.c,v 1.9 90/12/16 16:01:05 van Exp $";
9 */
10
11#ifndef lint
12static char sccsid[] = "@(#)xgdb.c 6.3 (Berkeley) 5/8/91";
13#endif /* not lint */
14
15/*
16 * Interface from GDB to X windows. Copyright (C) 1987 Free Software
17 * Foundation, Inc.
18 *
19 * GDB is distributed in the hope that it will be useful, but WITHOUT ANY
20 * WARRANTY. No author or distributor accepts responsibility to anyone for
21 * the consequences of using it or for whether it serves any particular
22 * purpose or works at all, unless he says so in writing. Refer to the GDB
23 * General Public License for full details.
24 *
25 * Everyone is granted permission to copy, modify and redistribute GDB, but only
26 * under the conditions described in the GDB General Public License. A copy
27 * of this license is supposed to have been given to you along with GDB so
28 * you can know your rights and responsibilities. It should be in a file
29 * named COPYING. Among other things, the copyright notice and this notice
30 * must be preserved on all copies.
31 *
32 * In other words, go ahead and share GDB, but don't try to stop anyone else
33 * from sharing it farther. Help stamp out software hoarding!
34 */
35
36/*
37 * Original version was contributed by Derek Beatty, 30 June 87.
38 * This version is essentially a re-write of the original by Van
39 * Jacobson (van@helios.ee.lbl.gov), Nov, 90.
40 */
41
42#include "defs.h"
43#include "param.h"
44#include "symtab.h"
45#include "frame.h"
46
47extern int stop_breakpoint;
48
49#include <X11/IntrinsicP.h>
50#include <X11/StringDefs.h>
51#include <X11/Xaw/AsciiSink.h>
52#include <X11/Xaw/AsciiText.h>
53#include <X11/Xaw/Box.h>
54#include <X11/Xaw/Command.h>
55#include <X11/Xaw/Label.h>
56#include <X11/Xaw/Paned.h>
57#include <X11/Xaw/Text.h>
58
59#include <stdio.h>
60#include <ctype.h>
61#include <sys/file.h>
62#include <sys/errno.h>
63
64extern int errno;
65extern char *getenv();
66extern char *malloc();
67extern void bcopy();
68extern int select();
69
70extern int get_filename_and_charpos();
71extern int source_line_charpos();
72extern int source_charpos_line();
73extern void execute_command();
74extern void error_no_arg();
75extern void add_com();
76
77/* The X display where the window appears. */
78
79static char *displayname;
80static Display *display;
81
82static XtAppContext app_context;
83
84/* Windows manipulated by this package. */
85
86static Widget main_widget;
87static Widget containing_widget;
88static Widget source_name_widget;
89static Widget source_text_widget;
90static Widget button_box_widget;
91
92/* Source text display. */
93
94static struct frame_info *last_fi;
95static CORE_ADDR last_pc;
96static struct symtab *last_cur_symtab;
97static int last_cur_line;
98
99static int source_window_line;
100static char *source_window_file;
101static struct symtab *source_window_symtab;
102
103static char version_label[64];
104extern char *version;
105
106/* Forward declarations */
107
108static Widget create_text_widget();
109
110static int
111safe_strcmp(a, b)
112 register char *a, *b;
113{
114 register int i;
115
116 if (a == b)
117 return (0);
118 if (!a && b)
119 return (1);
120 if (a && !b)
121 return (-1);
122 return (strcmp(a, b));
123}
124
125
126/* Display an appropriate piece of source code in the source window. */
127
128void
129xgdb_display_source()
130{
131 char *filename = NULL;
132 struct symtab_and_line get_selected_frame_sal();
133 struct symtab_and_line sal;
134 struct frame_info *fi;
135
136 /* Do nothing if called before we are initialized */
137
138 if (!containing_widget)
139 return;
140
141 /*
142 * Figure out what to display (the appropriate hooks to tell
143 * us don't exist so we guess): If there's a current frame
144 * and it or its pc changed from the last time we were here,
145 * display appropriate source line. Otherwise if the current
146 * source symtab or line is different, display that line.
147 * Otherwise nothing changed so leave the display alone.
148 */
149 fi = get_frame_info(selected_frame);
150 if (fi && (fi != last_fi || fi->pc != last_pc)) {
151 last_fi = fi;
152 last_pc = fi->pc;
153 sal = find_pc_line(fi->pc, fi->next_frame);
154 if (sal.symtab == NULL) { /* XXX */
155 sal.symtab = current_source_symtab;
156 sal.line = current_source_line;
157 }
158 current_source_symtab = sal.symtab;
159 current_source_line = sal.line;
160 } else if (current_source_symtab != last_cur_symtab ||
161 current_source_line != last_cur_line) {
162 sal.symtab = last_cur_symtab = current_source_symtab;
163 sal.line = last_cur_line = current_source_line;
164 } else
165 return;
166 /*
167 * Do a path search and get the exact filename of this source file.
168 * Also scan it and find its source lines if not already done.
169 */
170 if (sal.symtab && filename == NULL) {
171 if (get_filename_and_charpos(sal.symtab, sal.line, &filename))
172 /* line numbers may have changed - force highlight */
173 source_window_line = -1;
174 }
175
176 /*
177 * If the source window is wrong, destroy it and make a new one.
178 */
179 if (safe_strcmp(filename, source_window_file)) {
180 Arg args[1];
181 Widget src = XawTextGetSource(source_text_widget);
182
183 if (filename) {
184 XtSetArg(args[0], XtNstring, filename);
185 XtSetValues(src, args, XtNumber(args));
186 args[0].name = XtNlabel;
187 XtSetValues(source_name_widget, args, XtNumber(args));
188 } else {
189 XtSetArg(args[0], XtNstring, "/dev/null");
190 XtSetValues(src, args, XtNumber(args));
191 XtSetArg(args[0], XtNlabel, "");
192 XtSetValues(source_name_widget, args, XtNumber(args));
193 }
194 if (source_window_file)
195 free(source_window_file);
196 source_window_file = filename;
197 source_window_line = sal.line + 1; /* force highlight */
198 }
199 if (sal.symtab && source_window_line != sal.line) {
200 /*
201 * Update display and cursor positions as necessary.
202 * Cursor should be placed on line sal.line.
203 */
204 XawTextPosition l, r;
205
206 source_window_symtab = sal.symtab;
207 source_window_line = sal.line;
208 l = source_line_charpos(source_window_symtab, sal.line);
209 r = source_line_charpos(source_window_symtab, sal.line + 1);
210 if (r < l)
211 r = l + 1;
212 XawTextSetSelection(source_text_widget, l, r);
213 XawTextScrollToLine(source_text_widget, l, 10, 3);
214 XawTextSetInsertionPoint(source_text_widget, l);
215 }
216}
217
218
219/*
220 * Handlers for buttons.
221 */
222
223static int
224current_lineno()
225{
226 XawTextPosition start, finish;
227
228 XawTextGetSelectionPos(source_text_widget, &start, &finish);
229 if (start >= finish)
230 start = XawTextGetInsertionPoint(source_text_widget);
231
232 return (source_charpos_line(source_window_symtab, start));
233}
234
235static char *
236append_selection(cp)
237 char *cp;
238{
239 int len;
240 XawTextPosition l, r;
241
242 XawTextGetSelectionPos(source_text_widget, &l, &r);
243 if ((len = r - l) > 0) {
244 Widget src = XawTextGetSource(source_text_widget);
245
246 while (len > 0) {
247 XawTextBlock tb;
248
249 XawTextSourceRead(src, l, &tb, len);
250 bcopy(tb.ptr, cp, tb.length);
251 cp += tb.length;
252 len -= tb.length;
253 }
254 if (cp[-1] == 0)
255 --cp;
256 }
257 return (cp);
258}
259
260static char *
261append_selection_word(cp)
262 register char *cp;
263{
264 register int len;
265 XawTextPosition l, r;
266 XawTextBlock tb;
267 register char c;
268 register Widget src = XawTextGetSource(source_text_widget);
269
270 XawTextGetSelectionPos(source_text_widget, &l, &r);
271 if ((len = r - l) <= 0) {
272 l = XawTextGetInsertionPoint(source_text_widget);
273 len = 128; /* XXX */
274
275 /* might have clicked in middle of word -- back up to start */
276 for ( ; l > 0; --l) {
277 XawTextSourceRead(src, l - 1, &tb, 1);
278 c = tb.ptr[0];
279 if (! isalnum(c) && c != '_' && c != '$')
280 break;
281 }
282 }
283 while (len > 0) {
284 char *sp;
285 int i;
286
287 XawTextSourceRead(src, l, &tb, len);
288 for (sp = tb.ptr, i = tb.length; --i >= 0; ) {
289 c = *sp++;
290 if (!isalnum(c) && c != '_' && c != '$')
291 return (cp);
292 *cp++ = c;
293 }
294 len -= tb.length;
295 }
296 return (cp);
297}
298
299static char *
300append_selection_expr(cp)
301 char *cp;
302{
303 int len;
304 XawTextPosition l, r;
305 Widget src = XawTextGetSource(source_text_widget);
306 XawTextBlock tb;
307 char *sp;
308 char c;
309
310 XawTextGetSelectionPos(source_text_widget, &l, &r);
311 if (r > l)
312 return (append_selection(cp));
313
314 l = XawTextGetInsertionPoint(source_text_widget);
315
316 /* might have clicked in middle of word -- back up to start */
317 for ( ; l > 0; --l) {
318 XawTextSourceRead(src, l - 1, &tb, 1);
319 c = tb.ptr[0];
320 if (! isalnum(c) && c != '_' && c != '$')
321 break;
322 }
323
324 len = 128; /* XXX */
325 while (len > 0) {
326 int i;
327 char pstack[64];
328 int pcnt = 0;
329
330 XawTextSourceRead(src, l, &tb, len);
331 for (sp = tb.ptr, i = tb.length; --i >= 0; ) {
332 switch (c = *sp++) {
333 case '\n':
334 case ';':
335 return (cp);
336 case '=':
337 if (cp[-1] != '=')
338 return (cp - 1);
339 if (len == 128)
340 return (cp);
341 break;
342 case ',':
343 if (pcnt <= 0)
344 return (cp);
345 break;
346 case '(':
347 pstack[pcnt] = ')';
348 if (++pcnt >= sizeof(pstack))
349 return (cp);
350 break;
351 case '[':
352 pstack[pcnt] = ']';
353 if (++pcnt >= sizeof(pstack))
354 return (cp);
355 break;
356 case ')':
357 case ']':
358 if (--pcnt < 0 || pstack[pcnt] != c)
359 return (cp);
360 break;
361 }
362 *cp++ = c;
363 }
364 len -= tb.length;
365 }
366 return (cp);
367}
368
369static int input_avail; /* XXX kluge: do_command sets this when command
370 * data from button is avaialble to force top level
371 * to break out of its loop. */
372/*
373 * Handle a button by running the command COMMAND.
374 */
375static void
376do_command(w, command, call_data)
377 Widget w;
378 register char *command;
379 caddr_t call_data;
380{
381 char cmd_line[256];
382 char buf[256];
383 register char *out = cmd_line;
384 char *cp;
385 register char c;
386 extern char *finish_command_input();
387
388 while (c = *command++) {
389 if (c == '%') {
390 switch (*command++) {
391 case 's': /* current selection */
392 out = append_selection(out);
393 break;
394 case 'S': /* 1st selected "word" at curor */
395 out = append_selection_word(out);
396 break;
397 case 'e': /* echo cmd before executing */
398 break;
399 case 'E': /* 1st selected expression at curor */
400 out = append_selection_expr(out);
401 break;
402
403 case 'l': /* current line number */
404 (void) sprintf(buf, "%d", current_lineno());
405 for (cp = buf; c = *cp++; *out++ = c)
406 ;
407 break;
408 case 'L': /* line we're stopped at */
409 (void) sprintf(buf, "%d", source_window_line);
410 for (cp = buf; c = *cp++; *out++ = c)
411 ;
412 break;
413 case 'f': /* current file name */
414 for (cp = source_window_symtab->filename;
415 c = *cp++; *out++ = c)
416 ;
417 break;
418 case 'b': /* break # we're stopped at */
419 if (stop_breakpoint <= 0)
420 /* if no breakpoint, don't do cmd */
421 return;
422
423 (void) sprintf(buf, "%d", stop_breakpoint);
424 for (cp = buf; c = *cp++; *out++ = c)
425 ;
426 break;
427 }
428 } else
429 *out++ = c;
430 }
431 *out = 0;
432 reinitialize_more_filter();
433 /* have to exit via readline or tty modes stay messed up */
434 for (cp = cmd_line; c = *cp++; )
435 rl_stuff_char(c);
436 rl_stuff_char('\n');
437 input_avail = 1;
438}
439
440/*
441 * Define and display all the buttons.
442 */
443static void
444addbutton(parent, name, function, closure)
445 Widget parent;
446 char *name;
447 void (*function) ();
448 caddr_t closure;
449{
450 static XtCallbackRec Callback[] = {
451 {NULL, (caddr_t) NULL},
452 {NULL, (caddr_t) NULL},
453 };
454 static Arg commandArgs[] = {
455 {XtNlabel, (XtArgVal) NULL},
456 {XtNcallback, (XtArgVal) Callback},
457 };
458 Widget w;
459 char wname[128];
460 register char *cp;
461
462 strcpy(wname, name);
463 while ((cp = index(wname, '*')) || (cp = index(wname, '.')))
464 *cp -= 0x10;
465
466 if (w = XtNameToWidget(parent, wname))
467 XtDestroyWidget(w);
468
469 Callback[0].callback = (XtCallbackProc) function;
470 Callback[0].closure = (caddr_t) closure;
471 commandArgs[0].value = (XtArgVal) name;
472 XtCreateManagedWidget(wname, commandWidgetClass, parent,
473 commandArgs, XtNumber(commandArgs));
474}
475
476/*
477 * Create the button windows and store them in `buttons'.
478 */
479static void
480create_buttons(parent)
481 Widget parent;
482{
483 addbutton(parent, "quit", do_command, "quit");
484}
485
486static void
487button_command(arg)
488 char *arg;
489{
490 char *label;
491 unsigned int len;
492
493 if (! arg)
494 error_no_arg("button label and command");
495
496 for (len = strlen(arg); len > 0 && isspace(arg[len - 1]); --len)
497 ;
498 if (len == 0)
499 error_no_arg("button label and command");
500 arg[len] = 0;
501
502 /* make a copy of button label & command for toolkit to use */
503 label = malloc(len + 1);
504 strcpy(label, arg);
505
506 /* find the end of the label */
507 if (*label == '"') {
508 if ((arg = index(++label, '"')) == 0) {
509 printf("button label missing closing quote\n");
510 return;
511 }
512 *arg++ = 0;
513 } else if (arg = index(label, ' '))
514 *arg++ = 0;
515 else
516 arg = label;
517
518 while (*arg && isspace(*arg))
519 ++arg;
520
521 addbutton(button_box_widget, label, do_command, arg);
522}
523
524static void
525button_delete_command(arg)
526 char *arg;
527{
528 unsigned int len;
529 Widget w;
530 register char *cp;
531
532 if (! arg)
533 error_no_arg("button name");
534
535 for (len = strlen(arg); len > 0 && isspace(arg[len - 1]); --len)
536 ;
537 if (len == 0)
538 error_no_arg("button name");
539 arg[len] = 0;
540
541 /* find the end of the label */
542 if (*arg == '"') {
543 if ((cp = index(++arg, '"')) == 0) {
544 printf("button label missing closing quote\n");
545 return;
546 }
547 *cp++ = 0;
548 }
549 while ((cp = index(arg, '*')) || (cp = index(arg, '.')))
550 *cp -= 0x10;
551
552 if (w = XtNameToWidget(button_box_widget, arg))
553 XtDestroyWidget(w);
554}
555
556/*
557 * Create a "label window" that just displays the string LABEL.
558 */
559static Widget
560create_label(name, label)
561 char *name, *label;
562{
563 Arg args[1];
564 Widget w;
565
566 XtSetArg(args[0], XtNlabel, label);
567 w = XtCreateManagedWidget(name, labelWidgetClass, containing_widget,
568 args, XtNumber(args));
569 return (w);
570}
571
572/*
573 * Create a subwindow of PARENT that displays and scrolls the contents of
574 * file FILENAME.
575 */
576static Widget
577create_text_widget(parent, filename)
578 Widget parent;
579 char *filename;
580{
581 static Arg arg[] = {
582 {XtNstring, NULL},
583 {XtNtype, XawAsciiFile},
584 {XtNcursor, None},
585 };
586 Widget text_widget;
587
588 arg[0].value = (XtArgVal)filename;
589 text_widget = XtCreateManagedWidget("src", asciiTextWidgetClass,
590 parent, arg, XtNumber(arg));
591 return (text_widget);
592}
593
594/*
595 * Entry point to create the widgets representing our display.
596 */
597void
598xgdb_create_window()
599{
600 /* initialize toolkit, setup defaults */
601#ifdef notyet
602 main_widget = XtAppInitialize(&app_context, "Xgdb", NULL, 0,
603 argcptr, argv, NULL, NULL, 0);
604#else
605 char *dummy_argv[] = { "xgdb", 0 };
606 int dummy_argc = 1;
607 main_widget = XtAppInitialize(&app_context, "Xgdb", NULL, 0,
608 &dummy_argc, dummy_argv, NULL, NULL, 0);
609#endif
610 display = XtDisplay(main_widget);
611 containing_widget = XtCreateManagedWidget("frame", panedWidgetClass,
612 main_widget, NULL, 0);
613
614 sprintf(version_label, "XGDB %s", version);
615 button_box_widget = XtCreateManagedWidget("buttons", boxWidgetClass,
616 containing_widget, NULL, 0);
617 create_buttons(button_box_widget);
618 source_name_widget = create_label("srcLabel", "No source file yet.");
619 source_text_widget = create_text_widget(containing_widget, "/dev/null");
620
621 XtRealizeWidget(main_widget);
622 XFlush(display);
623}
624
625/*
626 * If we use an X window, the readline input loop is told to call
627 * this function before reading a character from stdin.
628 */
629/*ARGSUSED*/
630static void
631xgdb_window_hook()
632{
633 register int inmask = 1 << fileno(stdin);
634 register int xmask = 1 << ConnectionNumber(display);
635 register int nfds, pend;
636 int input_rfds;
637 XEvent ev;
638
639 /*
640 * Display our current idea of the `interesting' source file then
641 * loop, dispatching window events until data is available on
642 * stdin. Then return so the input data can be processed.
643 */
644 input_avail = 0;
645 xgdb_display_source();
646
647 input_rfds = 0;
648 while (input_avail == 0 && (input_rfds & inmask) == 0) {
649 pend = XPending(display);
650 if (!pend) {
651 input_rfds = inmask | xmask;
652 nfds = select(32, &input_rfds, 0, 0,
653 (struct timeval *)0);
654 if (nfds == -1 && errno == EINTR)
655 continue;
656 }
657 if (pend || (input_rfds & xmask)) {
658 XNextEvent(display, &ev);
659 XtDispatchEvent(&ev);
660 }
661 }
662}
663
664void
665_initialize_xgdb()
666{
667 extern void (*window_hook) ();
668 extern int inhibit_windows;
669 extern struct cmd_list_element *deletelist;
670
671 if (inhibit_windows)
672 return;
673
674 if (! displayname) {
675 displayname = getenv("DISPLAY");
676 if (! displayname) {
677 fprintf(stderr, "xgdb: no display name\n");
678 inhibit_windows = 1;
679 return;
680 }
681 }
682 xgdb_create_window();
683 window_hook = xgdb_window_hook;
684 add_com("button", class_support, button_command,
685"Add command button to xgdb window. First argument is button\n\
686label, second is command associated with button. Command can\n\
687include printf-like escapes:\n\
688 %s for current selection,\n\
689 %S for first 'word' of current selection,\n\
690 %e for current selection or expression at insertion pt,\n\
691 %E for current selection or expression at insertion pt,\n\
692 %l for current line number,\n\
693 %L for line program stopped at,\n\
694 %f for current file name,\n\
695 %b for current breakpoint number.");
696 add_cmd("button", class_support, button_delete_command,
697"Delete a button from the xgdb window.\n\
698Argument is name of button to be deleted.",
699 &deletelist);
700}