home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
PC World Komputer 1999 mARCH
/
PCWK3A99.iso
/
Linux
/
DDD331
/
DDD-3_1_.000
/
DDD-3_1_
/
ddd-3.1.1
/
ddd
/
SourceView.C
< prev
next >
Wrap
C/C++ Source or Header
|
1998-12-04
|
233KB
|
9,552 lines
// $Id: SourceView.C,v 1.426.4.1 1998/12/04 10:39:04 zeller Exp $
// Use the Source, Luke.
// Copyright (C) 1995-1998 Technische Universitaet Braunschweig, Germany.
// Written by Dorothea Luetkehaus <luetke@ips.cs.tu-bs.de>
// and Andreas Zeller <zeller@ips.cs.tu-bs.de>.
//
// This file is part of DDD.
//
// DDD is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// DDD is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public
// License along with DDD -- see the file COPYING.
// If not, write to the Free Software Foundation, Inc.,
// 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//
// DDD is the data display debugger.
// For details, see the DDD World-Wide-Web page,
// `http://www.cs.tu-bs.de/softech/ddd/',
// or send a mail to the DDD developers <ddd@ips.cs.tu-bs.de>.
#ifdef __GNUG__
#pragma implementation
#endif
char SourceView_rcsid[] =
"$Id: SourceView.C,v 1.426.4.1 1998/12/04 10:39:04 zeller Exp $";
// Fixing Some Bugs on a Sunday Evening
// ------------------------------------
//
// Whose bugs these are I think I know,
// But now he works at 3DO;
// He will not see me working here
// To fix his code and make it go.
//
// The saner folk must think it queer
// To trace without the source code near
// After a launch and frozen mouse
// The weirdest stack crawl of the year.
//
// I give my nodding head a shake
// To see if I can stay awake
// The only other thing to do
// Is find some more coffeine to take.
//
// This bug is pretty hard to nip,
// But I have other ones to fix,
// And tons to go before we ship,
// And tons to go before we ship.
//
//
// Written by David A. Lyons <dlyons@apple.com>, January 1994
// (with apologies to Robert Frost)
//
// Hey, it's fiction. Close to reality in spirit,
// but does not refer to a specific project, bug, Sunday,
// or brand of soft drink.
#ifndef LOG_GLYPHS
#define LOG_GlYPHS 0
#endif
//-----------------------------------------------------------------------------
#include "SourceView.h"
// DDD stuff
#include "AppData.h"
#include "ComboBox.h"
#include "Command.h"
#include "DataDisp.h" // Only for `DataDisp::SelectionLostCB'
#include "Delay.h"
#include "DestroyCB.h"
#include "HelpCB.h"
#include "HistoryD.h"
#include "InitImage.h"
#include "IntArray.h"
#include "MakeMenu.h"
#include "PosBuffer.h"
#include "RefreshDI.h"
#include "TimeOut.h"
#include "UndoBuffer.h"
#include "assert.h"
#include "buttons.h"
#include "charsets.h"
#include "cmdtty.h"
#include "cook.h"
#include "cwd.h"
#include "dbx-lookup.h"
#include "ddd.h"
#include "deref.h"
#include "disp-read.h"
#include "editing.h"
#include "events.h"
#include "file.h"
#include "filetype.h"
#include "fortranize.h"
#include "history.h"
#include "index.h"
#include "isid.h"
#include "java.h"
#include "logo.h"
#include "misc.h"
#include "mydialogs.h"
#include "options.h"
#include "post.h"
#include "question.h"
#include "regexps.h"
#include "shell.h"
#include "shorten.h"
#include "status.h"
#include "string-fun.h"
#include "strtoul.h"
#include "tabs.h"
#include "verify.h"
#include "version.h"
#include "windows.h"
#include "wm.h"
// Motif stuff
#include <Xm/Xm.h>
#include <Xm/Form.h>
#include <Xm/Label.h>
#include <Xm/MessageB.h>
#include <Xm/Text.h>
#include <Xm/TextF.h>
#include <Xm/RowColumn.h>
#include <Xm/PushB.h>
#include <Xm/SelectioB.h>
#include <Xm/List.h>
#include <Xm/PanedW.h>
#include <Xm/ToggleB.h>
#include <X11/StringDefs.h>
#include <X11/cursorfont.h>
#if XmVersion >= 2000
#include <Xm/SpinB.h>
#ifndef XmIsSpinBox
#define XmIsSpinBox(w) XtIsSubclass((w), xmSpinBoxWidgetClass)
#endif
#endif
// LessTif hacks
#include <X11/IntrinsicP.h>
#include "LessTifH.h"
// System stuff
extern "C" {
#include <sys/types.h>
#include <sys/stat.h>
}
#include <stdio.h>
#include <fcntl.h>
#include <iomanip.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include <limits.h>
#ifndef ULONG_MAX
#define ULONG_MAX ((unsigned long)(~0L)) /* 0xFFFFFFFF */
#endif
// Test for regular file - see stat(3)
#ifndef S_ISREG
#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
#endif
//-----------------------------------------------------------------------
// Xt stuff
//-----------------------------------------------------------------------
XtActionsRec SourceView::actions [] = {
{"source-popup-menu", SourceView::srcpopupAct },
{"source-start-select-word", SourceView::startSelectWordAct },
{"source-end-select-word", SourceView::endSelectWordAct },
{"source-update-glyphs", SourceView::updateGlyphsAct },
{"source-drag-glyph", SourceView::dragGlyphAct },
{"source-follow-glyph", SourceView::followGlyphAct },
{"source-drop-glyph", SourceView::dropGlyphAct },
{"source-delete-glyph", SourceView::deleteGlyphAct },
{"source-double-click", SourceView::doubleClickAct },
{"source-set-arg", SourceView::setArgAct },
};
//-----------------------------------------------------------------------
// Menus
//-----------------------------------------------------------------------
// Popup menus - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
struct LineItms { enum Itms {SetBP, SetTempBP, Sep1, TempNContBP,
Sep2, SetPC}; };
MMDesc SourceView::line_popup[] =
{
{"set", MMPush, {SourceView::line_popup_setCB, 0}, 0, 0, 0, 0},
{"set_temp", MMPush,
{SourceView::line_popup_set_tempCB, 0}, 0, 0, 0, 0},
MMSep,
{"temp_n_cont", MMPush,
{SourceView::line_popup_temp_n_contCB, 0}, 0, 0, 0, 0},
MMSep,
{"set_pc", MMPush, {SourceView::line_popup_set_pcCB, 0}, 0, 0, 0, 0},
MMEnd
};
struct BPItms { enum Itms {Properties, Disable, Delete, Sep, SetPC}; };
MMDesc SourceView::bp_popup[] =
{
{"properties", MMPush,
{SourceView::EditBreakpointPropertiesCB, 0}, 0, 0, 0, 0},
{"disable", MMPush, {SourceView::bp_popup_disableCB, 0}, 0, 0, 0, 0},
{"delete", MMPush, {SourceView::bp_popup_deleteCB, 0}, 0, 0, 0, 0},
MMSep,
{"set_pc", MMPush, {SourceView::bp_popup_set_pcCB, 0}, 0, 0, 0, 0},
MMEnd
};
struct BPButtons { enum Itms {Properties, Lookup, NewBP, NewWP, Print,
Enable, Disable, Delete}; };
MMDesc SourceView::bp_area[] =
{
{"properties", MMPush,
{SourceView::EditBreakpointPropertiesCB, 0}, 0, 0, 0, 0},
{"lookup", MMPush,
{SourceView::LookupBreakpointCB, XtPointer(0) }, 0, 0, 0, 0},
{"new_bp", MMPush, {SourceView::NewBreakpointCB, 0}, 0, 0, 0, 0},
{"new_wp", MMPush, {SourceView::NewWatchpointCB, 0}, 0, 0, 0, 0},
{"print", MMPush,
{SourceView::PrintWatchpointCB, XtPointer(0) }, 0, 0, 0, 0},
{"enable", MMPush,
{SourceView::BreakpointCmdCB, "enable" }, 0, 0, 0, 0},
{"disable", MMPush,
{SourceView::BreakpointCmdCB, "disable" }, 0, 0, 0, 0},
{"delete", MMPush | MMHelp,
{SourceView::BreakpointCmdCB, "delete" }, 0, 0, 0, 0},
MMEnd
};
struct TextItms {
enum Itms {
Print,
Disp,
Watch,
Sep1,
PrintRef,
DispRef,
WatchRef,
Sep2,
Whatis,
Sep3,
Lookup,
Break,
Clear
};
};
static String text_cmd_labels[] =
{
"Print ",
"Display ",
"Watch ",
"",
"Print ",
"Display ",
"Watch ",
"",
"What is ",
"",
"Lookup " ,
"Break at ",
"Clear at "
};
MMDesc SourceView::text_popup[] =
{
{"print", MMPush, {SourceView::text_popup_printCB, 0}, 0, 0, 0, 0},
{"disp", MMPush, {SourceView::text_popup_dispCB, 0}, 0, 0, 0, 0},
{"watch", MMPush | MMUnmanaged,
{SourceView::text_popup_watchCB, 0}, 0, 0, 0, 0},
MMSep,
{"printRef", MMPush,
{SourceView::text_popup_print_refCB, 0}, 0, 0, 0, 0},
{"dispRef", MMPush,
{SourceView::text_popup_disp_refCB, 0}, 0, 0, 0, 0},
{"watchRef", MMPush | MMUnmanaged,
{SourceView::text_popup_watch_refCB, 0}, 0, 0, 0, 0},
MMSep,
{"whatis", MMPush, {SourceView::text_popup_whatisCB, 0}, 0, 0, 0, 0},
MMSep,
{"lookup", MMPush, {SourceView::text_popup_lookupCB, 0}, 0, 0, 0, 0},
{"breakAt", MMPush, {SourceView::text_popup_breakCB, 0}, 0, 0, 0, 0},
{"clearAt", MMPush, {SourceView::text_popup_clearCB, 0}, 0, 0, 0, 0},
MMEnd
};
//-----------------------------------------------------------------------
// Glyphs and images
//-----------------------------------------------------------------------
#include "icons/glyphs/arrow.xbm"
#include "icons/glyphs/greyarrow.xbm"
#include "icons/glyphs/pastarrow.xbm"
#include "icons/glyphs/signalarrow.xbm"
#include "icons/glyphs/dragarrow.xbm"
#include "icons/glyphs/stop.xbm"
#include "icons/glyphs/greystop.xbm"
#include "icons/glyphs/dragstop.xbm"
#include "icons/glyphs/cond.xbm"
#include "icons/glyphs/greycond.xbm"
#include "icons/glyphs/dragcond.xbm"
#include "icons/glyphs/temp.xbm"
#include "icons/glyphs/greytemp.xbm"
#include "icons/glyphs/dragtemp.xbm"
//-----------------------------------------------------------------------
// Data. Lots of 'em.
//-----------------------------------------------------------------------
Widget SourceView::toplevel_w = 0;
Widget SourceView::source_form_w = 0;
Widget SourceView::source_text_w = 0;
Widget SourceView::code_form_w = 0;
Widget SourceView::code_text_w = 0;
Widget SourceView::edit_breakpoints_dialog_w = 0;
Widget SourceView::breakpoint_list_w = 0;
Widget SourceView::stack_dialog_w = 0;
Widget SourceView::frame_list_w = 0;
Widget SourceView::up_w = 0;
Widget SourceView::down_w = 0;
Widget SourceView::register_dialog_w = 0;
Widget SourceView::register_list_w = 0;
Widget SourceView::all_registers_w = 0;
Widget SourceView::int_registers_w = 0;
Widget SourceView::thread_dialog_w = 0;
Widget SourceView::thread_list_w = 0;
bool SourceView::stack_dialog_popped_up = false;
bool SourceView::register_dialog_popped_up = false;
bool SourceView::thread_dialog_popped_up = false;
bool SourceView::cache_source_files = true;
bool SourceView::cache_machine_code = true;
bool SourceView::display_glyphs = true;
bool SourceView::display_line_numbers = false;
bool SourceView::disassemble = true;
bool SourceView::all_registers = false;
int SourceView::source_indent_amount = 4;
int SourceView::script_indent_amount = 4;
int SourceView::code_indent_amount = 4;
int SourceView::line_indent_amount = 4;
int SourceView::tab_width = 8;
int SourceView::lines_above_cursor = 2;
int SourceView::lines_below_cursor = 3;
SourceOrigin SourceView::current_origin = ORIGIN_NONE;
Map<int, BreakPoint> SourceView::bp_map;
string SourceView::current_file_name = "";
int SourceView::line_count = 0;
IntIntArrayAssoc SourceView::bps_in_line;
TextPositionArray SourceView::_pos_of_line;
StringArray SourceView::bp_addresses;
StringStringAssoc SourceView::file_cache;
StringOriginAssoc SourceView::origin_cache;
StringStringAssoc SourceView::source_name_cache;
StringStringAssoc SourceView::file_name_cache;
CodeCache SourceView::code_cache;
string SourceView::current_source;
string SourceView::current_code;
string SourceView::current_code_start;
string SourceView::current_code_end;
string SourceView::current_pwd = cwd();
string SourceView::current_class_path = ".";
XmTextPosition SourceView::last_top = 0;
XmTextPosition SourceView::last_pos = 0;
XmTextPosition SourceView::last_start_highlight = 0;
XmTextPosition SourceView::last_end_highlight = 0;
XmTextPosition SourceView::last_top_pc = 0;
XmTextPosition SourceView::last_pos_pc = 0;
XmTextPosition SourceView::last_start_highlight_pc = 0;
XmTextPosition SourceView::last_end_highlight_pc = 0;
string SourceView::last_execution_file = "";
int SourceView::last_execution_line = 0;
string SourceView::last_execution_pc = "";
string SourceView::last_shown_pc = "";
int SourceView::last_frame_pos = 0;
bool SourceView::frame_pos_locked = false;
int SourceView::current_frame = -1;
bool SourceView::checking_scroll = false;
bool SourceView::at_lowest_frame = true;
bool SourceView::signal_received = false;
int SourceView::max_popup_expr_length = 20;
int SourceView::max_breakpoint_number_seen = 0;
//-----------------------------------------------------------------------
// Selection stuff
//-----------------------------------------------------------------------
static XmTextPosition selection_startpos;
static XmTextPosition selection_endpos;
static Time selection_time;
#if XtSpecificationRelease < 6
static XEvent selection_event;
#endif
//-----------------------------------------------------------------------
// Helping functions.
//-----------------------------------------------------------------------
// Sort A
static void sort(IntArray& a)
{
// Shell sort -- simple and fast
int h = 1;
do {
h = h * 3 + 1;
} while (h <= a.size());
do {
h /= 3;
for (int i = h; i < a.size(); i++)
{
int v = a[i];
int j;
for (j = i; j >= h && a[j - h] > v; j -= h)
a[j] = a[j - h];
if (i != j)
a[j] = v;
}
} while (h != 1);
}
// Return index of RXADDRESS in S, beginning from POS. Stop search at newline.
static int address_index(const string& s, int pos)
{
int eol = s.index('\n');
if (eol < 0)
eol = s.length();
string first_line = ((string&)s).at(pos, eol - pos);
int i = 0;
while (i < int(first_line.length()) && isspace(first_line[i]))
i++;
i = first_line.index(rxaddress_start, i);
if (i < 0)
return -1;
else
return pos + i;
}
// Return true if W is a descendant of code_form_w
bool SourceView::is_code_widget(Widget w)
{
while (w != 0)
{
if (w == code_form_w)
return true;
else
w = XtParent(w);
}
return false;
}
// Return true if W is a descendant of source_form_w
bool SourceView::is_source_widget(Widget w)
{
while (w != 0)
{
if (w == source_form_w)
return true;
else
w = XtParent(w);
}
return false;
}
string& SourceView::current_text(Widget w)
{
assert(is_source_widget(w) || is_code_widget(w));
if (is_code_widget(w))
return current_code;
else
return current_source;
}
static const int MAX_INDENT = 64;
int SourceView::indent_amount(Widget w, int pos)
{
assert(is_source_widget(w) || is_code_widget(w));
int indent = 0;
if (is_code_widget(w))
{
indent = code_indent_amount;
}
else
{
indent = source_indent_amount;
if (display_line_numbers)
indent += line_indent_amount;
// Set a minimum indentation for scripting languages
if (gdb->requires_script_indent())
indent = max(indent, script_indent_amount);
}
// Make sure indentation stays within reasonable bounds
indent = min(max(indent, 0), MAX_INDENT);
if (pos >= 0)
{
const string& text = current_text(w);
while (pos < int(text.length()) && text[pos] == ' ')
{
pos++;
indent++;
}
}
return indent;
}
//-----------------------------------------------------------------------
// Methods
//-----------------------------------------------------------------------
// ***************************************************************************
//
void SourceView::line_popup_setCB (Widget w,
XtPointer client_data,
XtPointer)
{
string address = *((string *)client_data);
create_bp(address, w);
}
void SourceView::line_popup_set_tempCB (Widget w,
XtPointer client_data,
XtPointer)
{
string address = *((string *)client_data);
create_temp_bp(address, w);
}
// Create or clear a breakpoint at position A. If SET, create a
// breakpoint; if not SET, delete it. If TEMP, make the breakpoint
// temporary. If COND is given, break only iff COND evals to true. W
// is the origin.
void SourceView::set_bp(const string& a, bool set, bool temp,
const string& cond, Widget w)
{
int new_bps = max_breakpoint_number_seen + 1;
string address = a;
if (address != "" && address[0] == '0')
address = "*" + address; // Address given
if (!set)
{
// Clear bp
gdb_command(clear_command(address), w);
}
else
{
// Set bp
switch (gdb->type())
{
case GDB:
case PYDB:
if (temp)
gdb_command("tbreak " + address, w);
else
gdb_command("break " + address, w);
break;
case DBX:
{
string cond_suffix = "";
if (cond != "")
{
if (gdb->has_handler_command())
cond_suffix = " -if " + cond;
else
cond_suffix = " if " + cond;
}
if (address.contains('*', 0))
{
// Address given
address = address.after('*');
gdb_command("stopi at " + address + cond_suffix, w);
if (temp)
{
syncCommandQueue();
gdb_command("when $pc == " + address + " "
+ command_list(clear_command(address, true,
new_bps)),
w);
}
}
else
{
string line = "";
if (address.matches(rxint))
{
// Line number given
line = address;
gdb_command("stop at " + address + cond_suffix, w);
}
else if (is_file_pos(address))
{
// FILE:LINE given
int colon_index = address.index(':', -1);
assert(colon_index >= 0);
string file = address.before(colon_index);
line = address.after(colon_index);
gdb_command("file " + file, w);
gdb_command("stop at " + line + cond_suffix, w);
}
else
{
// Function given
string pos = dbx_lookup(address);
if (pos.contains(':'))
{
string file = pos.before(':');
line = pos.after(':');
gdb_command("file " + file, w);
gdb_command("stop at " + line + cond_suffix, w);
}
else
{
// Cannot determine function position - try this one
gdb_command("stop in " + address + cond_suffix, w);
}
}
if (temp && line != "")
{
syncCommandQueue();
string clear_cmd = clear_command(line, true, new_bps);
gdb_command("when at " + line + " "
+ command_list(clear_cmd), w);
}
}
break;
}
case JDB:
{
if (is_file_pos(address))
gdb_command("stop at " + address);
else
gdb_command("stop in " + address);
break;
}
case XDB:
{
string command;
if (address.contains('*', 0))
command = "ba " + address.after('*');
else
command = "b " + address;
if (temp)
command += " \\1t";
if (cond != "" && !gdb->has_condition_command())
command += " {if " + cond + " {} {Q;c}}";
gdb_command(command, w);
break;
}
case PERL:
{
if (is_file_pos(address))
{
string file = address.before(':');
address = address.after(':');
if (!file_matches(file, current_file_name))
gdb_command("f " + file, w);
}
string command = "b " + address;
if (cond != "" && !gdb->has_condition_command())
command += " " + cond;
gdb_command(command, w);
break;
}
}
if (cond != "" && gdb->has_condition_command())
{
// Add condition
gdb_command(gdb->condition_command(itostring(new_bps), cond), w);
}
}
}
// ***************************************************************************
//
void SourceView::clearBP(XtPointer client_data, XtIntervalId *)
{
int bp_nr = (int)(long)client_data;
BreakPoint *bp = bp_map.get(bp_nr);
if (bp != 0)
delete_bp(bp_nr);
}
// Save last `jump' target for XDB
static string last_jump_address;
void SourceView::clearJumpBP(const string& msg, void *data)
{
set_status(msg);
if (gdb->type() == XDB && msg == "")
{
// Moving PC was successful.
show_execution_position(last_jump_address, true);
}
int old_max_breakpoint_number_seen = (int)(long)data;
for (int i = old_max_breakpoint_number_seen + 1;
i <= max_breakpoint_number_seen; i++)
{
// Delete all recently created breakpoints
XtAppAddTimeOut(XtWidgetToApplicationContext(source_text_w),
0, clearBP, XtPointer(i));
}
}
void SourceView::line_popup_temp_n_contCB (Widget w,
XtPointer client_data,
XtPointer)
{
string address = *((string *)client_data);
temp_n_cont(address, w);
}
void SourceView::temp_n_cont(const string& a, Widget w)
{
string address = a;
switch (gdb->type())
{
case GDB:
#if 0 // GDB `until' only works in the current frame
gdb_command("until " + address, w);
break;
#endif
case DBX:
case JDB:
case PYDB:
{
int old_max_breakpoint_number_seen = max_breakpoint_number_seen;
// Create a temporary breakpoint
create_temp_bp(address, w);
// Make sure the temporary breakpoint is deleted after `cont'
Command c("cont", w);
c.callback = clearJumpBP;
c.data = XtPointer(old_max_breakpoint_number_seen);
gdb_command(c);
break;
}
case XDB:
if (address.contains('*', 0))
address = address.after('*');
gdb_command("c " + address, w);
break;
case PERL:
if (is_file_pos(address))
address = address.after(':');
gdb_command("c " + address, w);
break;
}
}
// ***************************************************************************
//
void SourceView::line_popup_set_pcCB(Widget w,
XtPointer client_data,
XtPointer)
{
string address = *((string *)client_data);
move_pc(address, w);
}
// ***************************************************************************
//
bool SourceView::move_pc(const string& a, Widget w)
{
string address = a;
if (address.contains('*', 0))
{
if (compare_address(address.after('*'), last_execution_pc) == 0)
return false; // PC already at address
}
else
{
string file = address.before(':');
int line = get_positive_nr(address.after(':'));
if (file_matches(file, last_execution_file)
&& line == last_execution_line)
return false; // PC already at address
}
if (gdb->has_jump_command())
{
int old_max_breakpoint_number_seen = max_breakpoint_number_seen;
// We prefer the GDB `jump' command to setting `$pc'
// immediately since `jump' requires confirmation when jumping
// out of the current function.
switch (gdb->type())
{
case DBX:
// DBX immediately resumes execution - create a temp
// breakpoint at ADDRESS
create_temp_bp(address, w);
// DBX `cont at ' requires a line number.
gdb_command("file " + address.before(':'));
// FALL THROUGH
case XDB:
// XDB 'g' wants only a line number
address = address.after(':');
break;
case GDB:
// GDB immediately resumes execution - create a temp
// breakpoint at ADDRESS
create_temp_bp(address, w);
break;
case JDB:
case PYDB:
case PERL:
break; // Never reached
}
// Jump to the new address and clear the breakpoint again
last_jump_address = a;
Command c(gdb->jump_command(address), w);
c.callback = clearJumpBP;
c.data = XtPointer(old_max_breakpoint_number_seen);
gdb_command(c);
return true;
}
else if (gdb->type() != JDB && gdb->has_assign_command())
{
// Try the `set $pc = ADDR' alternative.
if (address.contains('*', 0))
{
address = address.after('*');
}
else
{
lookup(address, true);
syncCommandQueue();
address = last_shown_pc;
}
if (address == "")
{
set_status("Cannot determine address of " + a);
}
else
{
gdb_command(gdb->assign_command("$pc", address), w);
return true;
}
}
return false;
}
bool SourceView::move_bp(int bp_nr, const string& a, Widget w, bool copy)
{
string address = a;
// clog << "Moving breakpoint " << bp_nr << " to " << address << '\n';
BreakPoint *bp = bp_map.get(bp_nr);
if (bp == 0)
return false; // No such breakpoint
if (!copy)
{
if (address.contains('*', 0))
{
if (compare_address(address.after('*'), bp->address()) == 0)
return false; // Breakpoint already at address
}
else
{
string file = address.before(':');
int line = get_positive_nr(address.after(':'));
if (bp_matches(bp, file, line))
return false; // Breakpoint already at address
}
}
// Create a new breakpoint at ADDRESS, making it inherit the
// current settings
ostrstream os;
bool ok = bp->get_state(os, 0, false, address);
if (!ok)
return false; // Command failed
int new_bp_nr = next_breakpoint_number();
string commands(os);
commands.gsub("@0@", itostring(new_bp_nr));
while (commands != "")
{
string command = commands.before('\n');
gdb_command(command, w);
commands = commands.after('\n');
}
if (copy)
{
// Copy properties
copy_breakpoint_properties(bp_nr, new_bp_nr);
}
else
{
// Rename properties
move_breakpoint_properties(bp_nr, new_bp_nr);
// Delete old breakpoint
delete_bp(bp_nr);
}
return true;
}
void SourceView::_set_bps_cond(IntArray& _nrs, string cond,
int make_false, Widget w)
{
// _NRS might be changed via MOVE_BREAKPOINT_PROPERTIES,
// so we make a copy
IntArray nrs(_nrs);
int count = 0;
for (int i = 0; i < nrs.size(); i++)
{
int bp_nr = nrs[i];
BreakPoint *bp = bp_map.get(bp_nr);
if (bp == 0)
continue; // No such breakpoint
string c = cond;
if (c == char(-1))
c = bp->condition();
int m = make_false;
if (m < 0)
m = (!bp->enabled() && !gdb->has_enable_command());
if (m)
c = BreakPoint::make_false(c);
if (gdb->has_condition_command())
{
// Use the `cond' command to assign a condition
gdb_command(gdb->condition_command(itostring(bp_nr), c), w);
}
else
{
// Create a new breakpoint with a new condition COND, making it
// inherit the current settings
ostrstream os;
bool ok = bp->get_state(os, 0, false, "", c);
if (!ok)
continue; // Command failed
string commands(os);
int new_bp_nr = bp_nr;
if (gdb->has_numbered_breakpoints())
{
new_bp_nr = next_breakpoint_number() + count;
commands.gsub("@0@", itostring(new_bp_nr));
}
while (commands != "")
{
string command = commands.before('\n');
gdb_command(command, w);
commands = commands.after('\n');
}
if (gdb->has_numbered_breakpoints())
{
// Copy properties to new breakpoint
move_breakpoint_properties(bp_nr, new_bp_nr);
// Delete old breakpoint
delete_bp(bp_nr);
// Next breakpoint will get the next number
count++;
}
}
}
}
// ***************************************************************************
//
void SourceView::bp_popup_deleteCB (Widget w,
XtPointer client_data,
XtPointer)
{
int bp_nr = *((int *)client_data);
delete_bp(bp_nr, w);
}
// ***************************************************************************
//
void SourceView::bp_popup_disableCB (Widget w,
XtPointer client_data,
XtPointer)
{
int bp_nr = *((int *)client_data);
BreakPoint *bp = bp_map.get(bp_nr);
if (bp != 0)
{
if (bp->enabled())
disable_bp(bp_nr, w);
else
enable_bp(bp_nr, w);
}
}
// Convert NRS to a list of numbers
string SourceView::numbers(const IntArray& nrs)
{
string cmd = "";
for (int i = 0; i < nrs.size(); i++)
{
if (i > 0)
cmd += " ";
cmd += itostring(nrs[i]);
}
return cmd;
}
// Same, but use "" if we have GDB and all numbers are used
string SourceView::all_numbers(const IntArray& nrs)
{
if ((gdb->type() == GDB || gdb->type() == PYDB) && all_bps(nrs))
return ""; // In GDB, no arg means `all'
else
return numbers(nrs);
}
// Return true if NRS contains all breakpoints and
// a GDB delete/disable/enable command can be given without args.
bool SourceView::all_bps(const IntArray& nrs)
{
if ((gdb->type() != GDB && gdb->type() != PYDB) || nrs.size() < 2)
return false;
MapRef ref;
BreakPoint *bp = 0;
for (bp = bp_map.first(ref); bp != 0; bp = bp_map.next(ref))
{
bool found = false;
for (int i = 0; !found && i < nrs.size(); i++)
{
if (bp->number() == nrs[i])
found = true;
}
if (!found)
return false;
}
return true;
}
void SourceView::enable_bps(IntArray& nrs, Widget w)
{
if (gdb->has_enable_command())
{
gdb_command(gdb->enable_command(all_numbers(nrs)), w);
}
else if (gdb->has_conditions())
{
// Unset `false' breakpoint condition
enable_bps_cond(nrs, w);
}
}
void SourceView::disable_bps(IntArray& nrs, Widget w)
{
if (gdb->has_disable_command())
{
gdb_command(gdb->disable_command(all_numbers(nrs)), w);
}
else if (gdb->has_conditions())
{
// Set breakpoint condition to `false'
disable_bps_cond(nrs, w);
}
}
void SourceView::delete_bps(IntArray& nrs, Widget w)
{
if (gdb->recording() && gdb->has_clear_command())
{
// While recording, prefer commands without explicit numbers.
for (int i = 0; i < nrs.size(); i++)
{
BreakPoint *bp = bp_map.get(nrs[i]);
if (bp != 0)
gdb_command(clear_command(bp->pos()));
}
}
else if (gdb->has_delete_command())
{
gdb_command(gdb->delete_command(all_numbers(nrs)), w);
}
else
{
for (int i = 0; i < nrs.size(); i++)
gdb_command(delete_command(nrs[i]));
}
}
// A generic deletion command for breakpoint BP_NR - either `clear' or `delete'
string SourceView::delete_command(int bp_nr)
{
if (gdb->has_delete_command())
{
return gdb->delete_command(itostring(bp_nr));
}
else if (gdb->has_clear_command())
{
BreakPoint *bp = bp_map.get(bp_nr);
if (bp != 0)
return clear_command(bp->pos());
}
return ""; // No way to delete a breakpoint (*sigh*)
}
// Return `clear ARG' command. If CLEAR_NEXT is set, attempt to guess
// the next event number and clear this one as well. (This is useful
// for setting temporary breakpoints, as `delete' must also clear the
// event handler we're about to install.) Consider only breakpoints
// whose number is >= FIRST_BP.
string SourceView::clear_command(string pos, bool clear_next, int first_bp)
{
string file = current_file_name;
string line = pos;
if (gdb->type() == DBX && !pos.contains(':') && !pos.matches(rxint))
pos = dbx_lookup(pos);
if (pos.contains(':'))
{
file = pos.before(':');
line = pos.after(':');
}
int line_no = atoi(line);
if (!clear_next && gdb->has_clear_command())
{
switch (gdb->type())
{
case GDB:
case JDB:
case PYDB:
return "clear " + pos;
case PERL:
if (line_no > 0 && file_matches(file, current_file_name))
return "d " + line;
break;
case DBX:
if (line_no > 0 && file_matches(file, current_file_name))
return "clear " + line;
break;
case XDB:
break;
}
}
int max_bp_nr = -1;
string bps = "";
MapRef ref;
for (BreakPoint* bp = bp_map.first(ref);
bp != 0;
bp = bp_map.next(ref))
{
if (bp->number() >= first_bp
&& bp_matches(bp, file, line_no))
{
if (bps != "")
bps += gdb->wants_delete_comma() ? ", " : " ";
bps += itostring(bp->number());
max_bp_nr = max(max_bp_nr, bp->number());
}
}
if (bps == "")
return "";
if (clear_next && max_bp_nr >= 0)
{
bps += (gdb->wants_delete_comma() ? ", " : " ");
bps += itostring(max_bp_nr + 1);
}
return gdb->delete_command(bps);
}
// ***************************************************************************
//
void SourceView::bp_popup_set_pcCB(Widget w, XtPointer client_data,
XtPointer call_data)
{
int bp_nr = *((int *)client_data);
BreakPoint *bp = bp_map.get(bp_nr);
if (bp != 0 && bp->address() != "")
{
string address = string('*') + bp->address();
line_popup_set_pcCB(w, XtPointer(&address), call_data);
}
}
// ***************************************************************************
//
void SourceView::text_popup_breakCB (Widget w,
XtPointer client_data,
XtPointer)
{
string* word_ptr = (string*)client_data;
create_bp(fortranize(*word_ptr, true), w);
}
void SourceView::text_popup_clearCB (Widget w,
XtPointer client_data,
XtPointer)
{
string* word_ptr = (string*)client_data;
clear_bp(fortranize(*word_ptr, true), w);
}
// ***************************************************************************
//
void SourceView::text_popup_printCB (Widget w,
XtPointer client_data,
XtPointer)
{
string* word_ptr = (string*)client_data;
assert(word_ptr->length() > 0);
gdb_command(gdb->print_command(fortranize(*word_ptr), false), w);
}
void SourceView::text_popup_print_refCB (Widget w,
XtPointer client_data, XtPointer)
{
string* word_ptr = (string*)client_data;
assert(word_ptr->length() > 0);
gdb_command(gdb->print_command(deref(fortranize(*word_ptr)), false), w);
}
// ***************************************************************************
//
void SourceView::text_popup_watchCB (Widget w,
XtPointer client_data,
XtPointer)
{
string* word_ptr = (string*)client_data;
assert(word_ptr->length() > 0);
gdb_command(gdb->watch_command(fortranize(*word_ptr)), w);
}
void SourceView::text_popup_watch_refCB (Widget w,
XtPointer client_data, XtPointer)
{
string* word_ptr = (string*)client_data;
assert(word_ptr->length() > 0);
gdb_command(gdb->watch_command(deref(fortranize(*word_ptr))), w);
}
// ***************************************************************************
//
void SourceView::text_popup_dispCB (Widget w, XtPointer client_data, XtPointer)
{
string* word_ptr = (string*)client_data;
assert(word_ptr->length() > 0);
gdb_command("graph display " + fortranize(*word_ptr), w);
}
void SourceView::text_popup_disp_refCB (Widget w,
XtPointer client_data, XtPointer)
{
string* word_ptr = (string*)client_data;
assert(word_ptr->length() > 0);
gdb_command("graph display " + deref(fortranize(*word_ptr)), w);
}
// ***************************************************************************
//
void SourceView::text_popup_whatisCB (Widget w, XtPointer client_data,
XtPointer)
{
string* word_ptr = (string*)client_data;
assert(word_ptr->length() > 0);
gdb_command(gdb->whatis_command(fortranize(*word_ptr)), w);
}
// ***************************************************************************
//
void SourceView::text_popup_lookupCB (Widget, XtPointer client_data, XtPointer)
{
string* word_ptr = (string*)client_data;
lookup(fortranize(*word_ptr, true));
}
// ***************************************************************************
//
// Return the normalized full path of FILE
string SourceView::full_path(string file)
{
/* Chris van Engelen <engelen@lucent.com>, Jul 10, 1997
*
* The regular expression is used to remove parts from the full path
* which look like "/aap/../". However, it should ***NOT*** remove
* the sequence "/../../" from the path, obviously ! On the other
* hand, sequences like "/.tmpdir.test/../" should be removed.
* Therefore, the regular expression reads now like:
*
* - forward slash
* - zero or more characters other than forward slash (dot is allowed)
* - any character other than forward slash or dot: this makes sure that
* a sequence like "/../../" is not modified.
* - forward slash, two dots, and the final forward slash.
*
* The only valid patterns which are not normalized are patterns ending
* in a dot: too bad, you can't win them all.
*/
#if RUNTIME_REGEX
static regex rxdotdot("/[^/]*[^/.]/\\.\\./");
#endif
file += '/';
if (!file.contains('/', 0))
file = current_pwd + "/" + file;
/* CvE, Jul 10, 1997
*
* Repeatedly remove patterns like /dir1/../ from the file name.
* Note that a number of /../ patterns may follow each other, like
* in "/dir1/dir1/dir3/../../../"
*/
unsigned int file_length = file.length();
unsigned int prev_file_length;
do {
prev_file_length = file_length;
file.gsub(rxdotdot, "/");
file_length = file.length();
} while (file_length != prev_file_length);
/* CvE, Jul 10, 1997
*
* Repeatedly remove pattern /./ from the file name.
* Note that a number of /./ patterns may follow each other.
* Note that if the first parameter of gsub is a C-string,
* the pattern is not regarded to be a regular expression,
* so the dot in the pattern does not need to be escaped!
*/
file_length = file.length();
do {
prev_file_length = file_length;
file.gsub("/./", "/");
file_length = file.length();
} while (file_length != prev_file_length);
file.gsub("//", "/");
if (file.contains('/', -1))
file = file.before(int(file.length() - 1));
return file;
}
// Return the basename of FILE. We don't use the default ::basename(),
// due to conflicts with the decl in <libiberty.h>.
const char *SourceView::basename(const char *name)
{
const char *base = name;
while (*name)
{
if (*name++ == '/')
base = name;
}
return base;
}
bool SourceView::file_matches(const string& file1, const string& file2)
{
if (gdb->type() == JDB)
return file1 == file2;
if (gdb->type() == GDB || app_data.use_source_path)
return file1 == file2 || full_path(file1) == full_path(file2);
return base_matches(file1, file2);
}
bool SourceView::is_current_file(const string& file)
{
if (gdb->type() == JDB || gdb->type() == PYDB)
return file == current_source_name();
else
return file_matches(file, current_file_name);
}
bool SourceView::base_matches(const string& file1, const string& file2)
{
return string(basename(file1.chars())) == string(basename(file2.chars()));
}
// Check if BP occurs in the current source text
bool SourceView::bp_matches(BreakPoint *bp, int line)
{
return bp_matches(bp, current_source_name(), line) ||
bp_matches(bp, current_file_name, line);
}
bool SourceView::bp_matches(BreakPoint *bp, const string& file, int line)
{
return bp->type() == BREAKPOINT &&
(line == 0 || bp->line_nr() == line) &&
(bp->file_name() == "" || file_matches(bp->file_name(), file));
}
// ***************************************************************************
//
// If this is true, no motion occurred while selecting
static bool selection_click = false;
static string last_info_output = "";
void SourceView::set_source_argCB(Widget text_w,
XtPointer client_data,
XtPointer call_data)
{
string& text = current_text(text_w);
if (text == "")
return;
XmTextVerifyCallbackStruct *cbs = (XmTextVerifyCallbackStruct *)call_data;
bool motion = bool((int)(long)client_data);
if (motion)
selection_click = false;
XmTextPosition startPos, endPos;
Boolean have_selection =
XmTextGetSelectionPosition(text_w, &startPos, &endPos);
if (have_selection && lesstif_version < 1000)
{
// In LessTif 0.87, the unmanaged DataDisp::graph_selection_w
// text widget is not notified that it just has lost the
// selection. The effect is that selecting an item from the
// source (or a selection in any other window) does *not*
// clear the selection in the data window, as it should.
// As a workaround, notify explicitly.
data_disp->SelectionLostCB();
}
if (!have_selection || (app_data.source_editing && startPos == endPos))
{
// No selection? If the current motion was caused by a mouse
// click, fetch word at current cursor position instead.
if (cbs != 0 && cbs->reason == XmCR_MOVING_INSERT_CURSOR)
{
XEvent *event = cbs->event;
if (event == 0)
{
// LessTif 0.79 may not pass a reasonable event here.
#if XtSpecificationRelease >= 6
// Use the last processed event instead.
event = XtLastEventProcessed(XtDisplay(text_w));
#else
// Use the last recorded selection event instead.
event = &selection_event;
#endif
selection_click = true;
}
// Button4 and Button5 events are generated by wheel mouses.
// Don't change the selection when the wheel is activated.
if (event != 0 &&
(event->type == ButtonPress || event->type == ButtonRelease) &&
event->xbutton.button != Button4 &&
event->xbutton.button != Button5)
{
find_word_bounds(text_w, cbs->newInsert,
startPos, endPos);
have_selection = True;
}
}
}
if (!have_selection && cbs == 0)
{
// Still no selection? We're probably called from setSelection();
// use the last selected word instead.
startPos = selection_startpos;
endPos = selection_endpos;
have_selection = True;
}
#if XtSpecificationRelease < 6
// Don't use this selection event again.
selection_event.type = KeyPress;
#endif
if (!have_selection)
{
// No selection - sorry
return;
}
int startIndex = 0;
if (startPos > 0)
startIndex = text.index('\n', startPos - text.length()) + 1;
int endIndex = 0;
if (endPos > 0)
endIndex = text.index('\n', endPos - text.length()) + 1;
bool in_bp_area = false;
if (selection_click && startIndex == endIndex)
{
string pos = "";
if (text_w == source_text_w)
{
int line_nr = 0;
bool in_text;
int bp_nr;
string address;
if (get_line_of_pos(source_text_w, startPos, line_nr, address,
in_text, bp_nr)
&& !in_text)
{
in_bp_area = true;
// Selection from line number area: prepend source file name
pos = current_source_name() + ":" + itostring(line_nr);
source_arg->set_string(pos);
// If a breakpoint is here, select this one only
MapRef ref;
for (BreakPoint* bp = bp_map.first(ref);
bp != 0;
bp = bp_map.next(ref))
{
bp->selected() = (bp_matches(bp, line_nr));
}
}
}
else if (text_w == code_text_w
&& startPos - startIndex <= indent_amount(text_w)
&& endPos - endIndex <= indent_amount(text_w))
{
// Selection from address area
int index = address_index(text, startPos);
if (index >= 0)
{
in_bp_area = true;
pos = text.from(index);
pos = pos.through(rxaddress);
source_arg->set_string(pos);
// If a breakpoint is here, select this one only
MapRef ref;
for (BreakPoint* bp = bp_map.first(ref);
bp != 0;
bp = bp_map.next(ref))
{
bp->selected() =
(bp->type() == BREAKPOINT &&
compare_address(pos, bp->address()) == 0);
}
}
}
selection_click = false;
}
if (in_bp_area)
{
// Update breakpoint selection
process_breakpoints(last_info_output);
}
else if (!motion && !selection_click)
{
// Selection from source or code
string s;
if (startPos < XmTextPosition(text.length())
&& endPos < XmTextPosition(text.length()))
{
s = text(startPos, endPos - startPos);
}
while (s.contains('\n'))
s = s.after('\n');
if (s != "")
source_arg->set_string(s);
}
}
BreakPoint *SourceView::breakpoint_at(string arg)
{
MapRef ref;
for (BreakPoint* bp = bp_map.first(ref); bp != 0; bp = bp_map.next(ref))
{
if (bp->type() != BREAKPOINT)
continue;
if (arg.matches(rxint))
{
// Line number for current source given
if (bp_matches(bp, atoi(arg)))
return bp;
}
else
{
string pos = arg;
if (!is_file_pos(pos))
{
// Function given
if (bp->arg() == pos)
return bp;
if (gdb->type() == DBX)
pos = dbx_lookup(arg);
}
if (is_file_pos(pos))
{
// File:line given
string file = pos.before(':');
string line = pos.after(':');
if (bp_matches(bp, file, atoi(line)))
return bp;
}
}
}
return 0;
}
BreakPoint *SourceView::watchpoint_at(string expr)
{
for (int trial = 0; trial <= 2; trial++)
{
MapRef ref;
for (BreakPoint* bp = bp_map.first(ref); bp != 0;
bp = bp_map.next(ref))
{
if (bp->type() != WATCHPOINT)
continue;
switch (trial)
{
case 0:
if (bp->expr() == expr)
{
// Expression matches exactly
return bp;
}
break;
case 1:
if (bp->expr().contains('(') && bp->expr().before('(') == expr)
{
// Expr matches EXPR(...) (e.g. a qualified function name)
return bp;
}
case 2:
if (bp->expr().contains("`" + expr, -1) ||
bp->expr().contains("::" + expr, -1))
{
// Expr matches ...`EXPR (a Sun DBX identifier)
// or ...::EXPR (an SGI DBX identifier)
return bp;
}
}
}
}
return 0;
}
// ***************************************************************************
// Show position POS in TEXT_W, scrolling nicely
void SourceView::ShowPosition(Widget text_w, XmTextPosition pos, bool fromTop)
{
string& text = current_text(text_w);
if (text.length() == 0)
return; // No position to show
short rows = 0;
XmTextPosition current_top = 0;
XtVaGetValues(text_w,
XmNrows, &rows,
XmNtopCharacter, ¤t_top,
NULL);
// Find current relative row
short relative_row = 1;
for (XmTextPosition p = min(text.length() - 1, pos); p > current_top; p--)
if (text[p] == '\n')
relative_row++;
if (relative_row <= lines_above_cursor
|| relative_row >= rows - (lines_below_cursor + 1))
{
// Determine new TOP position
short n = rows / 2; // #Lines between new TOP and POS
if (fromTop || relative_row <= lines_above_cursor)
n = lines_above_cursor;
else if (relative_row >= rows - (lines_below_cursor + 1))
n = rows - (lines_below_cursor + 1);
XmTextPosition new_top = pos;
for (;;) {
while (new_top > 0 && text[new_top - 1] != '\n')
new_top--;
if (new_top == 0 || n-- <= 0)
break;
new_top--;
}
XmTextSetTopCharacter(text_w, new_top);
}
XmTextShowPosition(text_w, pos); // just to make sure
}
void SourceView::SetInsertionPosition(Widget text_w,
XmTextPosition pos, bool fromTop)
{
ShowPosition(text_w, pos, fromTop);
XmTextSetInsertionPosition(text_w, pos);
}
//-----------------------------------------------------------------------
// Error handling
//-----------------------------------------------------------------------
StringArray SourceView::bad_files;
bool SourceView::new_bad_file(const string& file_name)
{
for (int i = 0; i < bad_files.size(); i++)
if (file_name == bad_files[i])
return false;
bad_files += file_name;
return true;
}
void SourceView::post_file_error(const string& file_name,
string text, String name,
Widget origin)
{
if (new_bad_file(file_name))
post_error(text, name, origin);
}
void SourceView::post_file_warning(const string& file_name,
string text, String name,
Widget origin)
{
if (new_bad_file(file_name))
post_warning(text, name, origin);
}
//-----------------------------------------------------------------------
// Read file
//-----------------------------------------------------------------------
// Read local file from FILE_NAME
String SourceView::read_local(const string& file_name, long& length,
bool silent)
{
StatusDelay delay("Reading file " + quote(file_name));
length = 0;
// Make sure the file is a regular text file and open it
int fd;
if ((fd = open(file_name, O_RDONLY)) < 0)
{
delay.outcome = strerror(errno);
if (!silent)
post_file_error(file_name,
file_name + ": " + delay.outcome,
"source_file_error", source_text_w);
return 0;
}
struct stat statb;
if (fstat(fd, &statb) < 0)
{
delay.outcome = strerror(errno);
if (!silent)
post_file_error(file_name,
file_name + ": " + delay.outcome,
"source_file_error", source_text_w);
return 0;
}
// Avoid loading from directory, socket, device, or otherwise.
if (!S_ISREG(statb.st_mode))
{
delay.outcome = "not a regular file";
if (!silent)
post_file_error(file_name,
file_name + ": " + delay.outcome,
"source_file_error", source_text_w);
return 0;
}
// Put the contents of the file in the Text widget by allocating
// enough space for the entire file and reading the file into the
// allocated space.
char* text = XtMalloc(unsigned(statb.st_size + 1));
if ((length = read(fd, text, statb.st_size)) != statb.st_size)
{
delay.outcome = "truncated";
if (!silent)
post_file_error(file_name,
file_name + ": " + delay.outcome,
"source_trunc_error", source_text_w);
}
close(fd);
text[statb.st_size] = '\0'; // be sure to null-terminate
if (statb.st_size == 0)
{
delay.outcome = "empty file";
if (!silent)
post_file_warning(file_name,
file_name + ": " + delay.outcome,
"source_empty_warning", source_text_w);
}
return text;
}
// Read (possibly remote) file FILE_NAME; a little slower
String SourceView::read_remote(const string& file_name, long& length,
bool silent)
{
StatusDelay delay("Reading file " +
quote(file_name) + " from " + gdb_host);
length = 0;
string cat_command = sh_command("cat " + file_name);
Agent cat(cat_command);
cat.start();
FILE *fp = cat.inputfp();
if (fp == 0)
{
delay.outcome = "failed";
return 0;
}
String text = XtMalloc(1);
do {
text = XtRealloc(text, length + BUFSIZ + 1);
length += fread(text + length, sizeof(char), BUFSIZ, fp);
} while (!feof(fp));
text[length] = '\0'; // be sure to null-terminate
if (length == 0)
{
if (!silent)
post_file_error(file_name,
"Cannot access remote file " + quote(file_name),
"remote_file_error", source_text_w);
delay.outcome = "failed";
}
return text;
}
// Read class CLASS_NAME
String SourceView::read_class(const string& class_name,
string& file_name, SourceOrigin& origin,
long& length, bool silent)
{
StatusDelay delay("Loading class " + quote(class_name));
String text = 0;
length = 0;
file_name = java_class_file(class_name);
if (file_name != "")
{
if (remote_gdb())
text = read_remote(file_name, length, true);
else
{
file_name = full_path(file_name);
text = read_local(file_name, length, true);
}
}
if (text != 0 && length != 0)
{
// Save class name for further reference
source_name_cache[file_name] = class_name;
origin = remote_gdb() ? ORIGIN_REMOTE : ORIGIN_LOCAL;
return text;
}
else
{
// Could not load class
file_name = class_name;
origin = ORIGIN_NONE;
delay.outcome = "failed";
if (!silent)
post_file_error(class_name,
"Cannot access class " + quote(class_name),
"class_error", source_text_w);
return 0;
}
}
#define HUGE_LINE_NUMBER "1000000"
// Read file FILE_NAME via the GDB `list' function
// Really slow, is guaranteed to work for source files.
String SourceView::read_from_gdb(const string& file_name, long& length,
bool /* silent */)
{
length = 0;
if (!gdb->isReadyWithPrompt())
return 0;
if (gdb->type() == JDB)
return 0; // Won't work with JDB
StatusDelay delay("Reading file " + quote(file_name) +
" from " + gdb->title());
string command;
switch (gdb->type())
{
case GDB:
command = "list " + file_name + ":1," HUGE_LINE_NUMBER;
break;
case DBX:
case PYDB:
command = "list 1," HUGE_LINE_NUMBER;
break;
case PERL:
command = "l 1-" HUGE_LINE_NUMBER;
break;
case JDB:
command = "list " + file_name;
break;
case XDB:
command = "w " HUGE_LINE_NUMBER;
break;
}
string listing = gdb_question(command, -1, true);
// GDB listings have the format <NUMBER>\t<LINE>.
// Copy LINE only; line numbers will be re-added later.
// Note that tabs may be expanded to spaces due to a PTY interface.
String text = XtMalloc(listing.length());
int i = 0;
length = 0;
while (i < int(listing.length()))
{
int count = 0;
// Skip leading spaces. Some debuggers also issue `*', `=',
// or `>' to indicate the current position.
while (count < 8
&& i < int(listing.length())
&& (isspace(listing[i])
|| listing[i] == '='
|| listing[i] == '*'
|| listing[i] == '>'))
i++, count++;
if (i < int(listing.length()) && isdigit(listing[i]))
{
// Skip line number
while (i < int(listing.length()) && isdigit(listing[i]))
i++, count++;
// Skip `:' (XDB output)
if (count < 8 && i < int(listing.length()) && listing[i] == ':')
i++, count++;
// Break at first non-blank character or after 8 characters
while (count < 8 && i < int(listing.length()) && listing[i] == ' ')
i++, count++;
// Skip tab character
if (count < 8 && i < int(listing.length()) && listing[i] == '\t')
i++;
// Copy line
while (i < int(listing.length()) && listing[i] != '\n')
text[length++] = listing[i++];
// Copy newline character
text[length++] = '\n';
i++;
}
else
{
int start = i;
// Some other line -- the prompt, maybe?
while (i < int(listing.length()) && listing[i] != '\n')
i++;
if (i < int(listing.length()))
i++;
string msg = listing.from(start);
msg = msg.before('\n');
if (!msg.contains("end of file")) // XDB issues this
post_gdb_message(msg, true, source_text_w);
}
}
text[length] = '\0'; // be sure to null-terminate
if (length == 0)
delay.outcome = "failed";
return text;
}
// Read file FILE_NAME and format it
String SourceView::read_indented(string& file_name, long& length,
SourceOrigin& origin, bool silent)
{
length = 0;
Delay delay;
long t;
String text = 0;
origin = ORIGIN_NONE;
string full_file_name = file_name;
if (gdb->type() == JDB && !file_name.contains('/'))
{
// FILE_NAME is a class name. Search class in JDB `use' path.
text = read_class(file_name, full_file_name, origin, length, true);
}
if (gdb->type() == PERL && file_name.contains('-', 0))
{
// Attempt to load `-e' in Perl or likewise
origin = ORIGIN_NONE;
return 0;
}
if (text == 0 || length == 0)
{
for (int trial = 1; (text == 0 || length == 0) && trial <= 2; trial++)
{
switch (trial)
{
case 1:
// Loop #1: use full path of file
full_file_name = full_path(file_name);
break;
case 2:
// Loop #2: ask debugger for full path, using `edit'
full_file_name = full_path(dbx_path(file_name));
if (full_file_name == full_path(file_name))
continue;
break;
}
// Attempt #1. Try to read file from remote source.
if ((text == 0 || length == 0) && remote_gdb())
{
text = read_remote(full_file_name, length, true);
if (text != 0)
origin = ORIGIN_REMOTE;
}
// Attempt #2. Read file from local source.
if ((text == 0 || length == 0) && !remote_gdb())
{
text = read_local(full_file_name, length, true);
if (text != 0)
origin = ORIGIN_LOCAL;
}
// Attempt #3. Read file from local source, even if we are remote.
if ((text == 0 || length == 0) && remote_gdb())
{
text = read_local(full_file_name, length, true);
if (text != 0)
origin = ORIGIN_LOCAL;
}
}
}
// Attempt #4. Read file from GDB.
if (text == 0 || length == 0)
{
string saved_current_file_name = current_file_name;
current_file_name = full_file_name;
string source_name = current_source_name();
current_file_name = saved_current_file_name;
text = read_from_gdb(source_name, length, silent);
if (text != 0 && length != 0)
{
// Use the source name as file name
full_file_name = source_name;
if (text != 0)
origin = ORIGIN_GDB;
}
}
if ((text == 0 || length == 0) && !silent)
{
// All failed - produce an appropriate error message.
if (gdb->type() == JDB)
text = read_class(file_name, full_file_name, origin,
length, false);
else if (!remote_gdb())
text = read_local(full_file_name, length, false);
else
text = read_remote(full_file_name, length, false);
}
if (text == 0 || length == 0)
{
origin = ORIGIN_NONE;
return 0;
}
// At this point, we have a source text.
file_name = full_file_name;
// Determine text length and number of lines
int lines = 0;
for (t = 0; t < length; t++)
if (text[t] == '\n')
lines++;
int indented_text_length = length;
if (length > 0 && text[length - 1] != '\n')
{
// Text does not end in '\n':
// Make room for final '\n'
indented_text_length += 1;
// Make room for final line
lines++;
}
// Make room for line numbers
int indent = indent_amount(source_text_w);
indented_text_length += (indent + script_indent_amount) * lines;
String indented_text = XtMalloc(indented_text_length + 1);
string line_no_s = replicate(' ', indent);
t = 0;
char *pos_ptr = indented_text; // Writing position in indented_text
while (t < length)
{
assert (pos_ptr - indented_text <= indented_text_length);
// Increase line number
int i;
for (i = indent - 2; i >= 0; i--)
{
char& c = line_no_s[i];
if (c == ' ')
{
c = '1';
break;
}
else if (c < '9')
{
c++;
break;
}
else
c = '0';
}
// Copy line number
for (i = 0; i < indent; i++)
*pos_ptr++ = display_line_numbers ? line_no_s[i] : ' ';
if (indent < script_indent_amount)
{
// Check for empty line or line starting with '\t'
int spaces = 0;
while (t + spaces < length && text[t + spaces] == ' ')
spaces++;
if (spaces < script_indent_amount)
{
if (t + spaces >= length ||
text[t + spaces] == '\n' ||
text[t + spaces] == '\t')
{
// Prepend a few space characters
while (spaces < script_indent_amount)
{
*pos_ptr++ = ' ';
spaces++;
}
}
}
}
// Copy remainder of line
while (t < length && text[t] != '\n')
*pos_ptr++ = text[t++];
// Copy '\n' or '\0'
if (t == length)
{
// Text doesn't end in '\n'
*pos_ptr++ = '\n';
}
else
{
*pos_ptr++ = text[t++];
}
}
*pos_ptr = '\0';
XtFree(text);
length = pos_ptr - indented_text;
return indented_text;
}
// Read file FILE_NAME into current_source; get it from the cache if possible
int SourceView::read_current(string& file_name, bool force_reload, bool silent)
{
string requested_file_name = file_name;
if (cache_source_files && !force_reload && file_cache.has(file_name))
{
current_source = file_cache[file_name];
current_origin = origin_cache[file_name];
file_name = file_name_cache[file_name];
if (gdb->type() == JDB)
{
// In JDB, a single source may contain multiple classes.
// Store current class name FILE_NAME as source name.
source_name_cache[file_name] = requested_file_name;
}
}
else
{
long length = 0;
SourceOrigin orig;
String indented_text = read_indented(file_name, length, orig, silent);
if (indented_text == 0 || length == 0)
return -1; // Failure
current_source = string(indented_text, length);
current_origin = orig;
XtFree(indented_text);
if (current_source.length() > 0)
{
file_cache[file_name] = current_source;
origin_cache[file_name] = current_origin;
file_name_cache[file_name] = file_name;
if (file_name != requested_file_name)
{
file_cache[requested_file_name] = current_source;
origin_cache[requested_file_name] = current_origin;
file_name_cache[requested_file_name] = file_name;
}
}
int null_count = current_source.freq('\0');
if (null_count > 0 && !silent)
post_warning(file_name + ": binary file",
"source_binary_warning", source_text_w);
}
// Untabify current source, using the current tab width
untabify_if_needed(current_source, tab_width,
indent_amount(source_text_w));
// Setup global parameters
// Number of lines
line_count = current_source.freq('\n');
_pos_of_line = TextPositionArray(line_count + 2);
_pos_of_line.operator += (XmTextPosition(0));
_pos_of_line.operator += (XmTextPosition(0));
for (int i = 0; i < int(current_source.length()); i++)
if (current_source[i] == '\n')
_pos_of_line.operator += (XmTextPosition(i + 1));
assert(_pos_of_line.size() == line_count + 2);
if (current_source.length() == 0)
return -1;
else
return 0;
}
// Return position of line LINE
XmTextPosition SourceView::pos_of_line(int line)
{
if (line < 0 || line > line_count || line >= _pos_of_line.size())
return 0;
else
return _pos_of_line[line];
}
// Clear the file cache
void SourceView::clear_file_cache()
{
static StringStringAssoc string_empty;
file_cache = string_empty;
source_name_cache = string_empty;
file_name_cache = string_empty;
static StringOriginAssoc origin_empty;
origin_cache = origin_empty;
static StringArray bad_files_empty;
bad_files = bad_files_empty;
}
void SourceView::reload()
{
// Reload current file
if (current_file_name == "")
return;
string file;
if (gdb->type() == JDB)
file = line_of_cursor();
else
file = file_of_cursor();
string line = file.after(':');
file = file.before(':');
// StatusDelay delay("Reloading " + quote(file));
read_file(file, atoi(line), true);
// Restore breakpoints
refresh_bp_disp();
// Restore execution position
if (last_execution_file != "")
show_execution_position(last_execution_file + ":" +
itostring(last_execution_line),
at_lowest_frame, signal_received);
}
static const int MAX_TAB_WIDTH = 256;
// Change tab width
void SourceView::set_tab_width(int width)
{
if (width <= 0)
return;
if (tab_width != width)
{
// Make sure the tab width stays within reasonable ranges
tab_width = min(max(width, 1), MAX_TAB_WIDTH);
if (current_file_name != "")
{
StatusDelay delay("Reformatting");
reload();
}
}
}
// Change indentation
void SourceView::set_indent(int source_indent, int code_indent)
{
if (source_indent < 0 || code_indent < 0)
return;
if (source_indent == source_indent_amount &&
code_indent == code_indent_amount)
return;
if (source_indent != source_indent_amount)
{
source_indent_amount = min(max(source_indent, 0), MAX_INDENT);
if (current_file_name != "")
{
StatusDelay delay("Reformatting");
reload();
}
}
if (code_indent != code_indent_amount)
{
code_indent_amount = min(max(code_indent, 0), MAX_INDENT);
clear_code_cache();
show_pc(last_shown_pc);
}
}
void SourceView::read_file (string file_name,
int initial_line,
bool force_reload,
bool silent)
{
if (file_name == "")
return;
/*
Yves Arrouye <Yves.Arrouye@marin.fdn.fr> states:
Paths to filename are not `normalized' before being passed to
emacslient (or gnuclient). I mean that if one of your source
files was compiled as
/some/path//there/file.C
(which happens fairly often due to empty paths components in
Makefiles, for example), the file will not appear in Emacs
because the Emacs visit-file function and friends do consider
that when there is a // or a /~ in a path then everything
before the first / is garbage. The file that will be looked
for in the example is
/there/file.C
(the GUD Emacs mode exhibits the same bug ;-)). Thus // in
file pathes must be changed to / (or Emacs changed so that
only interactively-called versions of visit-file and friends
do that, but I don't think this will be done...).
*/
file_name.gsub("//", "/");
// Read in current_source
int error = read_current(file_name, force_reload, silent);
if (error)
return;
add_position_to_history(file_name, initial_line, false);
// The remainder may take some time...
Delay delay;
// Set source and initial line
XmTextSetString(source_text_w, (String)current_source);
XmTextPosition initial_pos = 0;
if (initial_line > 0 && initial_line <= line_count)
initial_pos = pos_of_line(initial_line) + indent_amount(source_text_w);
SetInsertionPosition(source_text_w, initial_pos, true);
// Set current file name
current_file_name = file_name;
// Refresh title
update_title();
// Refresh breakpoints
static IntIntArrayAssoc empty_bps;
bps_in_line = empty_bps;
static StringArray empty_addresses;
bp_addresses = empty_addresses;
refresh_bp_disp();
XtManageChild(source_text_w);
MString msg;
switch (current_origin)
{
case ORIGIN_LOCAL:
msg += rm("File " + quote(file_name));
if (remote_gdb())
msg += rm(" (from local host)");
break;
case ORIGIN_REMOTE:
msg += rm("File " + quote(file_name));
msg += rm(" (from " + gdb_host + ")");
break;
case ORIGIN_GDB:
msg += rm("Source " + quote(file_name));
msg += rm(" (from " + gdb->title() + ")");
break;
case ORIGIN_NONE:
msg += tt(file_name);
break;
}
msg += rm(" ");
if (line_count == 1)
msg += rm("1 line, ");
else
msg += rm(itostring(line_count) + " lines, ");
if (current_source.length() == 1)
msg += rm("1 character");
else
msg += rm(itostring(current_source.length()) + " characters");
set_status_mstring(msg);
XmTextClearSelection(source_text_w,
XtLastTimestampProcessed(XtDisplay(source_text_w)));
XmTextSetHighlight(source_text_w,
0, XmTextGetLastPosition(source_text_w),
XmHIGHLIGHT_NORMAL);
last_top = last_pos = last_start_highlight = last_end_highlight = 0;
update_glyphs(source_text_w);
if (app_data.source_window)
{
static bool popped_up = false;
if (!popped_up)
{
// Make sure source is visible
Widget shell = (source_view_shell != 0) ?
source_view_shell : command_shell;
if (source_view_shell != 0 || app_data.tty_mode)
{
initial_popup_shell(shell);
}
if (!app_data.command_toolbar)
initial_popup_shell(tool_shell);
if (!started_iconified(shell))
gdbOpenSourceWindowCB(source_text_w, 0, 0);
}
popped_up = true;
}
}
void SourceView::update_title()
{
if (toplevel_w == 0)
return;
string title = DDD_NAME ": " + current_file_name;
String title_s = title;
string icon =
DDD_NAME ": " + string(basename(current_file_name.chars()));
String icon_s = icon;
XtVaSetValues(toplevel_w,
XmNtitle, title_s,
XmNiconName, icon_s,
NULL);
}
//-----------------------------------------------------------------------
// Breakpoint handling
//-----------------------------------------------------------------------
// Update breakpoint locations
void SourceView::refresh_bp_disp()
{
refresh_source_bp_disp();
refresh_code_bp_disp();
update_glyphs();
}
void SourceView::refresh_source_bp_disp()
{
// Overwrite old breakpoint displays - - - - - - - - - - - - -
for (IntIntArrayAssocIter b_i_l_iter(bps_in_line);
b_i_l_iter.ok();
b_i_l_iter++)
{
int line_nr = b_i_l_iter.key();
if (line_nr < 0 || line_nr > line_count)
continue;
int pos = pos_of_line(line_nr);
int indent = indent_amount(source_text_w, pos);
if (indent > 0)
{
string s(current_source.at(pos, indent - 1));
if (s.length() > 0)
XmTextReplace(source_text_w,
pos_of_line(line_nr),
pos_of_line(line_nr) + s.length(),
(String)s);
}
}
static IntIntArrayAssoc empty_bps;
bps_in_line = empty_bps;
if (display_glyphs)
return;
// Find all breakpoints referring to this file
MapRef ref;
for (BreakPoint* bp = bp_map.first(ref); bp != 0; bp = bp_map.next(ref))
{
if (bp_matches(bp))
bps_in_line[bp->line_nr()] += bp->number();
}
// Show breakpoints in text
for (IntIntArrayAssocIter b_i_l_iter2(bps_in_line);
b_i_l_iter2.ok();
b_i_l_iter2++)
{
int line_nr = b_i_l_iter2.key();
if (line_nr < 0 || line_nr > line_count)
continue;
XmTextPosition pos = pos_of_line(line_nr);
int indent = indent_amount(source_text_w, pos);
if (indent > 0)
{
// Display all breakpoints in a line
VarIntArray& bps = bps_in_line[line_nr];
string insert_string = "";
for (int i = 0; i < bps.size(); i++)
{
BreakPoint *bp = bp_map.get(bps[i]);
insert_string += bp->symbol();
}
if (int(insert_string.length()) >= indent - 1)
{
insert_string = insert_string.before(indent - 1);
}
else
{
for (int i = insert_string.length(); i < indent - 1; i++)
{
insert_string += current_source[pos + i];
}
}
assert(int(insert_string.length()) == indent - 1);
if (insert_string.length() > 0)
XmTextReplace(source_text_w, pos,
pos + indent - 1,
(String)insert_string);
}
}
}
void SourceView::refresh_code_bp_disp()
{
// Clear all addresses
int i;
for (i = 0; i < bp_addresses.size(); i++)
{
const string& address = bp_addresses[i];
XmTextPosition pos = find_pc(address);
if (pos == XmTextPosition(-1))
continue;
// Process all breakpoints at ADDRESS
int indent = indent_amount(code_text_w, pos);
if (indent > 0)
{
string spaces = replicate(' ', indent);
XmTextReplace(code_text_w, pos, pos + indent, (String)spaces);
}
}
static StringArray empty;
bp_addresses = empty;
if (display_glyphs)
return;
// Collect all addresses
MapRef ref;
for (BreakPoint *bp = bp_map.first(ref); bp != 0;
bp = bp_map.next(ref))
{
if (bp->type() != BREAKPOINT)
continue;
bp_addresses += bp->address();
}
// Process all bp_addresses
for (i = 0; i < bp_addresses.size(); i++)
{
const string& address = bp_addresses[i];
XmTextPosition pos = find_pc(address);
if (pos == XmTextPosition(-1))
continue;
// Process all breakpoints at ADDRESS
string insert_string = "";
for (BreakPoint *bp = bp_map.first(ref);
bp != 0;
bp = bp_map.next(ref))
{
if (bp->address() == address)
insert_string += bp->symbol();
}
int indent = indent_amount(code_text_w, pos);
if (indent > 0)
{
insert_string += replicate(' ', indent);
insert_string = insert_string.before(indent);
XmTextReplace(code_text_w, pos, pos + indent,
(String)insert_string);
}
}
}
//-----------------------------------------------------------------------
// Position management
//-----------------------------------------------------------------------
// Find the line number at POS
// LINE_NR becomes the line number at POS
// IN_TEXT becomes true iff POS is in the source area
// BP_NR is the number of the breakpoint at POS (none: 0)
// Return false iff failure
bool SourceView::get_line_of_pos (Widget w,
XmTextPosition pos,
int& line_nr,
string& address,
bool& in_text,
int& bp_nr)
{
bool found = false;
line_nr = 0;
address = "";
in_text = true;
bp_nr = 0;
Widget text_w;
if (is_source_widget(w))
text_w = source_text_w;
else if (is_code_widget(w))
text_w = code_text_w;
else
return false;
if (w != text_w)
{
// Glyph selected
MapRef ref;
for (BreakPoint *bp = bp_map.first(ref);
bp != 0;
bp = bp_map.next(ref))
{
if (w == bp->source_glyph() || w == bp->code_glyph())
{
// Breakpoint glyph found
line_nr = bp->line_nr();
address = bp->address();
in_text = false;
bp_nr = bp->number();
return true;
}
}
}
if (pos >= int(current_text(text_w).length()))
{
// Position is on the right of text
in_text = false;
line_nr = line_count;
return true;
}
if (text_w == source_text_w)
{
// Search in source code
XmTextPosition line_pos = 0;
XmTextPosition next_line_pos = 0;
while (!found && line_count >= line_nr)
{
next_line_pos = (line_count >= line_nr + 1) ?
pos_of_line(line_nr + 1) :
XmTextGetLastPosition (text_w) + 1;
bool left_of_first_nonblank = false;
if (pos < next_line_pos)
{
// Check if we're left of first non-blank source character
int first_nonblank = line_pos + indent_amount(text_w);
const string& text = current_text(text_w);
while (first_nonblank < next_line_pos
&& first_nonblank < int(text.length())
&& isspace(text[first_nonblank]))
first_nonblank++;
left_of_first_nonblank = (pos < first_nonblank);
}
if (pos == line_pos
|| left_of_first_nonblank
|| pos < (line_pos + indent_amount(text_w) - 1))
{
// Position in breakpoint area
found = true;
in_text = false;
line_nr = max(line_nr, 1);
// Check for breakpoints...
VarIntArray& bps = bps_in_line[line_nr];
if (bps.size() == 1)
{
// Return single breakpoint in this line
bp_nr = bps[0];
}
else if (bps.size() > 1)
{
// Find which breakpoint was selected
XmTextPosition bp_disp_pos = line_pos;
int i;
for (i = 0; i < bps.size(); i++)
{
BreakPoint* bp = bp_map.get(bps[i]);
assert (bp);
bp_disp_pos += 2; // respect '#' and '_';
bp_disp_pos += itostring(bp->number()).length();
if (pos < bp_disp_pos)
{
bp_nr = bps[i];
break; // exit for loop
}
}
}
}
else if (pos < next_line_pos)
{
// Position is in text
found = true;
in_text = true;
}
else
{
// Position is in one of the following lines
line_pos = next_line_pos;
line_nr++;
}
}
}
else if (text_w == code_text_w)
{
// Search in machine code
XmTextPosition line_pos = pos;
while (line_pos >= 0 && current_code[line_pos] != '\n')
line_pos--;
line_pos++;
if (pos == line_pos || pos - line_pos < indent_amount(text_w))
{
// Breakpoint area
in_text = false;
// Check if we have a breakpoint around here
int index = address_index(current_code, pos);
if (index >= 0)
{
address = current_code.from(index);
address = address.through(rxaddress);
VarIntArray bps;
MapRef ref;
for (BreakPoint *bp = bp_map.first(ref);
bp != 0;
bp = bp_map.next(ref))
{
if (compare_address(address, bp->address()) == 0)
bps += bp->number();
}
if (bps.size() == 1)
{
// Return single breakpoint in this line
bp_nr = bps[0];
}
else if (bps.size() > 1)
{
// Find which breakpoint was selected
int i;
XmTextPosition bp_disp_pos = line_pos;
for (i = 0; i < bps.size(); i++)
{
BreakPoint* bp = bp_map.get(bps[i]);
assert (bp);
bp_disp_pos += 2; // respect '#' and '_';
bp_disp_pos += itostring(bp->number()).length();
if (pos < bp_disp_pos)
{
bp_nr = bps[i];
break; // exit for loop
}
}
}
}
}
found = true;
}
return found;
}
// ***************************************************************************
// Find word around POS. STARTPOS is the first character, ENDPOS + 1
// is the last character in the word.
void SourceView::find_word_bounds (Widget text_w,
const XmTextPosition pos,
XmTextPosition& startpos,
XmTextPosition& endpos)
{
startpos = endpos = pos;
string& text = current_text(text_w);
XmTextPosition line_pos = pos;
if (line_pos < XmTextPosition(text.length()))
while (line_pos > 0 && text[line_pos - 1] != '\n')
line_pos--;
int offset = pos - line_pos;
if (offset == 0 || offset < indent_amount(text_w))
{
// Do not select words in breakpoint area
return;
}
// Find end of word
while (endpos < XmTextPosition(text.length()) && isid(text[endpos]))
endpos++;
// Find start of word
if (startpos >= XmTextPosition(text.length()))
startpos = XmTextPosition(text.length() - 1);
while (startpos > 0)
{
while (startpos > 0 && isid(text[startpos - 1]))
startpos--;
if (gdb->program_language() == LANGUAGE_PERL &&
startpos > 1 &&
is_perl_prefix(text[startpos - 1]))
{
// Include Perl prefix character
startpos -= 1;
break;
}
else if (startpos > 2 &&
isid(text[startpos - 2]) &&
text[startpos - 1] == '.')
{
// Select A.B as a whole
startpos -= 1;
}
else if (startpos > 3 &&
isid(text[startpos - 3]) &&
text[startpos - 2] == '-' &&
text[startpos - 1] == '>')
{
// Select A->B as a whole
startpos -= 2;
}
else if (startpos > 3 &&
isid(text[startpos - 3]) &&
text[startpos - 2] == ':' &&
text[startpos - 1] == ':')
{
// Select A::B as a whole
startpos -= 2;
}
else
break;
}
}
// Get the word at event position
string SourceView::get_word_at_event(Widget text_w,
XEvent *event,
XmTextPosition& startpos,
XmTextPosition& endpos)
{
BoxPoint event_pos = point(event);
XmTextPosition pos = XmTextXYToPos(text_w, event_pos[X], event_pos[Y]);
return get_word_at_pos(text_w, pos, startpos, endpos);
}
// Get the word at POS
string SourceView::get_word_at_pos(Widget text_w,
XmTextPosition pos,
XmTextPosition& startpos,
XmTextPosition& endpos)
{
string& text = current_text(text_w);
if (text == "")
return "";
if (!XmTextGetSelectionPosition(text_w, &startpos, &endpos)
|| pos < startpos
|| pos > endpos)
{
find_word_bounds(text_w, pos, startpos, endpos);
}
string word = "";
if (startpos < XmTextPosition(text.length())
&& startpos < endpos)
word = text.at(int(startpos), int(endpos - startpos));
strip_space(word);
return word;
}
//----------------------------------------------------------------------------
// Create GDB requests and evaluate replies
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Constructor
//----------------------------------------------------------------------------
// Install the given X bitmap as NAME
static void InstallImage(unsigned char *bits, int width, int height,
const string& name)
{
XImage *image = CreateImageFromBitmapData(bits, width, height);
Boolean ok = XmInstallImage(image, name);
if (!ok)
cerr << "Could not install " << quote(name) << " bitmap\n";
}
SourceView::SourceView(Widget parent)
{
XtAppContext app_context = XtWidgetToApplicationContext(parent);
// Find application shell
toplevel_w = parent;
while (toplevel_w != 0 && !XtIsWMShell(toplevel_w))
toplevel_w = XtParent(toplevel_w);
// Install glyph images
InstallImage(arrow_bits, arrow_width, arrow_height,
"plain_arrow");
InstallImage(grey_arrow_bits, grey_arrow_width, grey_arrow_height,
"grey_arrow");
InstallImage(past_arrow_bits, past_arrow_width, past_arrow_height,
"past_arrow");
InstallImage(signal_arrow_bits, signal_arrow_width, signal_arrow_height,
"signal_arrow");
InstallImage(drag_arrow_bits, drag_arrow_width, drag_arrow_height,
"drag_arrow");
InstallImage(stop_bits, stop_width, stop_height,
"plain_stop");
InstallImage(cond_bits, cond_width, cond_height,
"plain_cond");
InstallImage(temp_bits, temp_width, temp_height,
"plain_temp");
InstallImage(grey_stop_bits, grey_stop_width, grey_stop_height,
"grey_stop");
InstallImage(grey_cond_bits, grey_cond_width, grey_cond_height,
"grey_cond");
InstallImage(grey_temp_bits, grey_temp_width, grey_temp_height,
"grey_temp");
InstallImage(drag_stop_bits, drag_stop_width, drag_stop_height,
"drag_stop");
InstallImage(drag_cond_bits, drag_cond_width, drag_cond_height,
"drag_cond");
InstallImage(drag_temp_bits, drag_temp_width, drag_temp_height,
"drag_temp");
// Setup actions
XtAppAddActions (app_context, actions, XtNumber (actions));
// Create source code window
create_text(parent, "source", app_data.source_editing,
source_form_w, source_text_w);
XtManageChild(source_form_w);
// Create machine code window
create_text(parent, "code", false, code_form_w, code_text_w);
if (disassemble)
XtManageChild(code_form_w);
}
void SourceView::create_shells()
{
Widget parent = XtParent(source_form_w);
XtAppContext app_context = XtWidgetToApplicationContext(parent);
// Create breakpoint editor
Arg args[10];
Cardinal arg = 0;
arg = 0;
XtSetArg(args[arg], XmNvisibleItemCount, 0); arg++;
edit_breakpoints_dialog_w =
verify(createTopLevelSelectionDialog(parent, "edit_breakpoints_dialog",
args, arg));
Delay::register_shell(edit_breakpoints_dialog_w);
XtUnmanageChild(XmSelectionBoxGetChild(edit_breakpoints_dialog_w,
XmDIALOG_TEXT));
XtUnmanageChild(XmSelectionBoxGetChild(edit_breakpoints_dialog_w,
XmDIALOG_CANCEL_BUTTON));
XtUnmanageChild(XmSelectionBoxGetChild(edit_breakpoints_dialog_w,
XmDIALOG_APPLY_BUTTON));
XtUnmanageChild(XmSelectionBoxGetChild(edit_breakpoints_dialog_w,
XmDIALOG_SELECTION_LABEL));
XtUnmanageChild(XmSelectionBoxGetChild(edit_breakpoints_dialog_w,
XmDIALOG_LIST_LABEL));
breakpoint_list_w =
XmSelectionBoxGetChild(edit_breakpoints_dialog_w, XmDIALOG_LIST);
if (app_data.flat_dialog_buttons)
{
for (MMDesc *item = bp_area; item != 0 && item->name != 0; item++)
{
if ((item->type & MMTypeMask) == MMPush)
item->type = (MMFlatPush | (item->type & ~MMTypeMask));
}
}
Widget buttons = verify(MMcreateWorkArea(edit_breakpoints_dialog_w,
"buttons", bp_area));
XtVaSetValues(buttons,
XmNmarginWidth, 0,
XmNmarginHeight, 0,
XmNborderWidth, 0,
XmNshadowThickness, 0,
XmNspacing, 0,
NULL);
MMaddCallbacks(bp_area);
MMaddHelpCallback(bp_area, ImmediateHelpCB);
if (breakpoint_list_w != 0)
{
XtAddCallback(breakpoint_list_w,
XmNsingleSelectionCallback,
UpdateBreakpointButtonsCB,
0);
XtAddCallback(breakpoint_list_w,
XmNmultipleSelectionCallback,
UpdateBreakpointButtonsCB,
0);
#if 0
XtAddCallback(breakpoint_list_w,
XmNmultipleSelectionCallback,
LookupBreakpointCB,
0);
#endif
XtAddCallback(breakpoint_list_w,
XmNextendedSelectionCallback,
UpdateBreakpointButtonsCB,
0);
XtAddCallback(breakpoint_list_w,
XmNbrowseSelectionCallback,
UpdateBreakpointButtonsCB,
0);
}
if (edit_breakpoints_dialog_w != 0)
{
XtAddCallback(edit_breakpoints_dialog_w,
XmNokCallback,
UnmanageThisCB,
edit_breakpoints_dialog_w);
XtAddCallback(edit_breakpoints_dialog_w,
XmNhelpCallback,
ImmediateHelpCB,
0);
}
// Create stack view
arg = 0;
XtSetArg(args[arg], XmNautoUnmanage, False); arg++;
stack_dialog_w =
verify(createTopLevelSelectionDialog(parent,
"stack_dialog", args, arg));
Delay::register_shell(stack_dialog_w);
XtUnmanageChild(XmSelectionBoxGetChild(stack_dialog_w,
XmDIALOG_TEXT));
XtUnmanageChild(XmSelectionBoxGetChild(stack_dialog_w,
XmDIALOG_SELECTION_LABEL));
up_w = XmSelectionBoxGetChild(stack_dialog_w, XmDIALOG_OK_BUTTON);
down_w = XmSelectionBoxGetChild(stack_dialog_w, XmDIALOG_APPLY_BUTTON);
set_sensitive(up_w, False);
set_sensitive(down_w, False);
refresh_buttons();
arg = 0;
frame_list_w = XmSelectionBoxGetChild(stack_dialog_w, XmDIALOG_LIST);
XtVaSetValues(frame_list_w,
XmNselectionPolicy, XmSINGLE_SELECT,
NULL);
XtAddCallback(frame_list_w,
XmNsingleSelectionCallback, SelectFrameCB, 0);
XtAddCallback(frame_list_w,
XmNmultipleSelectionCallback, SelectFrameCB, 0);
XtAddCallback(frame_list_w,
XmNextendedSelectionCallback, SelectFrameCB, 0);
XtAddCallback(frame_list_w,
XmNbrowseSelectionCallback, SelectFrameCB, 0);
XtAddCallback(stack_dialog_w,
XmNokCallback, gdbCommandCB, "up");
XtAddCallback(stack_dialog_w,
XmNapplyCallback, gdbCommandCB, "down");
XtAddCallback(stack_dialog_w,
XmNcancelCallback, UnmanageThisCB, stack_dialog_w);
XtAddCallback(stack_dialog_w,
XmNcancelCallback, StackDialogPoppedDownCB, 0);
XtAddCallback(stack_dialog_w,
XmNhelpCallback, ImmediateHelpCB, 0);
Widget cancel_w = XmSelectionBoxGetChild(stack_dialog_w,
XmDIALOG_CANCEL_BUTTON);
XtVaSetValues(stack_dialog_w, XmNdefaultButton, cancel_w, 0);
// Create register view
arg = 0;
XtSetArg(args[arg], XmNautoUnmanage, False); arg++;
register_dialog_w =
verify(createTopLevelSelectionDialog(parent,
"register_dialog", args, arg));
Delay::register_shell(register_dialog_w);
XtUnmanageChild(XmSelectionBoxGetChild(register_dialog_w,
XmDIALOG_TEXT));
XtUnmanageChild(XmSelectionBoxGetChild(register_dialog_w,
XmDIALOG_SELECTION_LABEL));
XtUnmanageChild(XmSelectionBoxGetChild(register_dialog_w,
XmDIALOG_APPLY_BUTTON));
XtUnmanageChild(XmSelectionBoxGetChild(register_dialog_w,
XmDIALOG_CANCEL_BUTTON));
arg = 0;
Widget box = XmCreateRadioBox(register_dialog_w, "box", args, arg);
XtManageChild(box);
arg = 0;
XtSetArg(args[arg], XmNset, !all_registers); arg++;
int_registers_w =
XmCreateToggleButton(box, "int_registers", args, arg);
XtManageChild(int_registers_w);
arg = 0;
XtSetArg(args[arg], XmNset, all_registers); arg++;
all_registers_w =
XmCreateToggleButton(box, "all_registers", args, arg);
XtManageChild(all_registers_w);
XtAddCallback(all_registers_w, XmNvalueChangedCallback,
sourceToggleAllRegistersCB, XtPointer(0));
arg = 0;
register_list_w = XmSelectionBoxGetChild(register_dialog_w, XmDIALOG_LIST);
XtVaSetValues(register_list_w,
XmNselectionPolicy, XmSINGLE_SELECT,
NULL);
XtAddCallback(register_list_w,
XmNsingleSelectionCallback, SelectRegisterCB, 0);
XtAddCallback(register_list_w,
XmNmultipleSelectionCallback, SelectRegisterCB, 0);
XtAddCallback(register_list_w,
XmNextendedSelectionCallback, SelectRegisterCB, 0);
XtAddCallback(register_list_w,
XmNbrowseSelectionCallback, SelectRegisterCB, 0);
XtAddCallback(register_dialog_w,
XmNokCallback, UnmanageThisCB, register_dialog_w);
XtAddCallback(register_dialog_w,
XmNokCallback, RegisterDialogPoppedDownCB, 0);
XtAddCallback(register_dialog_w,
XmNhelpCallback, ImmediateHelpCB, 0);
// Create thread view
arg = 0;
XtSetArg(args[arg], XmNautoUnmanage, False); arg++;
thread_dialog_w =
verify(createTopLevelSelectionDialog(parent,
"thread_dialog", args, arg));
Delay::register_shell(thread_dialog_w);
XtUnmanageChild(XmSelectionBoxGetChild(thread_dialog_w,
XmDIALOG_TEXT));
XtUnmanageChild(XmSelectionBoxGetChild(thread_dialog_w,
XmDIALOG_SELECTION_LABEL));
if (gdb->type() != JDB)
{
XtUnmanageChild(XmSelectionBoxGetChild(thread_dialog_w,
XmDIALOG_OK_BUTTON));
XtUnmanageChild(XmSelectionBoxGetChild(thread_dialog_w,
XmDIALOG_APPLY_BUTTON));
}
arg = 0;
thread_list_w = XmSelectionBoxGetChild(thread_dialog_w, XmDIALOG_LIST);
XtVaSetValues(thread_list_w,
XmNselectionPolicy, XmSINGLE_SELECT,
NULL);
XtAddCallback(thread_list_w,
XmNsingleSelectionCallback, SelectThreadCB, 0);
XtAddCallback(thread_list_w,
XmNmultipleSelectionCallback, SelectThreadCB, 0);
XtAddCallback(thread_list_w,
XmNextendedSelectionCallback, SelectThreadCB, 0);
XtAddCallback(thread_list_w,
XmNbrowseSelectionCallback, SelectThreadCB, 0);
XtAddCallback(thread_dialog_w,
XmNcancelCallback, UnmanageThisCB, thread_dialog_w);
XtAddCallback(thread_dialog_w,
XmNcancelCallback, ThreadDialogPoppedDownCB, 0);
XtAddCallback(thread_dialog_w,
XmNokCallback, ThreadCommandCB, "suspend");
XtAddCallback(thread_dialog_w,
XmNapplyCallback, ThreadCommandCB, "resume");
XtAddCallback(thread_dialog_w,
XmNhelpCallback, ImmediateHelpCB, 0);
// Create remaining glyphs in the background
XtAppAddWorkProc (app_context, CreateGlyphsWorkProc, XtPointer(0));
}
// Check for modifications
void SourceView::CheckModificationCB(Widget, XtPointer client_data,
XtPointer call_data)
{
bool editable = bool((int)(long)client_data);
XmTextVerifyCallbackStruct *cbs = (XmTextVerifyCallbackStruct *)call_data;
if (!editable && cbs != 0 && cbs->event != 0)
{
cbs->doit = False;
return;
}
// Follow text modifications here... (FIXME)
}
// Create source or code window
void SourceView::create_text(Widget parent, const string& base, bool editable,
Widget& form, Widget& text)
{
Arg args[15];
int arg = 0;
// Create text window
arg = 0;
XtSetArg(args[arg], XmNmarginHeight, 0); arg++;
XtSetArg(args[arg], XmNmarginWidth, 0); arg++;
string form_name = base + "_form_w";
form = verify(XmCreateForm(parent, form_name, args, arg));
arg = 0;
XtSetArg(args[arg], XmNselectionArrayCount, 1); arg++;
XtSetArg(args[arg], XmNtopAttachment, XmATTACH_FORM); arg++;
XtSetArg(args[arg], XmNbottomAttachment, XmATTACH_FORM); arg++;
XtSetArg(args[arg], XmNleftAttachment, XmATTACH_FORM); arg++;
XtSetArg(args[arg], XmNrightAttachment, XmATTACH_FORM); arg++;
XtSetArg(args[arg], XmNallowResize, True); arg++;
XtSetArg(args[arg], XmNeditMode, XmMULTI_LINE_EDIT); arg++;
XtSetArg(args[arg], XmNcursorPositionVisible, True); arg++;
if (lesstif_version <= 82)
{
// LessTif 0.81 has a bad implementation of auto-show
// position: rather than scrolling only when needed, the line
// containing the cursor is *always* scrolled such that it
// becomes the first line. Hence, disable the LessTif
// auto-show mechanism and rely on the DDD ones.
XtSetArg(args[arg], XmNautoShowCursorPosition, False); arg++;
}
else
{
XtSetArg(args[arg], XmNautoShowCursorPosition, True); arg++;
}
if (lesstif_version <= 86)
{
// LessTif 0.86 and earlier has trouble with non-editable text
// windows: cursor movement is inhibited. Rely on
// `CheckModificationCB' instead.
XtSetArg(args[arg], XmNeditable, True); arg++;
}
else
{
XtSetArg(args[arg], XmNeditable, editable); arg++;
}
string text_name = base + "_text_w";
text = verify(XmCreateScrolledText(form, text_name, args, arg));
XtManageChild(text);
// Set up the scrolled window
XtVaSetValues(XtParent(text),
XmNspacing, 0,
XmNborderWidth, 0,
XmNshadowThickness, 0,
NULL);
// Give the form the size specified for the text
set_scrolled_window_size(text, form);
// Set callbacks
XtAddCallback(text, XmNgainPrimaryCallback,
set_source_argCB, XtPointer(false));
XtAddCallback(text, XmNmotionVerifyCallback,
set_source_argCB, XtPointer(true));
XtAddCallback(text, XmNmotionVerifyCallback,
CheckScrollCB, XtPointer(0));
XtAddCallback(text, XmNmodifyVerifyCallback,
CheckModificationCB, XtPointer(editable));
InstallTextTips(text);
// Fetch scrollbar ID and add callbacks
Widget scrollbar = 0;
XtVaGetValues(XtParent(text), XmNverticalScrollBar, &scrollbar, NULL);
if (scrollbar != 0)
{
XtAddCallback(scrollbar, XmNincrementCallback, CheckScrollCB, 0);
XtAddCallback(scrollbar, XmNdecrementCallback, CheckScrollCB, 0);
XtAddCallback(scrollbar, XmNpageIncrementCallback, CheckScrollCB, 0);
XtAddCallback(scrollbar, XmNpageDecrementCallback, CheckScrollCB, 0);
XtAddCallback(scrollbar, XmNtoTopCallback, CheckScrollCB, 0);
XtAddCallback(scrollbar, XmNtoBottomCallback, CheckScrollCB, 0);
XtAddCallback(scrollbar, XmNdragCallback, CheckScrollCB, 0);
XtAddCallback(scrollbar, XmNvalueChangedCallback, CheckScrollCB, 0);
}
}
//-----------------------------------------------------------------------
// Position management
//-----------------------------------------------------------------------
// Set current execution position, based on the GDB position info
// POSITION; no arg means clear current position.
// STOPPED indicates that the program just stopped.
// SIGNALED indicates that the program received a signal.
void SourceView::show_execution_position (string position, bool stopped,
bool signaled, bool silent)
{
if (stopped)
{
at_lowest_frame = true;
signal_received = signaled;
}
if (position == "")
{
if (!display_glyphs)
{
// Remove old marker
int indent = indent_amount(source_text_w);
if (indent > 0)
{
static const string no_marker = " ";
XmTextReplace (source_text_w,
last_pos + indent - no_marker.length(),
last_pos + indent,
(String)no_marker);
}
if (last_start_highlight)
XmTextSetHighlight (source_text_w,
last_start_highlight, last_end_highlight,
XmHIGHLIGHT_NORMAL);
}
last_pos = last_start_highlight = last_end_highlight = 0;
last_execution_file = "";
last_execution_line = 0;
update_glyphs();
undo_buffer.remove_position();
undo_buffer.add_state();
return;
}
string file_name = current_file_name;
if (position.contains(':'))
{
file_name = position.before(":");
position = position.after(":");
}
int line = get_positive_nr(position);
if (line < 0)
return;
if (!is_current_file(file_name))
read_file(file_name, line, silent);
if (is_current_file(file_name))
{
int indent = indent_amount(source_text_w);
if (!display_glyphs && indent > 0)
{
// Remove old marker
static const string no_marker = " ";
XmTextReplace (source_text_w,
last_pos + indent - no_marker.length(),
last_pos + indent,
(String)no_marker);
}
// Show current position
_show_execution_position(file_name, line, silent, stopped);
}
}
// Unset current execution position (program terminated)
void SourceView::clear_execution_position()
{
show_execution_position();
last_execution_pc = "";
last_shown_pc = "";
update_glyphs();
undo_buffer.remove_address();
undo_buffer.add_state();
}
void SourceView::_show_execution_position(string file, int line,
bool silent, bool stopped)
{
last_execution_file = file;
last_execution_line = line;
if (!is_current_file(file))
read_file(file, line, silent);
if (!is_current_file(file) || line < 1 || line > line_count)
return;
add_position_to_history(file, line, stopped);
XmTextPosition pos = pos_of_line(line);
int indent = indent_amount(source_text_w);
SetInsertionPosition(source_text_w, pos + indent, false);
// Mark current line
if (!display_glyphs && indent > 0)
{
// Set new marker
static const string marker = ">";
XmTextReplace (source_text_w,
pos + indent - marker.length(),
pos + indent,
(String)marker);
}
XmTextPosition pos_line_end = 0;
if (current_source != "")
pos_line_end = current_source.index('\n', pos) + 1;
if (!display_glyphs &&
(pos != last_start_highlight || pos_line_end != last_end_highlight))
{
if (last_start_highlight)
{
XmTextSetHighlight (source_text_w,
last_start_highlight, last_end_highlight,
XmHIGHLIGHT_NORMAL);
}
XmTextSetHighlight (source_text_w,
pos, pos_line_end,
XmHIGHLIGHT_SELECTED);
}
last_pos = pos;
last_start_highlight = pos;
last_end_highlight = pos_line_end;
update_glyphs();
}
void SourceView::show_position(string position, bool silent)
{
string file_name = current_file_name;
if (position.contains(':'))
{
file_name = position.before(':');
position = position.after(':');
}
int line = get_positive_nr(position);
// In case of `Open Source', we get FILE:1 positions. Be sure to
// reload the file in this case.
bool force_reload = (line == 1);
if (!is_current_file(file_name) || force_reload)
read_file(file_name, line, force_reload, silent);
// Have window scroll to correct position
if (is_current_file(file_name))
{
if (line == 0 && gdb->type() == JDB)
{
// Scroll to current class
int pos = java_class_start(current_source, current_source_name());
if (pos >= 0)
{
int line_nr = 0;
bool in_text;
int bp_nr;
string address;
if (get_line_of_pos(source_text_w, pos, line_nr, address,
in_text, bp_nr))
{
line = line_nr;
}
}
}
if (line > 0 && line <= line_count)
{
add_position_to_history(file_name, line, false);
XmTextPosition pos = pos_of_line(line);
int indent = indent_amount(source_text_w, pos);
SetInsertionPosition(source_text_w, pos + indent, true);
last_pos = pos;
}
}
}
//-----------------------------------------------------------------------
// Process GDB output
//-----------------------------------------------------------------------
// Process reply on 'info breakpoints'.
// Update breakpoints in BP_BAP, adding new ones or deleting existing ones.
// Update breakpoint display by calling REFRESH_BP_DISP.
void SourceView::process_info_bp (string& info_output,
const string& break_arg)
{
// DEC DBX issues empty lines, which causes trouble
info_output.gsub("\n\n", "\n");
// SGI DBX issues `Process PID' before numbers
#if RUNTIME_REGEX
static regex rxprocess1("Process[ \t]+[0-9]+:[ \t]*");
#endif
info_output.gsub(rxprocess1, "");
last_info_output = info_output;
string keep_me = "";
switch (gdb->type())
{
case GDB:
// If this is no breakpoint info, process it as GDB message
if (!info_output.contains("Num", 0) &&
!info_output.contains("No breakpoints", 0))
check_remainder(info_output);
break;
case DBX:
case XDB:
case JDB:
case PYDB:
case PERL:
break;
}
VarIntArray bps_not_read;
MapRef ref;
int i;
for (i = bp_map.first_key(ref); i != 0; i = bp_map.next_key(ref))
bps_not_read += i;
bool changed = false;
bool added = false;
ostrstream undo_commands;
string file = current_file_name;
while (info_output != "")
{
int bp_nr = -1;
switch(gdb->type())
{
case GDB:
case PYDB:
if (!has_nr(info_output))
{
// Skip this line
info_output = info_output.after('\n');
continue;
}
bp_nr = get_positive_nr (info_output);
break;
case DBX:
{
// SGI IRIX DBX issues `Process PID: '
// before status lines.
#if RUNTIME_REGEX
static regex rxprocess2("Process[ \t]+[0-9]+:");
#endif
if (info_output.contains(rxprocess2, 0))
info_output = info_output.after(':');
strip_leading_space(info_output);
if (!info_output.contains('(', 0)
&& !info_output.contains('[', 0))
{
// No breakpoint info - skip this line
info_output = info_output.after('\n');
continue;
}
string bp_nr_s = info_output.after(0);
bp_nr = get_positive_nr (bp_nr_s);
}
break;
case XDB:
bp_nr = get_positive_nr(info_output);
break;
case JDB:
case PERL:
{
// JDB and Perl have no breakpoint numbers.
// Check if we already have a breakpoint at this location.
bp_nr = breakpoint_number(info_output);
if (bp_nr == 0)
bp_nr = max_breakpoint_number_seen + 1; // new breakpoint
if (bp_nr < 0)
{
// Not a breakpoint
string line = info_output.before('\n');
if (!line.contains("Current breakpoints set"))
keep_me += line;
// Skip this line
info_output = info_output.after('\n');
continue;
}
break;
}
}
if (bp_nr <= 0)
{
info_output = info_output.after('\n');
continue;
}
if (bp_map.contains (bp_nr))
{
// Update existing breakpoint
bps_not_read -= bp_nr;
BreakPoint *bp = bp_map.get(bp_nr);
ostrstream old_state;
undo_buffer.add_breakpoint_state(old_state, bp);
ostrstream local_commands;
bool need_total_undo = false;
bool bp_changed =
bp->update(info_output, local_commands, need_total_undo);
if (bp_changed)
{
if (bp->position_changed() || bp->enabled_changed())
{
changed = true;
}
if (need_total_undo)
{
// To undo this change, we must delete the old
// breakpoint and create a new one.
undo_commands << delete_command(bp->number()) << "\n"
<< string(old_state);
}
else
{
// A simple command suffices to undo this change.
undo_commands << string(local_commands);
}
}
}
else
{
// New breakpoint
changed = true;
BreakPoint *new_bp =
new BreakPoint(info_output, break_arg, bp_nr, file);
bp_map.insert(bp_nr, new_bp);
if (gdb->has_delete_command())
{
string num = "@" + itostring(bp_nr) + "@";
undo_commands << gdb->delete_command(num) << '\n';
}
else
{
undo_commands << delete_command(bp_nr) << '\n';
}
if (!added)
{
added = true;
// Select this breakpoint only
MapRef ref;
for (BreakPoint* bp = bp_map.first(ref);
bp != 0;
bp = bp_map.next(ref))
{
bp->selected() = false;
}
}
new_bp->selected() = true;
}
max_breakpoint_number_seen = max(max_breakpoint_number_seen, bp_nr);
}
// Keep this stuff for further processing
info_output = keep_me;
// Delete all breakpoints not found now
for (i = 0; i < bps_not_read.size(); i++)
{
BreakPoint *bp = bp_map.get(bps_not_read[i]);
// Perl only lists breakpoints in the current file
if (gdb->type() == PERL && !bp_matches(bp, current_file_name))
continue;
// Delete it
undo_buffer.add_breakpoint_state(undo_commands, bp);
delete bp;
bp_map.del(bps_not_read[i]);
changed = true;
}
if (changed)
refresh_bp_disp();
// Set up breakpoint editor contents
process_breakpoints(last_info_output);
undo_buffer.add_command(string(undo_commands));
// Set up existing panels
update_properties_panels();
}
int SourceView::next_breakpoint_number()
{
return max_breakpoint_number_seen + 1;
}
// Process GDB `info line main' output
void SourceView::process_info_line_main(string& info_output)
{
clear_file_cache();
clear_code_cache();
clear_dbx_lookup_cache();
if (info_output == "")
return;
current_file_name = "";
switch (gdb->type())
{
case GDB:
case XDB:
case JDB:
case PYDB:
case PERL:
{
PosBuffer pos_buffer;
pos_buffer.filter(info_output);
pos_buffer.answer_ended();
if (pos_buffer.pos_found())
show_position(pos_buffer.get_position());
if (pos_buffer.pc_found())
show_pc(pos_buffer.get_pc());
if (!pos_buffer.pos_found() && !pos_buffer.pc_found())
add_current_to_history();
}
break;
case DBX:
{
show_position(info_output);
info_output = "";
}
break;
}
// Strip 'Line <n> of <file> starts at <address>...' info
// Strip 'No symbol table is loaded.' info
String strips[] = {"Line ", "No symbol table is loaded."};
for (int i = 0; i < int(XtNumber(strips)); i++)
{
int line = info_output.index(strips[i]);
if (line >= 0)
{
int end_line = info_output.index('\n', line);
if (end_line >= 0)
info_output(line, end_line - line) = "";
}
}
check_remainder(info_output);
}
void SourceView::check_remainder(string& info_output)
{
// Any remaining input is an informative GDB message.
// Strip newlines around the message and post it.
while (info_output.length() > 0 &&
info_output[0] == '\n')
info_output = info_output.after(0);
while (info_output.length() > 0 &&
info_output[info_output.length() - 1] == '\n')
info_output = info_output.before(int(info_output.length() - 1));
post_gdb_message(info_output, true, source_text_w);
}
//-----------------------------------------------------------------------
// Locate position
//-----------------------------------------------------------------------
void SourceView::lookup(string s, bool silent)
{
if (s != "" && isspace(s[0]))
s = s.after(rxwhite);
undo_buffer.set_source("lookup");
if (s == "")
{
// Empty argument given
if (last_execution_pc != "")
{
// Show last PC
show_pc(last_execution_pc, XmHIGHLIGHT_SELECTED);
}
else
{
// Show cursor position
SetInsertionPosition(code_text_w,
XmTextGetInsertionPosition(code_text_w));
}
if (last_execution_file != "")
{
// Show last execution position
_show_execution_position(last_execution_file,
last_execution_line,
silent, false);
}
else
{
// Show cursor position
SetInsertionPosition(source_text_w,
XmTextGetInsertionPosition(source_text_w));
}
}
else if (is_file_pos(s))
{
// FILE:LINE given
add_current_to_history();
if (gdb->type() == GDB)
{
Command c("list " + s);
c.verbose = !silent;
c.echo = !silent;
c.prompt = !silent;
gdb_command(c);
}
else
{
show_position(s);
}
}
else if (s[0] != '0' && isdigit(s[0]))
{
// Line number given
int line = atoi(s);
if (line > 0 && line <= line_count)
{
add_current_to_history();
switch (gdb->type())
{
case GDB:
{
Command c("list " + current_source_name() + ":" +
itostring(line));
c.verbose = !silent;
c.echo = !silent;
c.prompt = !silent;
gdb_command(c);
break;
}
case JDB:
show_position(current_source_name() + ":" + itostring(line));
break;
case DBX:
case XDB:
case PYDB:
case PERL:
show_position(full_path(current_file_name)
+ ":" + itostring(line));
break;
}
}
else
{
if (!silent)
post_error("No line "
+ itostring(line) + " in current source.",
"no_such_line_error", source_text_w);
}
}
else if (s[0] == '#')
{
// Breakpoint given
string nr_str = s.after('#');
int nr = get_positive_nr(nr_str);
if (nr >= 0)
{
MapRef ref;
BreakPoint *bp;
for (bp = bp_map.first(ref); bp != 0; bp = bp_map.next(ref))
{
if (nr == bp->number())
{
add_current_to_history();
show_position(bp->pos());
show_pc(bp->address());
break;
}
}
if (bp == 0 && !silent)
post_error("No breakpoint number " + itostring(nr) + ".",
"no_such_breakpoint_error", source_text_w);
}
}
else
{
// Function or *address given
add_current_to_history();
switch (gdb->type())
{
case GDB:
case PYDB:
{
if (s[0] == '0') // Address given
s.prepend("*");
if (gdb->has_quotes())
{
if (s.length() > 0 && s[0] != '\'' && s[0] != '*')
s = string('\'') + s + '\'';
}
Command c("list " + s);
c.verbose = !silent;
c.echo = !silent;
c.prompt = !silent;
gdb_command(c);
break;
}
case PERL:
{
Command c("l " + s);
c.verbose = !silent;
c.echo = !silent;
c.prompt = !silent;
gdb_command(c);
break;
}
case DBX:
case JDB:
{
string pos = dbx_lookup(s, silent);
if (pos != "")
show_position(pos);
break;
}
case XDB:
{
Command c("v " + s);
c.verbose = !silent;
c.echo = !silent;
c.prompt = !silent;
gdb_command(c);
break;
}
}
}
}
//-----------------------------------------------------------------------
// Position history
//-----------------------------------------------------------------------
// Add current position to history
void SourceView::add_current_to_history()
{
XmTextPosition pos;
int line_nr;
bool in_text;
int bp_nr;
string address;
bool pos_found;
// Get position in source code
pos = XmTextGetInsertionPosition(source_text_w);
pos_found = get_line_of_pos(source_text_w, pos, line_nr, address,
in_text, bp_nr);
if (pos_found)
add_position_to_history(current_source_name(), line_nr, false);
// Get position in machine code
pos = XmTextGetInsertionPosition(code_text_w);
pos_found = get_line_of_pos(code_text_w, pos, line_nr, address,
in_text, bp_nr);
if (pos_found && address != "")
undo_buffer.add_address(address, false);
}
// Add position to history
void SourceView::add_position_to_history(const string& file_name, int line,
bool stopped)
{
string source_name = file_name;
switch (gdb->type())
{
case GDB:
case JDB:
case PYDB:
// Use source names instead.
if (source_name_cache.has(file_name))
source_name = source_name_cache[file_name];
break;
case DBX:
case XDB:
case PERL:
break;
}
undo_buffer.add_position(source_name, line, stopped);
undo_buffer.add_state();
}
// Lookup entry from position history
void SourceView::goto_entry(const string& file_name, int line,
const string& address, bool exec_pos)
{
#if 0
// Show position in status line
string msg = "";
if (file_name != "")
msg = "File " + quote(file_name);
if (line != 0)
{
if (msg == "")
msg = "Line ";
else
msg += ", line ";
msg += itostring(line);
}
if (address != "")
{
if (msg == "")
msg = "Address ";
else
msg += ", address ";
msg += address;
}
set_status(msg);
#endif
if (file_name != "")
{
// Lookup source
if (!is_current_file(file_name))
{
read_file(file_name, line);
}
if (is_current_file(file_name) && line > 0 && line <= line_count)
{
if (exec_pos)
{
_show_execution_position(file_name, line, true, true);
}
else
{
XmTextPosition pos = pos_of_line(line);
int indent = indent_amount(source_text_w, pos);
SetInsertionPosition(source_text_w, pos + indent, true);
}
}
}
if (address != "")
{
// Lookup address
show_pc(address,
(exec_pos || address == last_execution_pc) ?
XmHIGHLIGHT_SELECTED : XmHIGHLIGHT_NORMAL);
}
}
//-----------------------------------------------------------------------
// Process current working directory
//-----------------------------------------------------------------------
void SourceView::process_pwd(string& pwd_output)
{
strip_space(pwd_output);
while (pwd_output != "")
{
string pwd;
if (pwd_output.contains('\n'))
{
pwd = pwd_output.before('\n');
pwd_output = pwd_output.after('\n');
}
else
{
pwd = pwd_output;
pwd_output = "";
}
switch (gdb->type())
{
case GDB: // 'Working directory PATH.'
case PYDB:
if (pwd.contains("Working directory", 0))
{
pwd = pwd.before('.', -1);
pwd = pwd.after(' ', -1);
}
// FALL THROUGH
case XDB:
case DBX: // 'PATH'
case JDB:
case PERL:
if (pwd.contains('/', 0) && !pwd.contains(" "))
{
current_pwd = pwd;
process_cd(current_pwd);
return;
}
break;
}
}
}
//-----------------------------------------------------------------------
// Process current use path
//-----------------------------------------------------------------------
void SourceView::process_use(string& use_output)
{
strip_space(use_output);
current_class_path = use_output;
clear_file_cache();
reload();
}
//-----------------------------------------------------------------------
// Searching
//-----------------------------------------------------------------------
void SourceView::find(const string& s,
SourceView::SearchDirection direction,
bool words_only,
bool case_sensitive,
Time time)
{
int matchlen = s.length();
int pos = -1;
XmTextPosition cursor = XmTextGetInsertionPosition(source_text_w);
XmTextPosition initial_cursor = cursor;
int wraps = 0;
if (!have_source())
{
post_error("No source.", "no_source_error", source_text_w);
return;
}
string key = s;
string text = current_source;
if (!case_sensitive)
{
// FIXME: This should be done according to the current locale
key.downcase();
text.downcase();
}
// Make sure we don't re-find the currently found word
XmTextPosition startpos;
XmTextPosition endpos;
if (XmTextGetSelectionPosition(source_text_w, &startpos, &endpos))
{
switch (direction)
{
case forward:
if (cursor == startpos
&& cursor < XmTextPosition(text.length()))
cursor++;
break;
case backward:
if (cursor == endpos && cursor > 0)
cursor--;
break;
}
}
// Go and find the word
for (;;) {
switch (direction)
{
case forward:
pos = text.index(key, cursor);
if (pos < 0)
{
if (wraps++)
break;
pos = text.index(key, 0);
}
break;
case backward:
pos = text.index(key, cursor - text.length() - 1);
if (pos < 0)
{
if (wraps++)
break;
pos = text.index(key, -1);
}
break;
}
if (pos < 0)
break; // String not found or double wrap
// Set the cursor to the appropriate position
switch (direction)
{
case forward:
cursor = pos + matchlen;
break;
case backward:
cursor = pos;
break;
}
if (words_only)
{
// Make sure the occurrence is not part of a larger word
if (pos > 0 && pos < int(text.length()))
{
if (isid(text[pos]) && isid(text[pos - 1]))
continue;
}
if (pos + matchlen < int(text.length()))
{
if (isid(text[pos + matchlen - 1]) &&
isid(text[pos + matchlen]))
continue;
}
}
// We got it!
break;
}
if (pos > 0)
{
string msg;
// Highlight occurrence
XmTextSetSelection(source_text_w, pos, pos + matchlen, time);
// Set position
SetInsertionPosition(source_text_w, cursor, false);
if (cursor == initial_cursor)
{
// Unchanged position
msg = "No other occurrences of " + quote(s) + ".";
}
else
{
int line_nr;
bool in_text;
int bp_nr;
string address;
if (!get_line_of_pos(source_text_w, pos, line_nr,
address, in_text, bp_nr))
line_nr = line_count;
string occurrence = current_source(pos, matchlen);
msg = "Found " + quote(occurrence) + " in " + line_of_cursor();
if (wraps)
msg += " (wrapped)";
}
set_status(msg);
}
else
{
post_warning(quote(s) + " not found.", "source_find_error",
source_text_w);
}
}
//-----------------------------------------------------------------------
// Return source name
//-----------------------------------------------------------------------
string SourceView::current_source_name()
{
string source = "";
switch (gdb->type())
{
case GDB:
// GDB internally recognizes only `source names', i.e., the
// source file name as compiled into the executable.
if (source_name_cache[current_file_name] == "")
{
// Try the current source.
string ans = gdb_question("info source");
if (ans != NO_GDB_ANSWER)
{
ans = ans.before('\n');
ans = ans.after(' ', -1);
if (base_matches(ans, current_file_name))
{
// For security, we request that source and current
// file have the same basename.
source_name_cache[current_file_name] = ans;
}
else
{
// The current source does not match the current file.
// Try all sources.
static string all_sources = "<ALL SOURCES>";
if (source_name_cache[all_sources] == "")
{
StringArray sources;
get_gdb_sources(sources);
if (sources.size() > 0)
{
ans = "";
for (int i = 0; i < sources.size(); i++)
ans += sources[i] + '\n';
source_name_cache[all_sources] = ans;
}
}
ans = source_name_cache[all_sources];
if (ans != "")
{
int n = ans.freq('\n');
string *sources = new string[n + 1];
split(ans, sources, n + 1, '\n');
for (int i = 0; i < n + 1; i++)
{
if (base_matches(sources[i], current_file_name))
{
const string& src = sources[i];
source_name_cache[current_file_name] = src;
break;
}
}
delete[] sources;
if (source_name_cache[current_file_name] == "")
{
// No such source text. Store the base name
// such that GDB is not asked again.
string base = basename(current_file_name.chars());
source_name_cache[current_file_name] = base;
}
}
}
}
}
source = source_name_cache[current_file_name];
break;
case DBX:
case XDB:
case PYDB:
case PERL:
if (app_data.use_source_path)
{
// These debuggers use full file names.
source = full_path(current_file_name);
}
break;
case JDB:
if (source_name_cache.has(current_file_name))
{
// Use the source name as stored by read_class()
source = source_name_cache[current_file_name];
}
if (source == "")
{
source = basename(current_file_name.chars());
strip_java_suffix(source);
}
break;
}
// In case this does not work, use the current base name.
if (source == "")
source = basename(current_file_name.chars());
return source;
}
string SourceView::line_of_cursor()
{
XmTextPosition pos = XmTextGetInsertionPosition(source_text_w);
string s = current_source_name();
if (s == "")
return ""; // No source
int line_nr;
bool in_text;
int bp_nr;
string address;
if (get_line_of_pos(source_text_w, pos, line_nr, address, in_text, bp_nr))
return s + ":" + itostring(line_nr); // Cursor within source
else
return s + ":" + itostring(line_count); // Cursor in last line
}
string SourceView::file_of_cursor()
{
string pos = line_of_cursor();
return full_path(current_file_name) + pos.from(':');
}
//-----------------------------------------------------------------------------
// Handle mouse selections
//----------------------------------------------------------------------------
void SourceView::setSelection(XtPointer client_data, XtIntervalId *)
{
Widget w = (Widget)client_data;
assert(XmIsText(w));
XmTextSetSelection(w, selection_startpos, selection_endpos,
selection_time);
selection_time = 0;
set_source_argCB(w, XtPointer(false), 0);
}
void SourceView::startSelectWordAct (Widget text_w, XEvent* e,
String *params, Cardinal *num_params)
{
#if XtSpecificationRelease < 6
selection_event = *e;
#endif
XtCallActionProc(text_w, "grab-focus", e, params, *num_params);
if (e->type != ButtonPress && e->type != ButtonRelease)
return;
XButtonEvent *event = &e->xbutton;
XmTextPosition pos = XmTextXYToPos (text_w, event->x, event->y);
XmTextPosition startpos, endpos;
if (app_data.source_editing)
startpos = endpos = pos;
else
find_word_bounds(text_w, pos, startpos, endpos);
selection_click = true;
selection_startpos = startpos;
selection_endpos = endpos;
selection_time = time(e);
XtAppAddTimeOut(XtWidgetToApplicationContext(text_w), 0, setSelection,
(XtPointer)text_w);
}
void SourceView::endSelectWordAct (Widget text_w, XEvent* e,
String *params, Cardinal *num_params)
{
#if XtSpecificationRelease < 6
selection_event = *e;
#endif
selection_click = false;
XtCallActionProc(text_w, "extend-end", e, params, *num_params);
if (e->type != ButtonPress && e->type != ButtonRelease)
return;
XmTextPosition startpos, endpos;
if (XmTextGetSelectionPosition(text_w, &startpos, &endpos))
{
selection_startpos = startpos;
selection_endpos = endpos;
}
selection_time = time(e);
XtAppAddTimeOut(XtWidgetToApplicationContext(text_w), 0, setSelection,
(XtPointer)text_w);
}
//-----------------------------------------------------------------------------
// Handle source popup
//----------------------------------------------------------------------------
void SourceView::set_text_popup_label(int item, const string& arg, bool sens)
{
Widget w = text_popup[item].widget;
MString label = MString(text_cmd_labels[item]) + tt(arg);
XtVaSetValues(w, XmNlabelString, label.xmstring(), NULL);
set_sensitive(w, sens);
}
void SourceView::set_text_popup_resource(int item, const string& arg)
{
if (lesstif_version <= 82)
{
// Set up resources for yet-to-be-created popup menu
string db = string(DDD_CLASS_NAME "*text_popup.")
+ text_popup[item].name + "." + XmNlabelString + ": "
+ "@" CHARSET_RM " " + text_cmd_labels[item]
+ " @" CHARSET_TT " " + arg;
XrmDatabase res = XrmGetStringDatabase(db.chars());
XrmDatabase target = XtDatabase(XtDisplay(source_text_w));
XrmMergeDatabases(res, &target);
}
}
// Get relative coordinates of GLYPH in TEXT
void SourceView::translate_glyph_pos(Widget glyph, Widget text, int& x, int& y)
{
int dest_x, dest_y;
Window child;
XTranslateCoordinates(XtDisplay(glyph),
XtWindow(glyph), XtWindow(text),
x, y, &dest_x, &dest_y, &child);
x = dest_x;
y = dest_y;
}
// Popup button3 source menu
void SourceView::srcpopupAct (Widget w, XEvent* e, String *, Cardinal *)
{
if (e->type != ButtonPress && e->type != ButtonRelease)
return;
Widget text_w;
if (is_source_widget(w))
text_w = source_text_w;
else if (is_code_widget(w))
text_w = code_text_w;
else
return;
XButtonEvent* event = &e->xbutton;
int x = event->x;
int y = event->y;
if (w != source_text_w && w != code_text_w)
{
// Called from a glyph: translate glyph position to text position
translate_glyph_pos(w, text_w, x, y);
}
// Get the position
XmTextPosition pos = XmTextXYToPos(text_w, x, y);
// Move the insertion cursor to this position, but don't disturb the
// selection
XmTextPosition left, right;
Boolean have_selection = XmTextGetSelectionPosition(text_w, &left, &right);
if (have_selection && pos >= left && pos <= right)
{
// Do not scroll here. Do not use SetInsertionPosition().
XmTextSetInsertionPosition(text_w, pos);
XmTextSetSelection(text_w, left, right, time(e));
}
else
{
// Do not scroll here. Do not use SetInsertionPosition().
XmTextClearSelection(text_w, time(e));
XmTextSetInsertionPosition(text_w, pos);
}
int line_nr;
bool in_text;
static int bp_nr;
static string address;
bool pos_found = get_line_of_pos(w, pos, line_nr, address, in_text, bp_nr);
bool right_of_text =
pos < XmTextPosition(current_text(w).length())
&& current_text(w)[pos] == '\n';
if (pos_found && bp_nr != 0)
{
// Clicked on breakpoint: create breakpoint menu
static Widget bp_popup_w = 0;
static Widget bp_popup_parent = 0;
if (lesstif_version <= 84 && w != bp_popup_parent)
{
// LessTif 0.84 and earlier wants this menu re-created
// every time the parent has changed. Otherwise, it gets
// insensitive.
if (bp_popup_w != 0)
XtDestroyWidget(bp_popup_w);
bp_popup_w = 0;
}
if (bp_popup_w == 0)
{
bp_popup_parent = w;
bp_popup_w = MMcreatePopupMenu(w, "bp_popup", bp_popup);
MMaddCallbacks (bp_popup, XtPointer(&bp_nr));
MMaddHelpCallback(bp_popup, ImmediateHelpCB);
InstallButtonTips(bp_popup_w);
}
// Grey out unsupported functions
set_sensitive(bp_popup[BPItms::Disable].widget, gdb->can_disable());
set_sensitive(bp_popup[BPItms::SetPC].widget,
gdb->has_jump_command() || gdb->has_assign_command());
MString label(bp_map.get(bp_nr)->enabled() ?
"Disable Breakpoint" : "Enable Breakpoint");
XtVaSetValues(bp_popup[BPItms::Disable].widget,
XmNlabelString, label.xmstring(),
NULL);
XmMenuPosition(bp_popup_w, event);
XtManageChild(bp_popup_w);
}
else if (pos_found
&& (line_nr > 0 || address != "")
&& (!in_text || right_of_text))
{
// Create popup menu for selected line
static Widget line_popup_w = 0;
if (line_popup_w == 0)
{
line_popup_w = MMcreatePopupMenu (w, "line_popup", line_popup);
MMaddCallbacks(line_popup, XtPointer(&address));
MMaddHelpCallback(line_popup, ImmediateHelpCB);
InstallButtonTips(line_popup_w);
set_sensitive(line_popup[LineItms::SetTempBP].widget,
gdb->has_temporary_breakpoints());
set_sensitive(line_popup[LineItms::SetPC].widget,
gdb->has_jump_command() ||
gdb->has_assign_command());
}
if (is_source_widget(w))
address = current_source_name() + ":" + itostring(line_nr);
else
address = string('*') + address;
XmMenuPosition (line_popup_w, event);
XtManageChild (line_popup_w);
}
else
{
// Determine surrounding token (or selection) and create popup
static string word;
XmTextPosition startpos = 0;
XmTextPosition endpos = 0;
if (pos_found)
word = get_word_at_pos(text_w, pos, startpos, endpos);
// Popup specific word menu
string current_arg = word;
shorten(current_arg, max_popup_expr_length);
string current_ref_arg = deref(current_arg);
if (lesstif_version <= 82)
{
set_text_popup_resource(TextItms::Print, current_arg);
set_text_popup_resource(TextItms::Disp, current_arg);
set_text_popup_resource(TextItms::Watch, current_arg);
set_text_popup_resource(TextItms::PrintRef, current_ref_arg);
set_text_popup_resource(TextItms::DispRef, current_ref_arg);
set_text_popup_resource(TextItms::WatchRef, current_ref_arg);
set_text_popup_resource(TextItms::Whatis, current_arg);
set_text_popup_resource(TextItms::Lookup, current_arg);
set_text_popup_resource(TextItms::Break, current_arg);
set_text_popup_resource(TextItms::Clear, current_arg);
}
Widget text_popup_w =
MMcreatePopupMenu(text_w, "text_popup", text_popup);
MMaddCallbacks(text_popup, XtPointer(&word));
MMaddHelpCallback(text_popup, ImmediateHelpCB);
InstallButtonTips(text_popup_w);
// The popup menu is destroyed immediately after having popped down.
Widget shell = XtParent(text_popup_w);
XtAddCallback(shell, XtNpopdownCallback, DestroyThisCB, shell);
bool has_arg = (word.length() > 0);
bool has_watch = has_arg && gdb->has_watch_command();
set_text_popup_label(TextItms::Print, current_arg, has_arg);
set_text_popup_label(TextItms::Disp, current_arg, has_arg);
set_text_popup_label(TextItms::Watch, current_arg, has_watch);
set_text_popup_label(TextItms::PrintRef, current_ref_arg, has_arg);
set_text_popup_label(TextItms::DispRef, current_ref_arg, has_arg);
set_text_popup_label(TextItms::WatchRef, current_ref_arg, has_watch);
set_text_popup_label(TextItms::Whatis, current_arg, has_arg);
set_text_popup_label(TextItms::Lookup, current_arg, has_arg);
set_text_popup_label(TextItms::Break, current_arg, has_arg);
set_text_popup_label(TextItms::Clear, current_arg, has_arg);
if (current_arg != current_ref_arg)
{
XtManageChild(text_popup[TextItms::Sep1].widget);
XtManageChild(text_popup[TextItms::PrintRef].widget);
XtManageChild(text_popup[TextItms::DispRef].widget);
// XtManageChild(text_popup[TextItms::WatchRef].widget);
}
else
{
XtUnmanageChild(text_popup[TextItms::Sep1].widget);
XtUnmanageChild(text_popup[TextItms::PrintRef].widget);
XtUnmanageChild(text_popup[TextItms::DispRef].widget);
XtUnmanageChild(text_popup[TextItms::WatchRef].widget);
}
XmMenuPosition (text_popup_w, event);
XtManageChild (text_popup_w);
}
}
void SourceView::doubleClickAct(Widget w, XEvent *e, String *params,
Cardinal *num_params)
{
if (e->type != ButtonPress && e->type != ButtonRelease)
return;
Widget text_w;
if (is_source_widget(w))
text_w = source_text_w;
else if (is_code_widget(w))
text_w = code_text_w;
else
return;
XButtonEvent* event = &e->xbutton;
int x = event->x;
int y = event->y;
bool control = ((event->state & ControlMask) != 0);
if (w != source_text_w && w != code_text_w)
{
// Called from a glyph: translate glyph position to text position
translate_glyph_pos(w, text_w, x, y);
}
if (w == source_text_w || w == code_text_w)
{
// Called from text: check for double click
Time selection_time = time(e);
static Time last_selection_time = 0;
bool double_click =
last_selection_time != 0 &&
(Time(selection_time - last_selection_time) <=
Time(XtGetMultiClickTime(XtDisplay(text_w))));
if (double_click)
last_selection_time = 0;
else
last_selection_time = selection_time;
if (!double_click)
return;
}
// Get the position
XmTextPosition pos = XmTextXYToPos(text_w, x, y);
int line_nr;
bool in_text;
static int bp_nr;
static string address;
bool pos_found = get_line_of_pos(w, pos, line_nr, address, in_text, bp_nr);
if (!pos_found)
return;
if (bp_nr != 0)
{
// Clicked on breakpoint
if (control)
delete_bp(bp_nr, text_w);
else
edit_bp(bp_nr, text_w);
return;
}
XmTextPosition startpos = 0;
XmTextPosition endpos = 0;
string arg = source_arg->get_string();
string word = get_word_at_pos(text_w, pos, startpos, endpos);
if (in_text && arg == word)
{
// In text: do some action on current word
if (text_w == source_text_w)
{
// Check for function call
int p = endpos;
while (p < (int)current_source.length() &&
isspace(current_source[p]))
p++;
if (control || current_source.contains('(', p))
{
if (*num_params >= 3)
gdb_button_command(params[2]);
else
gdb_button_command("list ()");
return;
}
}
if (*num_params >= 1)
gdb_button_command(params[0]);
else
gdb_button_command("graph display ()");
return;
}
if (!in_text)
{
// In breakpoint area
IntArray bps;
if (text_w == source_text_w)
{
MapRef ref;
for (BreakPoint* bp = bp_map.first(ref);
bp != 0;
bp = bp_map.next(ref))
{
if (bp_matches(bp, line_nr))
bps += bp->number();
}
}
else
{
MapRef ref;
for (BreakPoint* bp = bp_map.first(ref);
bp != 0;
bp = bp_map.next(ref))
{
if (bp->type() == BREAKPOINT &&
compare_address(address, bp->address()) == 0)
bps += bp->number();
}
}
if (bps.size() > 0)
{
// In breakpoint area, and we already have a breakpoint.
if (control)
delete_bps(bps, text_w);
else
edit_bps(bps, text_w);
}
else
{
// In breakpoint area, and we have no breakpoint: create a new one
if (*num_params >= 2)
gdb_button_command(params[1]);
else if (control)
create_temp_bp(source_arg->get_string(), w);
else
create_bp(source_arg->get_string(), w);
}
}
}
void SourceView::setArgAct(Widget w, XEvent *, String *, Cardinal *)
{
String s = 0;
if (XmIsText(w))
s = XmTextGetSelection(w);
else if (XmIsTextField(w))
s = XmTextFieldGetSelection(w);
if (s != 0)
{
source_arg->set_string(s);
XtFree(s);
}
}
//-----------------------------------------------------------------------------
// Breakpoint selection
//----------------------------------------------------------------------------
void SourceView::NewBreakpointDCB(Widget w, XtPointer client_data, XtPointer)
{
Widget text = Widget(client_data);
String _input = XmTextFieldGetString(text);
string input(_input);
XtFree(_input);
if (input == "")
return;
create_bp(input, w);
}
void SourceView::NewBreakpointCB(Widget w, XtPointer, XtPointer)
{
static Widget dialog = 0;
if (dialog == 0)
{
Arg args[10];
Cardinal arg = 0;
dialog = verify(XmCreatePromptDialog(find_shell(w),
"new_breakpoint_dialog",
args, arg));
Delay::register_shell(dialog);
if (lesstif_version <= 79)
XtUnmanageChild(XmSelectionBoxGetChild(dialog,
XmDIALOG_APPLY_BUTTON));
XtUnmanageChild(XmSelectionBoxGetChild(dialog,
XmDIALOG_SELECTION_LABEL));
XtUnmanageChild(XmSelectionBoxGetChild(dialog, XmDIALOG_TEXT));
arg = 0;
XtSetArg(args[arg], XmNmarginWidth, 0); arg++;
XtSetArg(args[arg], XmNmarginHeight, 0); arg++;
XtSetArg(args[arg], XmNborderWidth, 0); arg++;
Widget box = XmCreateRowColumn(dialog, "box", args, arg);
XtManageChild(box);
Widget label = XmCreateLabel(box, "label", args, arg);
XtManageChild(label);
arg = 0;
Widget text = CreateComboBox(box, "text", args, arg);
tie_combo_box_to_history(text, break_history_filter);
XtAddCallback(dialog, XmNhelpCallback, ImmediateHelpCB, NULL);
XtAddCallback(dialog, XmNokCallback, NewBreakpointDCB,
XtPointer(text));
}
manage_and_raise(dialog);
}
WatchMode SourceView::selected_watch_mode = WATCH_CHANGE;
void SourceView::SetWatchModeCB(Widget, XtPointer client_data,
XtPointer call_data)
{
XmToggleButtonCallbackStruct *info =
(XmToggleButtonCallbackStruct *)call_data;
if (info->set)
selected_watch_mode = WatchMode((int)(long)client_data);
}
void SourceView::NewWatchpointDCB(Widget w, XtPointer client_data, XtPointer)
{
Widget text = Widget(client_data);
String _input = XmTextFieldGetString(text);
string input(_input);
XtFree(_input);
strip_space(input);
if (input == "")
return;
gdb_command(gdb->watch_command(input, selected_watch_mode), w);
}
void SourceView::NewWatchpointCB(Widget w, XtPointer, XtPointer)
{
static Widget dialog = 0;
if (dialog == 0)
{
static Widget cwatch_w, rwatch_w, awatch_w;
static MMDesc wp_modes[] =
{
{ "cwatch", MMPush, { SetWatchModeCB, XtPointer(WATCH_CHANGE) },
NULL, &cwatch_w, 0, 0 },
{ "rwatch", MMPush, { SetWatchModeCB, XtPointer(WATCH_READ) },
NULL, &rwatch_w, 0, 0 },
{ "awatch", MMPush, { SetWatchModeCB, XtPointer(WATCH_ACCESS)},
NULL, &awatch_w, 0, 0 },
MMEnd
};
static MMDesc wp_menu[] =
{
{ "set", MMLabel, MMNoCB, 0, 0, 0, 0 },
{ "method", MMOptionMenu, MMNoCB, wp_modes, 0, 0, 0 },
{ "on", MMLabel, MMNoCB, 0, 0, 0, 0 },
MMEnd
};
Arg args[10];
Cardinal arg = 0;
dialog = verify(XmCreatePromptDialog(find_shell(w),
"new_watchpoint_dialog",
args, arg));
Delay::register_shell(dialog);
if (lesstif_version <= 79)
XtUnmanageChild(XmSelectionBoxGetChild(dialog,
XmDIALOG_APPLY_BUTTON));
XtUnmanageChild(XmSelectionBoxGetChild(dialog,
XmDIALOG_SELECTION_LABEL));
XtUnmanageChild(XmSelectionBoxGetChild(dialog, XmDIALOG_TEXT));
arg = 0;
XtSetArg(args[arg], XmNmarginWidth, 0); arg++;
XtSetArg(args[arg], XmNmarginHeight, 0); arg++;
XtSetArg(args[arg], XmNborderWidth, 0); arg++;
Widget box = XmCreateRowColumn(dialog, "box", args, arg);
XtManageChild(box);
arg = 0;
XtSetArg(args[arg], XmNorientation, XmHORIZONTAL); arg++;
XtSetArg(args[arg], XmNborderWidth, 0); arg++;
XtSetArg(args[arg], XmNentryBorder, 0); arg++;
XtSetArg(args[arg], XmNspacing, 0); arg++;
XtSetArg(args[arg], XmNmarginWidth, 0); arg++;
XtSetArg(args[arg], XmNmarginHeight, 0); arg++;
Widget panel = MMcreateButtonPanel(box, "panel", wp_menu, args, arg);
(void) panel;
MMaddCallbacks(wp_menu);
MMaddHelpCallback(wp_menu, ImmediateHelpCB);
set_sensitive(cwatch_w, (gdb->has_watch_command() & WATCH_CHANGE)
== WATCH_CHANGE);
set_sensitive(rwatch_w, (gdb->has_watch_command() & WATCH_READ)
== WATCH_READ);
set_sensitive(awatch_w, (gdb->has_watch_command() & WATCH_ACCESS)
== WATCH_ACCESS);
// Initialize: use CWATCH as default menu item
XtCallActionProc(cwatch_w, "ArmAndActivate",
(XEvent *)0, (String *)0, 0);
arg = 0;
Widget text = CreateComboBox(box, "text", args, arg);
tie_combo_box_to_history(text, watch_history_filter);
XtAddCallback(dialog, XmNhelpCallback, ImmediateHelpCB, NULL);
XtAddCallback(dialog, XmNokCallback, NewWatchpointDCB,
XtPointer(text));
}
manage_and_raise(dialog);
}
//-----------------------------------------------------------------------------
// Breakpoint properties
//----------------------------------------------------------------------------
struct BreakpointPropertiesInfo {
IntArray nrs; // The affected breakpoints
Widget dialog; // The widgets of the properties panel
Widget title;
Widget lookup;
Widget print;
Widget enable;
Widget disable;
Widget temp;
Widget del;
Widget ignore;
Widget condition;
Widget record;
Widget end;
Widget edit;
Widget editor;
XtIntervalId timer; // The spinbox timer
bool spin_locked; // If true, don't invoke spinbox callbacks
int ignore_spin_update; // If > 0, don't update spinbox from bp info
bool sync_commands; // If true, propagate cmd to other breakpoints
BreakpointPropertiesInfo *next; // Next info in list
static BreakpointPropertiesInfo *all; // List of all infos
BreakpointPropertiesInfo()
: nrs(),
dialog(0), title(0),
lookup(0), enable(0), disable(0), temp(0), del(0),
ignore(0), condition(0), record(0), edit(0), editor(0),
timer(0), spin_locked(false), ignore_spin_update(0),
sync_commands(false), next(all)
{
all = this;
}
~BreakpointPropertiesInfo()
{
if (all == this)
{
all = next;
}
else
{
BreakpointPropertiesInfo *info = all;
while (info->next != this)
info = info->next;
info->next = next;
}
}
};
BreakpointPropertiesInfo *BreakpointPropertiesInfo::all = 0;
void SourceView::DeleteInfoCB(Widget, XtPointer client_data,
XtPointer call_data)
{
BreakpointPropertiesInfo *info =
(BreakpointPropertiesInfo *)client_data;
gdb->removeHandler(Recording, RecordingHP, (void *)info);
if (gdb->recording())
gdb_command("\003"); // Abort recording
if (XtIsManaged(XtParent(info->editor)))
{
// Finish entering commands. Since W is being destroyed, pass
// SOURCE_TEXT_W as reference.
EditBreakpointCommandsCB(source_text_w, client_data, call_data);
// Update all remaining panels in the next run
gdb->addHandler(Recording, RecordingHP, XtPointer(0));
}
delete info;
}
void SourceView::update_properties_panels()
{
// Update all panels
BreakpointPropertiesInfo *info = BreakpointPropertiesInfo::all;
while (info != 0)
{
update_properties_panel(info);
info = info->next;
}
}
void SourceView::move_breakpoint_properties(int old_bp, int new_bp)
{
// Update all panels
bool update = false;
BreakpointPropertiesInfo *info = BreakpointPropertiesInfo::all;
while (info != 0)
{
bool changed = false;
for (int i = 0; i < info->nrs.size(); i++)
{
if (info->nrs[i] == old_bp)
{
info->nrs[i] = new_bp;
update = changed = true;
}
}
if (changed)
sort(info->nrs); // Sort again
info = info->next;
}
if (update)
update_properties_panels();
}
void SourceView::copy_breakpoint_properties(int old_bp, int new_bp)
{
// Update all panels
bool update = false;
BreakpointPropertiesInfo *info = BreakpointPropertiesInfo::all;
while (info != 0)
{
for (int i = 0; i < info->nrs.size(); i++)
{
if (info->nrs[i] == old_bp)
{
info->nrs += new_bp;
update = true;
break;
}
}
info = info->next;
}
if (update)
update_properties_panels();
}
void SourceView::update_properties_panel(BreakpointPropertiesInfo *info)
{
// Remove breakpoints from list
bool future = false;
int i;
for (i = 0; i < info->nrs.size(); i++)
{
if (info->nrs[i] >= next_breakpoint_number())
{
// Panel for future (renamed) breakpoint
future = true;
continue;
}
BreakPoint *bp = bp_map.get(info->nrs[i]);
if (bp == 0)
{
// Breakpoint not found -- mark as deleted
info->nrs[i] = 0;
}
}
IntArray new_nrs;
for (i = 0; i < info->nrs.size(); i++)
{
if (info->nrs[i] != 0)
new_nrs += info->nrs[i];
}
info->nrs = new_nrs;
if (info->nrs.size() == 0)
{
// No breakpoint left -- destroy dialog shell
XtUnmanageChild(info->dialog);
return;
}
if (future)
return; // We cannot update yet
// Use first breakpoint for getting values
BreakPoint *bp = bp_map.get(info->nrs[0]);
assert(bp != 0);
// Set titles
string what;
switch (bp->type())
{
case BREAKPOINT:
what = "Breakpoint";
break;
case WATCHPOINT:
what = "Watchpoint";
break;
}
string label;
if (info->nrs.size() == 1)
{
label = what + " " + itostring(info->nrs[0]);
}
else
{
label = what + "s ";
for (i = 0; i < info->nrs.size(); i++)
{
if (i > 0)
{
if (info->nrs.size() == 2)
label += " and ";
else if (i == info->nrs.size() - 1)
label += ", and ";
else
label += ", ";
}
label += itostring(info->nrs[i]);
}
}
set_label(info->title, label);
MString title = string(DDD_NAME) + ": Properties: " + label;
XtVaSetValues(info->dialog, XmNdialogTitle,
title.xmstring(), NULL);
// Set values
string commands = "";
for (i = 0; i < bp->commands().size(); i++)
{
string cmd = bp->commands()[i];
strip_auto_command_prefix(cmd);
commands += cmd + "\n";
}
XmTextSetString(info->editor, commands);
if (info->ignore_spin_update > 0)
{
info->ignore_spin_update--;
}
else
{
bool lock = info->spin_locked;
info->spin_locked = true;
#if XmVersion >= 2000
if (XmIsSpinBox(XtParent(info->ignore)))
{
XtVaSetValues(info->ignore, XmNposition, bp->ignore_count(), NULL);
}
else
#endif
{
String old_ignore = XmTextFieldGetString(info->ignore);
if (atoi(old_ignore) != bp->ignore_count())
{
string ignore = itostring(bp->ignore_count());
if (ignore == "0")
ignore = "";
XmTextFieldSetString(info->ignore, (String)ignore);
}
XtFree(old_ignore);
}
info->spin_locked = lock;
}
// Don't update unchanged condition to prevent OSF/Motif 2.0
// ComboBox from growing
String old_condition = XmTextFieldGetString(info->condition);
if (bp->condition() != old_condition)
{
XmTextFieldSetString(info->condition, (String)bp->condition());
}
XtFree(old_condition);
bool can_enable = false;
bool can_disable = false;
bool can_maketemp = false;
bool can_print = false;
for (i = 0; i < info->nrs.size(); i++)
{
BreakPoint *bp = bp_map.get(info->nrs[i]);
if (bp->enabled())
can_disable = gdb->can_disable();
else
can_enable = gdb->can_enable();
if (bp->dispo() != BPDEL)
can_maketemp = (gdb->type() == GDB || gdb->type() == PYDB);
if (bp->type() == WATCHPOINT)
can_print = true;
}
if (can_print)
XtManageChild(info->print);
else
XtUnmanageChild(info->print);
set_sensitive(info->enable, can_enable);
set_sensitive(info->disable, can_disable);
set_sensitive(info->temp, can_maketemp);
set_sensitive(info->ignore, gdb->has_ignore_command());
set_sensitive(XtParent(info->ignore), gdb->has_ignore_command());
set_sensitive(info->condition, gdb->has_breakpoint_conditions());
set_sensitive(XtParent(info->condition), gdb->has_breakpoint_conditions());
bool can_record = gdb->type() == GDB && !gdb->recording();
set_sensitive(info->record, can_record);
set_sensitive(info->end, gdb->recording());
set_sensitive(info->edit, can_record);
set_sensitive(info->editor, can_record);
if (info->sync_commands)
{
for (i = 1; i < info->nrs.size(); i++)
set_bp_commands(info->nrs[i], bp->commands());
info->sync_commands = false;
}
}
static string cond_filter(const string& cmd)
{
switch (gdb->type())
{
case GDB:
case PYDB:
if (cmd.contains("cond", 0))
{
// Skip command
string arg = cmd.after(rxwhite);
// Skip breakpoint number
arg = arg.after(rxwhite);
strip_space(arg);
return arg;
}
break;
case DBX:
if (cmd.contains("if "))
{
string arg = cmd.after("if ");
strip_space(arg);
return arg;
}
break;
case XDB:
if (cmd.contains("{if "))
{
string arg = cmd.after("if ");
if (arg.contains("{}"))
arg = arg.before("{}");
strip_space(arg);
return arg;
}
break;
case JDB:
// No conditions in JDB
break;
case PERL:
{
// FIXME
break;
}
}
return ""; // No condition
}
// Get selected breakpoint numbers
void SourceView::getBreakpointNumbers(IntArray& breakpoint_nrs)
{
if (breakpoint_list_w == 0)
return;
IntArray numbers;
getItemNumbers(breakpoint_list_w, numbers);
// Double-check each number for safety. One may define commands
// as breakpoint numbers and cause DDD to crash.
for (int i = 0; i < numbers.size(); i++)
{
BreakPoint *bp = bp_map.get(numbers[i]);
if (bp != 0)
breakpoint_nrs += numbers[i];
}
}
// Edit breakpoint properties
void SourceView::EditBreakpointPropertiesCB(Widget,
XtPointer client_data,
XtPointer)
{
IntArray breakpoint_nrs;
if (client_data == 0)
{
getBreakpointNumbers(breakpoint_nrs);
}
else
{
breakpoint_nrs += *((int *)client_data);
}
edit_bps(breakpoint_nrs);
}
void SourceView::edit_bps(IntArray& breakpoint_nrs, Widget /* origin */)
{
if (breakpoint_nrs.size() == 0)
return; // No breakpoints given
sort(breakpoint_nrs);
// Check for first breakpoint
BreakPoint *bp = bp_map.get(breakpoint_nrs[0]);
if (bp == 0)
return; // No such breakpoint
BreakpointPropertiesInfo *info = new BreakpointPropertiesInfo;
info->spin_locked = true;
info->nrs = breakpoint_nrs;
Arg args[10];
int arg = 0;
XtSetArg(args[arg], XmNautoUnmanage, False); arg++;
info->dialog =
verify(XmCreatePromptDialog(source_text_w,
"breakpoint_properties",
args, arg));
XtVaSetValues(info->dialog, XmNdefaultButton, Widget(0), NULL);
// Remove old prompt
Widget text = XmSelectionBoxGetChild(info->dialog, XmDIALOG_TEXT);
XtUnmanageChild(text);
Widget old_label =
XmSelectionBoxGetChild(info->dialog, XmDIALOG_SELECTION_LABEL);
XtUnmanageChild(old_label);
Delay::register_shell(info->dialog);
if (lesstif_version <= 79)
XtUnmanageChild(XmSelectionBoxGetChild(info->dialog,
XmDIALOG_APPLY_BUTTON));
XtUnmanageChild(XmSelectionBoxGetChild(info->dialog,
XmDIALOG_CANCEL_BUTTON));
MMDesc commands_menu[] =
{
{ "record", MMPush,
{ RecordBreakpointCommandsCB, XtPointer(info) },
0, &info->record, 0, 0 },
{ "end", MMPush | MMInsensitive,
{ EndBreakpointCommandsCB, XtPointer(info) },
0, &info->end, 0, 0 },
{ "edit", MMPush | MMInsensitive,
{ EditBreakpointCommandsCB, XtPointer(info) },
0, &info->edit, 0, 0 },
MMEnd
};
MMDesc enabled_menu[] =
{
{ "lookup", MMPush,
{ LookupBreakpointCB, XtPointer(info) }, 0, &info->lookup, 0, 0 },
{ "print", MMPush,
{ PrintWatchpointCB, XtPointer(info) }, 0, &info->print, 0, 0 },
{ "enable", MMPush,
{ EnableBreakpointsCB, XtPointer(info) }, 0, &info->enable, 0, 0 },
{ "disable", MMPush,
{ DisableBreakpointsCB, XtPointer(info) }, 0, &info->disable, 0, 0},
{ "temporary", MMPush,
{ MakeBreakpointsTempCB, XtPointer(info) }, 0, &info->temp, 0, 0 },
{ "delete", MMPush | MMHelp,
{ DeleteBreakpointsCB, XtPointer(info) }, 0, &info->del, 0, 0 },
MMEnd
};
if (app_data.flat_dialog_buttons)
{
for (MMDesc *item = enabled_menu; item != 0 && item->name != 0; item++)
{
if ((item->type & MMTypeMask) == MMPush)
item->type = (MMFlatPush | (item->type & ~MMTypeMask));
}
}
MMDesc panel_menu[] =
{
{ "title", MMButtonPanel, MMNoCB, enabled_menu, 0, 0, 0 },
{ "condition", MMComboBox,
{ SetBreakpointConditionCB, XtPointer(info) },
0, &info->condition, 0, 0 },
{ "ignore", MMSpinBox,
{ SetBreakpointIgnoreCountCB, XtPointer(info) },
0, &info->ignore, 0, 0 },
{ "commands", MMButtonPanel, MMNoCB, commands_menu, 0, 0, 0 },
MMEnd
};
arg = 0;
XtSetArg(args[arg], XmNorientation, XmHORIZONTAL); arg++;
Widget form = XmCreateRowColumn(info->dialog, "form", args, arg);
XtManageChild(form);
Widget panel = MMcreatePanel(form, "panel", panel_menu);
XtVaSetValues(panel,
XmNmarginWidth, 0,
XmNmarginHeight, 0,
NULL);
Widget buttons = XtParent(info->lookup);
XtVaSetValues(buttons,
XmNmarginWidth, 0,
XmNmarginHeight, 0,
XmNborderWidth, 0,
XmNshadowThickness, 0,
XmNspacing, 0,
NULL);
arg = 0;
XtSetArg(args[arg], XmNeditMode, XmMULTI_LINE_EDIT); arg++;
info->editor = XmCreateScrolledText(form, "text", args, arg);
XtUnmanageChild(XtParent(info->editor));
XtManageChild(info->editor);
info->title = panel_menu[0].label;
MMaddCallbacks(panel_menu, XtPointer(info));
update_properties_panel(info);
InstallButtonTips(panel);
MMadjustPanel(panel_menu);
XtAddCallback(info->dialog, XmNokCallback,
UnmanageThisCB, info->dialog);
XtAddCallback(info->dialog, XmNhelpCallback,
ImmediateHelpCB, NULL);
XtAddCallback(info->dialog, XmNunmapCallback,
DestroyThisCB, XtParent(info->dialog));
XtAddCallback(info->dialog, XmNdestroyCallback,
DeleteInfoCB, XtPointer(info));
tie_combo_box_to_history(info->condition, cond_filter);
manage_and_raise(info->dialog);
info->spin_locked = false;
}
// Set breakpoint condition
void SourceView::SetBreakpointConditionCB(Widget w,
XtPointer client_data,
XtPointer call_data)
{
XmAnyCallbackStruct *cbs = (XmAnyCallbackStruct *)call_data;
switch (cbs->reason)
{
case XmCR_ACTIVATE: // Pressed `RETURN'
case XmCR_SINGLE_SELECT: // Selection from ComboBox
case XmCR_MULTIPLE_SELECT:
case XmCR_EXTENDED_SELECT:
case XmCR_BROWSE_SELECT:
break;
default:
return; // Value changed
}
BreakpointPropertiesInfo *info =
(BreakpointPropertiesInfo *)client_data;
String cond = XmTextFieldGetString(info->condition);
set_bps_cond(info->nrs, cond, w);
XtFree(cond);
}
// Set breakpoint ignore count
void SourceView::SetBreakpointIgnoreCountCB(Widget w,
XtPointer client_data,
XtPointer call_data)
{
XmAnyCallbackStruct *cbs = (XmAnyCallbackStruct *)call_data;
BreakpointPropertiesInfo *info =
(BreakpointPropertiesInfo *)client_data;
if (info->spin_locked)
return; // Ignore the SetValue change
int delay = 500; // Wait until the SpinBox stops spinning
if (cbs->reason == XmCR_ACTIVATE)
delay = 0;
if (info->timer != 0)
{
XtRemoveTimeOut(info->timer);
info->timer = 0;
}
info->timer = XtAppAddTimeOut(XtWidgetToApplicationContext(w),
delay,
SetBreakpointIgnoreCountNowCB,
client_data);
}
void SourceView::SetBreakpointIgnoreCountNowCB(XtPointer client_data,
XtIntervalId *id)
{
BreakpointPropertiesInfo *info =
(BreakpointPropertiesInfo *)client_data;
assert (info->timer == *id);
(void) id; // Use it
info->timer = 0;
String _count = XmTextFieldGetString(info->ignore);
int count = atoi(_count);
XtFree(_count);
for (int i = 0; i < info->nrs.size(); i++)
{
gdb_command(gdb->ignore_command(itostring(info->nrs[i]), count));
info->ignore_spin_update++;
}
}
// Make breakpoint temporary
void SourceView::MakeBreakpointsTempCB(Widget, XtPointer client_data,
XtPointer)
{
BreakpointPropertiesInfo *info =
(BreakpointPropertiesInfo *)client_data;
gdb_command("enable delete " + numbers(info->nrs));
}
// Delete Breakpoint
void SourceView::DeleteBreakpointsCB(Widget, XtPointer client_data, XtPointer)
{
BreakpointPropertiesInfo *info =
(BreakpointPropertiesInfo *)client_data;
delete_bps(info->nrs);
}
// Enable Breakpoints
void SourceView::EnableBreakpointsCB(Widget, XtPointer client_data, XtPointer)
{
BreakpointPropertiesInfo *info =
(BreakpointPropertiesInfo *)client_data;
enable_bps(info->nrs);
}
// Disable Breakpoints
void SourceView::DisableBreakpointsCB(Widget, XtPointer client_data, XtPointer)
{
BreakpointPropertiesInfo *info =
(BreakpointPropertiesInfo *)client_data;
disable_bps(info->nrs);
}
// Record breakpoint commands
void SourceView::RecordBreakpointCommandsCB(Widget w,
XtPointer client_data,
XtPointer)
{
BreakpointPropertiesInfo *info =
(BreakpointPropertiesInfo *)client_data;
gdb->removeHandler(Recording, RecordingHP, (void *)info);
gdb->addHandler(Recording, RecordingHP, (void *)info);
gdb_command("commands " + itostring(info->nrs[0]), w);
}
// End recording breakpoint commands
void SourceView::EndBreakpointCommandsCB(Widget w, XtPointer, XtPointer)
{
gdb_command("end", w);
}
void SourceView::RefreshBreakpointsHP(Agent *, void *, void *call_data)
{
bool gdb_ready = bool(call_data);
if (gdb_ready && !gdb->recording())
{
// Don't get called recursively
gdb->removeHandler(ReadyForQuestion, RefreshBreakpointsHP);
string breakpoints = gdb_question("info breakpoints");
if (breakpoints == NO_GDB_ANSWER)
{
// Try again next time
gdb->addHandler(ReadyForQuestion, RefreshBreakpointsHP);
}
else
{
process_info_bp(breakpoints);
}
}
}
// Log recording state
void SourceView::RecordingHP(Agent *, void *client_data, void *call_data)
{
BreakpointPropertiesInfo *info =
(BreakpointPropertiesInfo *)client_data;
bool recording = bool(call_data);
// Refresh buttons
if (info == 0)
update_properties_panels();
else
update_properties_panel(info);
if (!recording)
{
// Recording is over. Don't get called again.
gdb->removeHandler(Recording, RecordingHP, (void *)info);
// Update breakpoints
gdb->addHandler(ReadyForQuestion, RefreshBreakpointsHP);
if (info != 0)
{
// Upon next panel update, propagate command to other breakpoints
info->sync_commands = true;
}
}
}
// Set breakpoint commands
void SourceView::set_bp_commands(IntArray& nrs, const StringArray& commands,
Widget origin)
{
for (int i = 0; i < nrs.size(); i++)
{
// Check for breakpoint
MapRef ref;
BreakPoint *bp = 0;
for (bp = bp_map.first(ref); bp != 0; bp = bp_map.next(ref))
{
if (bp->number() == nrs[i])
break;
}
if (bp == 0)
continue; // No such breakpoint
if (commands.size() == bp->commands().size())
{
bool same_commands = true;
for (int j = 0; same_commands && j < bp->commands().size(); j++)
{
string c1 = bp->commands()[j];
strip_auto_command_prefix(c1);
string c2 = commands[j];
strip_auto_command_prefix(c2);
if (c1 != c2)
same_commands = false;
}
if (same_commands)
continue; // Commands unchanged
}
gdb_command("commands " + itostring(nrs[i]), origin);
for (int j = 0; j < commands.size(); j++)
gdb_command(commands[j], origin);
gdb_command("end", origin);
}
}
// Edit breakpoint commands
void SourceView::EditBreakpointCommandsCB(Widget w,
XtPointer client_data,
XtPointer)
{
BreakpointPropertiesInfo *info =
(BreakpointPropertiesInfo *)client_data;
if (XtIsManaged(XtParent(info->editor)))
{
XtUnmanageChild(XtParent(info->editor));
MString label = "Edit " + MString(">>", "small");
set_label(info->edit, label);
String _commands = XmTextGetString(info->editor);
string cmd = _commands;
XtFree(_commands);
if (!cmd.contains('\n', -1))
cmd += '\n';
StringArray commands;
while (cmd != "")
{
string c = cmd.before('\n');
if (c != "")
commands += c;
cmd = cmd.after('\n');
}
set_bp_commands(info->nrs, commands, w);
// Update all panels in the next run
gdb->addHandler(Recording, RecordingHP, (void *)0);
}
else
{
XtManageChild(XtParent(info->editor));
MString label = "Edit " + MString("<<", "small");
set_label(info->edit, label);
}
}
void SourceView::edit_breakpoint_properties(int bp_nr)
{
static int n;
n = bp_nr;
EditBreakpointPropertiesCB(source_text_w, XtPointer(&n), 0);
}
//-----------------------------------------------------------------------------
// Breakpoint commands
//----------------------------------------------------------------------------
void SourceView::BreakpointCmdCB(Widget,
XtPointer client_data,
XtPointer)
{
if (breakpoint_list_w == 0)
return;
IntArray nrs;
getBreakpointNumbers(nrs);
if (nrs.size() == 0)
return;
string cmd = (String)client_data;
if (cmd == "delete")
delete_bps(nrs);
else if (cmd == "enable")
enable_bps(nrs);
else if (cmd == "disable")
disable_bps(nrs);
}
void SourceView::LookupBreakpointCB(Widget, XtPointer client_data, XtPointer)
{
if (breakpoint_list_w == 0)
return;
IntArray breakpoint_nrs;
if (client_data == 0)
{
getBreakpointNumbers(breakpoint_nrs);
}
else
{
BreakpointPropertiesInfo *info =
(BreakpointPropertiesInfo *)client_data;
breakpoint_nrs = info->nrs;
}
if (breakpoint_nrs.size() == 0)
return;
BreakPoint *bp = bp_map.get(breakpoint_nrs[0]);
if (bp == 0)
return;
switch (bp->type())
{
case BREAKPOINT:
lookup("#" + itostring(breakpoint_nrs[0]));
break;
case WATCHPOINT:
lookup(bp->expr());
break;
}
}
void SourceView::PrintWatchpointCB(Widget w, XtPointer client_data, XtPointer)
{
if (breakpoint_list_w == 0)
return;
IntArray breakpoint_nrs;
if (client_data == 0)
{
getBreakpointNumbers(breakpoint_nrs);
}
else
{
BreakpointPropertiesInfo *info =
(BreakpointPropertiesInfo *)client_data;
breakpoint_nrs = info->nrs;
}
if (breakpoint_nrs.size() < 1)
return;
BreakPoint *bp = bp_map.get(breakpoint_nrs[0]);
if (bp == 0)
return;
switch (bp->type())
{
case BREAKPOINT:
// How should we print a breakpoint? (FIXME)
break;
case WATCHPOINT:
gdb_command(gdb->print_command(bp->expr(), false), w);
break;
}
}
// Return breakpoint of BP_INFO; 0 if new; -1 if none
int SourceView::breakpoint_number(const string& bp_info)
{
string class_name = current_source_name();
int line = 0;
switch (gdb->type())
{
case JDB:
{
int colon = bp_info.index(':');
if (colon < 0)
return -1; // No breakpoint
class_name = bp_info.before(colon);
line = get_positive_nr(bp_info.after(colon));
break;
}
case PERL:
{
line = get_positive_nr(bp_info);
break;
}
default:
return -1; // Never reached
}
strip_leading_space(class_name);
if (line <= 0 || class_name.contains(' '))
return -1; // No breakpoint
MapRef ref;
for (BreakPoint* bp = bp_map.first(ref); bp != 0; bp = bp_map.next(ref))
if (bp_matches(bp, class_name, line))
return bp->number(); // Existing breakpoint
return 0; // New breakpoint
}
// Handle breakpoint info
void SourceView::process_breakpoints(string& info_breakpoints_output)
{
if (breakpoint_list_w == 0)
return;
strip_space(info_breakpoints_output);
if (info_breakpoints_output == "")
{
if (gdb->has_watch_command())
info_breakpoints_output = "No breakpoints or watchpoints.";
else
info_breakpoints_output = "No breakpoints.";
}
int count = info_breakpoints_output.freq('\n') + 1;
string *breakpoint_list = new string[count];
bool *selected = new bool[count];
split(info_breakpoints_output, breakpoint_list, count, '\n');
while (count > 0 && breakpoint_list[count - 1] == "")
count--;
bool select = false;
for (int i = 0; i < count; i++)
{
string& bp_info = breakpoint_list[i];
if (!gdb->has_numbered_breakpoints())
{
// JDB has no breakpoint numbers -- insert our own
int bp_nr = breakpoint_number(bp_info);
if (bp_nr > 0)
{
string s = itostring(bp_nr) + " ";
bp_info.prepend(s.at(0, 4));
}
}
// Select number
int bp_number = get_positive_nr(bp_info);
if (bp_number > 0)
{
MapRef ref;
for (BreakPoint* bp = bp_map.first(ref);
bp != 0;
bp = bp_map.next(ref))
{
if (bp->number() == bp_number)
{
select = bp->selected();
break;
}
}
}
selected[i] = select;
strip_auto_command_prefix(bp_info);
setup_where_line(bp_info);
}
setLabelList(breakpoint_list_w, breakpoint_list, selected, count,
(gdb->type() == GDB ||
gdb->type() == PYDB) && count > 1, false);
UpdateBreakpointButtonsCB(breakpoint_list_w, XtPointer(0), XtPointer(0));
delete[] breakpoint_list;
delete[] selected;
}
void SourceView::UpdateBreakpointButtonsCB(Widget, XtPointer,
XtPointer call_data)
{
(void) call_data; // Use it
if (edit_breakpoints_dialog_w == 0)
return;
IntArray breakpoint_nrs;
getBreakpointNumbers(breakpoint_nrs);
// Update selection
MapRef ref;
BreakPoint *bp;
for (bp = bp_map.first(ref); bp != 0; bp = bp_map.next(ref))
bp->selected() = false;
for (int i = 0; i < breakpoint_nrs.size(); i++)
{
int bp_number = breakpoint_nrs[i];
for (bp = bp_map.first(ref); bp != 0; bp = bp_map.next(ref))
{
if (bp->number() == bp_number)
{
bp->selected() = true;
break;
}
}
}
#if 0
if (call_data != 0)
{
// Update status line
if (breakpoint_nrs.size() == 1)
set_status_mstring(help_on_bp(breakpoint_nrs[0], true));
else
set_status("");
}
#endif
// Count selected ones
BreakPoint *selected_bp = 0;
int selected = 0;
int selected_enabled = 0;
int selected_disabled = 0;
for (bp = bp_map.first(ref); bp != 0; bp = bp_map.next(ref))
{
if (bp->selected())
{
selected_bp = bp;
selected++;
if (bp->enabled())
selected_enabled++;
else
selected_disabled++;
}
}
// Update buttons
set_sensitive(bp_area[BPButtons::NewWP].widget, gdb->has_watch_command());
set_sensitive(bp_area[BPButtons::Lookup].widget, selected == 1);
set_sensitive(bp_area[BPButtons::Print].widget,
selected == 1 && selected_bp->type() == WATCHPOINT);
set_sensitive(bp_area[BPButtons::Enable].widget,
gdb->can_enable() && selected_disabled > 0);
set_sensitive(bp_area[BPButtons::Disable].widget,
gdb->can_disable() && selected_enabled > 0);
set_sensitive(bp_area[BPButtons::Properties].widget, selected > 0);
set_sensitive(bp_area[BPButtons::Delete].widget, selected > 0);
}
void SourceView::EditBreakpointsCB(Widget, XtPointer, XtPointer)
{
manage_and_raise(edit_breakpoints_dialog_w);
}
//-----------------------------------------------------------------------------
// Stack frame selection
//----------------------------------------------------------------------------
void SourceView::StackDialogPoppedDownCB (Widget, XtPointer, XtPointer)
{
stack_dialog_popped_up = false;
refresh_buttons();
}
void SourceView::SelectFrameCB (Widget w, XtPointer, XtPointer call_data)
{
XmListCallbackStruct *cbs = (XmListCallbackStruct *)call_data;
int count = 0;
XtVaGetValues(w,
XmNitemCount, &count,
NULL);
set_sensitive(up_w, cbs->item_position > 1);
set_sensitive(down_w, cbs->item_position < count);
refresh_buttons();
switch (gdb->type())
{
case GDB:
// GDB frame output is caught by our routines.
gdb_command(gdb->frame_command(count - cbs->item_position));
break;
case XDB:
// XDB frame output is caught by our routines.
gdb_command(gdb->frame_command(cbs->item_position - 1));
break;
case DBX:
case JDB:
case PYDB:
case PERL:
if (gdb->has_frame_command())
{
// Issue `frame' command
gdb_command(gdb->frame_command(count - cbs->item_position + 1));
}
else
{
// JDB, PYDB and some DBXes lack a `frame' command.
// Use `up N'/`down N' instead.
int offset = cbs->item_position - last_frame_pos;
if (offset != 0)
gdb_command(gdb->relative_frame_command(-offset));
// Call `set_frame_pos' now.
frame_pos_locked = false;
set_frame_pos(0, cbs->item_position);
// Ignore the `up'/`down' reply.
frame_pos_locked = (offset != 0);
}
break;
}
}
void SourceView::refresh_stack_frames()
{
// Allow unlimited time to find out where we are
string where_s = gdb_question(gdb->where_command(), -1, true);
if (where_s == NO_GDB_ANSWER)
where_s = "No stack.";
process_where(where_s);
if (gdb->has_frame_command())
{
string frame = gdb_question(gdb->frame_command());
process_frame(frame);
}
}
void SourceView::ViewStackFramesCB(Widget, XtPointer, XtPointer)
{
refresh_stack_frames();
manage_and_raise(stack_dialog_w);
stack_dialog_popped_up = true;
refresh_buttons();
}
// Remove file paths and argument lists from `where' output
void SourceView::setup_where_line(string& line)
{
if (gdb->type() != JDB)
{
// Remove file paths (otherwise line can be too long for DBX)
// ... n.b. with templates, line can still be rather long
#if RUNTIME_REGEX
static regex rxfilepath("[^\"\'` /]*/");
#endif
line.gsub(rxfilepath, "");
}
if (gdb->type() != JDB)
{
// Shorten argument lists `(a = 1, b = 2, ...)' to `()'
#if RUNTIME_REGEX
static regex rxarglist("[(][^0-9][^)]*[)]");
#endif
int start = index(line, rxarglist, "(");
if (start > 0)
{
int end = line.index(')', -1);
if (end > start)
line = line.through(start) + line.from(end);
}
}
const int min_width = 40;
if (int(line.length()) < min_width)
line += replicate(' ', min_width - line.length());
}
// Return current JDB frame; 0 if none
inline int jdb_frame()
{
return get_positive_nr(gdb->prompt().from("["));
}
// Return current JDB thread; "" if none
inline string jdb_thread()
{
return gdb->prompt().before("[");
}
// Process `where' output
void SourceView::process_where(string& where_output)
{
if (!where_output.contains("No ", 0))
undo_buffer.add_where(where_output);
int count = where_output.freq('\n') + 1;
string *frame_list = new string[count];
bool *selected = new bool[count];
split(where_output, frame_list, count, '\n');
StringArray frames;
int i;
for (i = 0; i < count; i++)
{
const string& frame = frame_list[i];
if (frame == "")
continue; // Skip empty lines
if (frame.contains("Reading ", 0))
continue; // Skip GDB `Reading in symbols' messages
frames += frame;
}
delete[] frame_list;
frame_list = frames;
count = frames.size();
if (gdb->type() != XDB)
{
// Invert list such that `Up' and `Down' make sense
for (i = 0; i < count / 2; i++)
{
string tmp = frame_list[i];
frame_list[i] = frame_list[count - i - 1];
frame_list[count - i - 1] = tmp;
}
}
// Make sure we have a minimum width
for (i = 0; i < count; i++)
{
selected[i] = false;
setup_where_line(frame_list[i]);
}
// JDB does not report frames above the current one. Hence, we
// only update the reported frames and others unchanged.
if (gdb->type() == JDB && jdb_frame() != 1)
updateLabelList(frame_list_w, frame_list, count);
else
setLabelList(frame_list_w, frame_list, selected, count, false, false);
set_frame_pos(0, 0);
delete[] selected;
}
// Give a hint whether we're showing earlier state
void SourceView::showing_earlier_state(bool set)
{
#if 0
set_sensitive(stack_dialog_w, !set);
set_sensitive(thread_dialog_w, !set);
set_sensitive(register_dialog_w, !set);
#endif
static bool up_state;
static bool down_state;
if (set)
{
up_state = XtIsSensitive(up_w);
down_state = XtIsSensitive(down_w);
set_sensitive(up_w, False);
set_sensitive(down_w, False);
}
else
{
set_sensitive(up_w, up_state);
set_sensitive(down_w, down_state);
}
set_sensitive(all_registers_w, !set);
set_sensitive(int_registers_w, !set);
refresh_buttons();
update_glyphs();
}
// Process `frame' (or `up'/`down') output
void SourceView::process_frame(string& frame_output)
{
if (frame_output != ""
&& (frame_output[0] == '#' || gdb->type() != GDB))
{
string frame_nr;
switch (gdb->type())
{
case GDB:
case PYDB:
frame_nr = frame_output.after(0);
break;
case DBX:
frame_nr = frame_output;
// Sun DBX 4.0 issues `=>' before current frame
if (frame_nr.contains("=>", 0))
frame_nr = frame_nr.after("=>");
break;
case XDB:
frame_nr = frame_output.after(" = ", -1);
break;
case JDB:
frame_nr = frame_output.after("[");
break;
case PERL:
{
// FIXME
break;
}
}
int frame = get_positive_nr(frame_nr);
// In GDB, the lowest frame is #0, in DBX and JDB, it is #1
if (gdb->type() == DBX || gdb->type() == JDB)
frame--;
process_frame(frame);
}
else
{
process_frame(-1); // No frame
}
}
// Same, but accept a frame number
void SourceView::process_frame(int frame)
{
if (frame >= 0)
{
at_lowest_frame = (frame == 0);
if (current_frame < 0)
{
// We have not seen a `frame' output yet - assume we're at
// the lowest frame.
current_frame = 0;
}
// Save undoing command
string c;
if (gdb->has_frame_command())
c = gdb->frame_command(current_frame);
else
c = gdb->relative_frame_command(current_frame - frame);
undo_buffer.add_command(c, true);
// Save state
undo_buffer.add_frame(itostring(frame));
int count = 0;
int top_item = 0;
int visible_items = 0;
XtVaGetValues(frame_list_w,
XmNitemCount, &count,
XmNtopItemPosition, &top_item,
XmNvisibleItemCount, &visible_items,
NULL);
int pos = 1;
switch (gdb->type())
{
case GDB:
case DBX:
case JDB:
case PYDB:
case PERL:
pos = count - frame;
break;
case XDB:
pos = frame + 1;
break;
}
ListSetAndSelectPos(frame_list_w, pos);
set_sensitive(up_w, pos > 1);
set_sensitive(down_w, pos < count);
refresh_buttons();
update_glyphs();
current_frame = frame;
}
else
{
set_sensitive(up_w, False);
set_sensitive(down_w, False);
refresh_buttons();
current_frame = -1;
}
}
// Set frame manually to function FUNC; return TRUE if successful
bool SourceView::set_frame_func(const string& func)
{
int count = 0;
XmStringTable items;
XtVaGetValues(frame_list_w,
XmNitemCount, &count,
XmNitems, &items,
NULL);
for (int i = count - 1; i >= 0; i--)
{
String _item;
XmStringGetLtoR(items[i], LIST_CHARSET, &_item);
string item(_item);
XtFree(_item);
int func_index = item.index(func);
int paren_index = item.index('(');
if (func_index >= 0 &&
(func_index < paren_index || paren_index < 0))
{
set_frame_pos(0, i + 1);
return true;
}
}
return false;
}
// Set frame manually: ARG = 0: POS, ARG = +/- N: down/up N levels
void SourceView::set_frame_pos(int arg, int pos)
{
if (frame_pos_locked)
{
frame_pos_locked = false;
return;
}
int items = 0;
XtVaGetValues(frame_list_w, XmNitemCount, &items, NULL);
if (pos == 0)
pos = items;
if (arg != 0)
{
int *position_list;
int position_count;
if (XmListGetSelectedPos(frame_list_w,
&position_list, &position_count))
{
if (position_count == 1)
pos = position_list[0] + arg;
XtFree((char *)position_list);
} else
return;
if (position_count != 1 || pos < 1 || pos > items)
return;
}
ListSetAndSelectPos(frame_list_w, pos);
last_frame_pos = pos;
set_sensitive(up_w, pos > 1);
set_sensitive(down_w, pos < items);
refresh_buttons();
}
bool SourceView::where_required() { return stack_dialog_popped_up; }
bool SourceView::register_required() { return register_dialog_popped_up; }
bool SourceView::thread_required() { return thread_dialog_popped_up; }
bool SourceView::can_go_up()
{
return gdb->relative_frame_command(1) != "" &&
(!where_required() || XtIsSensitive(up_w));
}
bool SourceView::can_go_down()
{
return gdb->relative_frame_command(-1) != "" &&
(!where_required() || XtIsSensitive(down_w));
}
//-----------------------------------------------------------------------------
// Register stuff
//----------------------------------------------------------------------------
void SourceView::process_registers(string& register_output)
{
register_output.gsub(";\n", "\n");
register_output.gsub("; ", "\n");
register_output.gsub(";", "\n");
register_output.gsub("\n\n", "\n");
if (!register_output.contains("No ", 0))
undo_buffer.add_registers(register_output);
int count = register_output.freq('\n') + 1;
string *register_list = new string[count];
bool *selected = new bool[count];
split(register_output, register_list, count, '\n');
while (count > 0 && register_list[count - 1] == "")
count--;
for (int i = 0; i < count; i++)
{
tabto(register_list[i], 26);
untabify_if_needed(register_list[i]);
selected[i] = false;
}
setLabelList(register_list_w, register_list, selected, count,
false, false);
delete[] register_list;
delete[] selected;
}
void SourceView::refresh_registers()
{
string registers = gdb_question(refresh_registers_command());
if (registers == NO_GDB_ANSWER)
registers = "No registers.";
process_registers(registers);
}
string SourceView::refresh_registers_command()
{
return gdb->regs_command(all_registers);
}
void SourceView::ViewRegistersCB(Widget, XtPointer, XtPointer)
{
refresh_registers();
manage_and_raise(register_dialog_w);
register_dialog_popped_up = true;
}
void SourceView::RegisterDialogPoppedDownCB (Widget, XtPointer, XtPointer)
{
register_dialog_popped_up = false;
}
void SourceView::SelectRegisterCB (Widget, XtPointer, XtPointer call_data)
{
XmListCallbackStruct *cbs = (XmListCallbackStruct *)call_data;
// Get the selected line
String _item;
XmStringGetLtoR(cbs->item, LIST_CHARSET, &_item);
string item(_item);
XtFree(_item);
if (item != "" && item[item.length() - 1] != '.')
{
item = "/x $" + item.through(rxalphanum);
source_arg->set_string(item);
}
}
//-----------------------------------------------------------------------------
// Thread stuff
//----------------------------------------------------------------------------
string SourceView::current_threadgroup = "system";
void SourceView::process_threads(string& threads_output)
{
bool valid_threads_output = true;
if (threads_output == NO_GDB_ANSWER
|| threads_output == ""
|| threads_output.contains("No ", 0)
|| threads_output.matches(rxwhite))
{
valid_threads_output = false;
threads_output = "No threads.\n";
}
if (valid_threads_output)
undo_buffer.add_threads(threads_output);
int count = threads_output.freq('\n') + 1;
string *thread_list = new string[count];
bool *selected = new bool[count];
split(threads_output, thread_list, count, '\n');
while (count > 0 && thread_list[count - 1] == "")
count--;
switch (gdb->type())
{
case GDB:
{
for (int i = 0; i < count; i++)
{
selected[i] = thread_list[i].contains('*', 0);
if (selected[i])
thread_list[i] = thread_list[i].after(0);
strip_leading_space(thread_list[i]);
setup_where_line(thread_list[i]);
}
break;
}
case JDB:
{
string current_thread = jdb_thread();
current_threadgroup = "";
for (int i = 0; i < count; i++)
{
selected[i] = false;
string& item = thread_list[i];
if (item.contains("Group ", 0))
{
// Format is: `Group THREADGROUP:'
if (current_threadgroup != "")
{
// Multiple threadgroups are shown
current_threadgroup = "system";
}
else
{
current_threadgroup = item.after(" ");
strip_leading_space(current_threadgroup);
current_threadgroup = current_threadgroup.before(":");
}
}
else
{
// Format is: ` NUMBER. (CLASS)ADDRESS NAME STATE'
int addr_index = item.index("(");
if (addr_index < 0)
addr_index = item.index("0x");
if (addr_index >= 0)
{
// If is the current thread, select it
string thread = item.after("(");
thread = thread.after(" ");
strip_leading_space(thread);
if (thread.contains(current_thread + " ", 0))
selected[i] = true;
// Leave only ` NUMBER. NAME STATE'
int info_index = addr_index;
while (info_index < int(item.length()) &&
item[info_index] != ' ')
info_index++;
while (info_index < int(item.length()) &&
item[info_index] == ' ')
info_index++;
item = item.before(addr_index) + item.from(info_index);
// Give more verbose output on system threads
if (item.contains("runni", -1))
item += "ng";
else if (item.contains("suspe", -1))
item += "nded";
else if (item.contains("cond.", -1))
item += " waiting";
}
}
}
break;
}
case DBX:
case XDB:
case PYDB:
case PERL:
{
for (int i = 0; i < count; i++)
selected[i] = false;
break;
}
}
setLabelList(thread_list_w, thread_list, selected, count, false, false);
delete[] thread_list;
delete[] selected;
}
void SourceView::refresh_threads(bool all_threadgroups)
{
switch (gdb->type())
{
case GDB:
{
string threads = gdb_question("info threads");
process_threads(threads);
break;
}
case JDB:
{
if (all_threadgroups)
{
// In JDB, `threadgroup system' seems to make `threads' list
// the threads of *all* threadgroups, not only system threads.
// This command will also automatically trigger an update.
gdb_command("threadgroup system");
syncCommandQueue();
}
string threads = gdb_question("threads");
process_threads(threads);
break;
}
case DBX:
case XDB:
case PYDB:
case PERL:
// No threads.
break;
}
}
void SourceView::ViewThreadsCB(Widget, XtPointer, XtPointer)
{
refresh_threads(true);
manage_and_raise(thread_dialog_w);
thread_dialog_popped_up = true;
}
void SourceView::ThreadDialogPoppedDownCB(Widget, XtPointer, XtPointer)
{
thread_dialog_popped_up = false;
}
void SourceView::ThreadCommandCB(Widget w, XtPointer client_data, XtPointer)
{
string command = (char *)client_data;
// Get the selected threads
IntArray threads;
getItemNumbers(thread_list_w, threads);
for (int i = 0; i < threads.size(); i++)
command += " " + itostring(threads[i]);
gdb_command(command, w);
}
void SourceView::SelectThreadCB(Widget w, XtPointer, XtPointer)
{
// Get the selected threads
IntArray threads;
getItemNumbers(thread_list_w, threads);
if (threads.size() == 1)
{
// Make single thread the default thread.
gdb_command("thread " + itostring(threads[0]), w);
}
else if (threads.size() == 0 && gdb->type() == JDB)
{
// Check if we have selected a threadgroup
XmStringTable selected_items;
int selected_items_count = 0;
XtVaGetValues(thread_list_w,
XmNselectedItemCount, &selected_items_count,
XmNselectedItems, &selected_items,
NULL);
if (selected_items_count == 1)
{
String _item;
XmStringGetLtoR(selected_items[0], LIST_CHARSET, &_item);
string item(_item);
XtFree(_item);
// Output has the form `Group jtest.main:'
if (item.contains("Group ", 0))
{
string threadgroup = item.after(" ");
strip_leading_space(threadgroup);
threadgroup = threadgroup.before(":");
if (threadgroup == current_threadgroup)
threadgroup = "system"; // show all threadgroups
gdb_command("threadgroup " + threadgroup, w);
}
}
}
}
//-----------------------------------------------------------------------------
// Get Line in GDB format
//----------------------------------------------------------------------------
string SourceView::get_line(string position)
{
string file_name = current_file_name;
if (position.contains(':'))
{
file_name = position.before(':');
position = position.after(':');
}
int line = get_positive_nr(position);
// Sanity check: make sure the line # isn't too big
line = min(line, line_count);
if (line < 1)
return "";
if (!is_current_file(file_name))
read_file(file_name, line);
if (!is_current_file(file_name))
return "";
XmTextPosition start = pos_of_line(line) + indent_amount(source_text_w);
XmTextPosition end = current_source.index('\n', start);
if (end < 0)
end = current_source.length();
string text = current_source.at(int(start), end - start);
return itostring(line) + "\t" + text;
}
//----------------------------------------------------------------------------
// Glyph stuff
//----------------------------------------------------------------------------
// Whether to cache glyph images
bool SourceView::cache_glyph_images = true;
// Change number of glyphs
void SourceView::set_max_glyphs (int nmax)
{
WidgetArray empty(nmax);
for (int k = 0; k < 2; k++)
{
int i;
// Destroy old widgets...
for (i = 0; i < plain_stops[k].size(); i++)
{
if (plain_stops[k][i] != 0)
XtDestroyWidget(plain_stops[k][i]);
}
for (i = 0; i < grey_stops[k].size(); i++)
{
if (grey_stops[k][i] != 0)
XtDestroyWidget(grey_stops[k][i]);
}
for (i = 0; i < plain_conds[k].size(); i++)
{
if (plain_conds[k][i] != 0)
XtDestroyWidget(plain_conds[k][i]);
}
for (i = 0; i < grey_conds[k].size(); i++)
{
if (grey_stops[k][i] != 0)
XtDestroyWidget(grey_conds[k][i]);
}
for (i = 0; i < plain_temps[k].size(); i++)
{
if (plain_temps[k][i] != 0)
XtDestroyWidget(plain_temps[k][i]);
}
for (i = 0; i < grey_temps[k].size(); i++)
{
if (grey_stops[k][i] != 0)
XtDestroyWidget(grey_temps[k][i]);
}
// ...make array empty...
plain_stops[k] = empty;
grey_stops[k] = empty;
plain_conds[k] = empty;
grey_conds[k] = empty;
plain_temps[k] = empty;
grey_temps[k] = empty;
// ...and make room for new widgets. The last one is a null pointer.
for (i = 0; i < nmax + 1; i++)
{
plain_stops[k] += Widget(0);
grey_stops[k] += Widget(0);
plain_conds[k] += Widget(0);
grey_conds[k] += Widget(0);
plain_temps[k] += Widget(0);
grey_temps[k] += Widget(0);
}
}
}
// Glyph has been activated - catch the double click in Motif 1.x
void SourceView::ActivateGlyphCB(Widget glyph, XtPointer, XtPointer call_data)
{
XmPushButtonCallbackStruct *cbs = (XmPushButtonCallbackStruct *)call_data;
XEvent *e = cbs->event;
if (e->type != ButtonRelease)
return;
String *params = { 0 };
XtCallActionProc(glyph, "source-drop-glyph", e, params, 0);
if (cbs->click_count > 1)
XtCallActionProc(glyph, "source-double-click", e, params, 0);
}
// Create a pixmap from BITS suitable for the widget W
Pixmap SourceView::pixmap(Widget w, unsigned char *bits, int width, int height)
{
Pixel foreground, background;
XtVaGetValues(w,
XmNforeground, &foreground,
XmNbackground, &background,
NULL);
int depth = PlanesOfScreen(XtScreen(w));
Pixmap pix = XCreatePixmapFromBitmapData(XtDisplay(w), XtWindow(w),
(char *)bits, width, height,
foreground, background, depth);
return pix;
}
// Create glyph in FORM_W named NAME from given BITS
Widget SourceView::create_glyph(Widget form_w,
String name,
unsigned char *bits,
int width, int height)
{
// Get background color from text
Pixel background;
Widget text_w;
if (form_w == code_form_w)
text_w = code_text_w;
else
text_w = source_text_w;
XtVaGetValues(text_w, XmNbackground, &background, NULL);
// Create push button
Arg args[30];
Cardinal arg = 0;
XtSetArg(args[arg], XmNmappedWhenManaged, False); arg++;
XtSetArg(args[arg], XmNtopAttachment, XmATTACH_FORM); arg++;
XtSetArg(args[arg], XmNleftAttachment, XmATTACH_FORM); arg++;
XtSetArg(args[arg], XmNrecomputeSize, False); arg++;
XtSetArg(args[arg], XmNmarginBottom, 0); arg++;
XtSetArg(args[arg], XmNmarginTop, 0); arg++;
XtSetArg(args[arg], XmNmarginLeft, 0); arg++;
XtSetArg(args[arg], XmNmarginRight, 0); arg++;
XtSetArg(args[arg], XmNmarginWidth, 0); arg++;
XtSetArg(args[arg], XmNmarginHeight, 0); arg++;
XtSetArg(args[arg], XmNshadowThickness, 0); arg++;
XtSetArg(args[arg], XmNhighlightThickness, 0); arg++;
XtSetArg(args[arg], XmNborderWidth, 0); arg++;
XtSetArg(args[arg], XmNlabelType, XmPIXMAP); arg++;
XtSetArg(args[arg], XmNmultiClick, XmMULTICLICK_KEEP); arg++;
XtSetArg(args[arg], XmNalignment, XmALIGNMENT_BEGINNING); arg++;
XtSetArg(args[arg], XmNuserData, XtPointer(0)); arg++;
XtSetArg(args[arg], XmNfillOnArm, True); arg++;
XtSetArg(args[arg], XmNarmColor, background); arg++;
XtSetArg(args[arg], XmNbackground, background); arg++;
Widget w = verify(XmCreatePushButton(form_w, name, args, arg));
if (XtIsRealized(form_w))
XtRealizeWidget(w);
XtManageChild(w);
arg = 0;
if (!cache_glyph_images)
{
Pixmap pix = pixmap(w, bits, width, height);
XtSetArg(args[arg], XmNlabelPixmap, pix); arg++;
}
XtSetArg(args[arg], XmNwidth, width); arg++;
XtSetArg(args[arg], XmNheight, height); arg++;
XtSetValues(w, args, arg);
XtAddCallback(w, XmNactivateCallback, ActivateGlyphCB, 0);
InstallButtonTips(w);
return w;
}
// Return height of a single line
int SourceView::line_height(Widget text_w)
{
static int source_height = 0;
static int code_height = 0;
if (text_w == source_text_w && source_height > 0)
return source_height;
else if (text_w == code_text_w && code_height > 0)
return code_height;
bool ok;
XmTextPosition top = XmTextGetTopCharacter(text_w);
Position top_x, top_y;
ok = XmTextPosToXY(text_w, top, &top_x, &top_y);
if (!ok)
return 0;
string& text = current_text(text_w);
XmTextPosition second = text.index('\n', top) + 1;
Position second_x, second_y;
ok = XmTextPosToXY(text_w, second, &second_x, &second_y);
if (!ok)
return 0;
int height = abs(second_y - top_y);
if (text_w == source_text_w)
source_height = height;
else if (text_w == code_text_w)
code_height = height;
return height;
}
// If false, don't change glyphs - just check if they would change
bool SourceView::change_glyphs = true;
// All glyphs that have changed during update_glyphs_now()
WidgetArray SourceView::changed_glyphs;
// Unmap glyph W
void SourceView::unmap_glyph(Widget glyph)
{
if (glyph == 0)
return;
assert(is_code_widget(glyph) || is_source_widget(glyph));
XtPointer user_data;
XtVaGetValues(glyph, XmNuserData, &user_data, NULL);
if (user_data == 0)
return; // Already unmapped
if (change_glyphs)
{
const Position invisible_x = -100;
const Position invisible_y = -100;
// Unmapping the glyph while dragging breaks the drag.
// Move the glyph to an invisible position instead.
XtVaSetValues(glyph,
XmNleftOffset, invisible_x,
XmNtopOffset, invisible_y,
XmNuserData, XtPointer(0),
NULL);
if (lesstif_version <= 85)
{
// LessTif 0.84 and earlier wants it the hard way.
XtMoveWidget(glyph, invisible_x, invisible_y);
}
log_glyph(glyph);
}
changed_glyphs += glyph;
}
// Map glyph GLYPH in (X, Y)
void SourceView::map_glyph(Widget& glyph, Position x, Position y)
{
while (glyph == 0)
CreateGlyphsWorkProc(0);
assert(is_code_widget(glyph) || is_source_widget(glyph));
Widget text_w;
if (is_source_widget(glyph))
text_w = source_text_w;
else
text_w = code_text_w;
XtPointer user_data;
Dimension height = 0;
Dimension border_width = 0;
Dimension margin_height = 0;
Dimension shadow_thickness = 0;
Dimension highlight_thickness = 0;
int old_x = 0;
int old_y = 0;
XtVaGetValues(glyph,
XmNheight, &height,
XmNborderWidth, &border_width,
XmNmarginHeight, &margin_height,
XmNshadowThickness, &shadow_thickness,
XmNhighlightThickness, &highlight_thickness,
XmNuserData, &user_data,
XmNleftOffset, &old_x,
XmNtopOffset, &old_y,
NULL);
Dimension glyph_height =
height + border_width + margin_height
+ shadow_thickness + highlight_thickness;
y -= (line_height(text_w) + glyph_height) / 2 - 2;
if (lesstif_version < 1000)
x += 2;
if (x != old_x || y != old_y)
{
if (change_glyphs)
{
if (lesstif_version <= 84)
{
// LessTif 0.84 and earlier want it the hard way.
XtMoveWidget(glyph, x, y);
}
XtVaSetValues(glyph, XmNleftOffset, x, XmNtopOffset, y, NULL);
log_glyph(glyph);
}
changed_glyphs += glyph;
}
if (user_data != 0)
return; // Already mapped
if (change_glyphs)
{
XtMapWidget(glyph);
XtVaSetValues(glyph, XmNuserData, XtPointer(1), NULL);
log_glyph(glyph);
changed_glyphs += glyph;
}
}
// True if code/source glyphs need to be updated
bool SourceView::update_code_glyphs = false;
bool SourceView::update_source_glyphs = false;
// Update glyphs for widget GLYPH (0: all)
void SourceView::update_glyphs(Widget glyph)
{
static XtWorkProcId update_glyph_id = 0;
if (glyph == 0)
update_source_glyphs = update_code_glyphs = true;
else if (is_source_widget(glyph))
update_source_glyphs = true;
else if (is_code_widget(glyph))
update_code_glyphs = true;
if (update_glyph_id != 0)
XtRemoveTimeOut(update_glyph_id);
// Chris van Engelen reports:
// When reading in a new file, there is an infinite loop involving
// function SourceView::UpdateGlyphsWorkProc: function
// XtAppPending always returns a value indicating that there are
// events pending, and since there always is at least one glyph to
// be updated (the execution position arrow), the function returns
// with a new call to UpdateGlyphsWorkProc scheduled. This problem
// was solved by increasing the delay time for the first
// scheduling to 50ms.
update_glyph_id =
XtAppAddTimeOut(XtWidgetToApplicationContext(source_text_w), 50,
UpdateGlyphsWorkProc, XtPointer(&update_glyph_id));
}
// Invoked by scrolling keys
void SourceView::updateGlyphsAct(Widget w, XEvent*, String *, Cardinal *)
{
CheckScrollCB(w, 0, 0);
}
// Invoked whenever the text widget may be about to scroll
void SourceView::CheckScrollCB(Widget, XtPointer, XtPointer)
{
static XtIntervalId check_scroll_id = 0;
if (check_scroll_id != 0)
{
XtRemoveTimeOut(check_scroll_id);
check_scroll_id = 0;
}
check_scroll_id =
XtAppAddTimeOut(XtWidgetToApplicationContext(source_text_w),
app_data.glyph_update_delay,
CheckScrollWorkProc, XtPointer(&check_scroll_id));
}
void SourceView::CheckScrollWorkProc(XtPointer client_data, XtIntervalId *id)
{
(void) id; // Use it
XtIntervalId *timer = (XtIntervalId *)client_data;
if (timer != 0)
{
assert(*timer == *id);
*timer = 0;
}
XmTextPosition old_top = last_top;
last_top = XmTextGetTopCharacter(source_text_w);
XmTextPosition old_top_pc = last_top_pc;
last_top_pc = XmTextGetTopCharacter(code_text_w);
if (old_top != last_top && old_top_pc != last_top_pc)
update_glyphs();
else if (old_top != last_top)
update_glyphs(source_text_w);
else if (old_top_pc != last_top_pc)
update_glyphs(code_text_w);
}
// Pixel offsets
// Horizontal arrow offset (pixels)
int SourceView::arrow_x_offset = -5;
// Horizontal breakpoint symbol offset (pixels)
int SourceView::stop_x_offset = +6;
// Additional offset for multiple breakpoints (pixels)
int SourceView::multiple_stop_x_offset = stop_width;
// Glyph locations: x[0] is source, x[1] is code
Widget SourceView::plain_arrows[2] = {0, 0};
Widget SourceView::grey_arrows[2] = {0, 0};
Widget SourceView::past_arrows[2] = {0, 0};
Widget SourceView::signal_arrows[2] = {0, 0};
Widget SourceView::drag_arrows[2] = {0, 0};
Widget SourceView::drag_stops[2] = {0, 0};
Widget SourceView::drag_conds[2] = {0, 0};
Widget SourceView::drag_temps[2] = {0, 0};
WidgetArray SourceView::plain_stops[2];
WidgetArray SourceView::grey_stops[2];
WidgetArray SourceView::plain_conds[2];
WidgetArray SourceView::grey_conds[2];
WidgetArray SourceView::plain_temps[2];
WidgetArray SourceView::grey_temps[2];
// Create glyphs in the background
Boolean SourceView::CreateGlyphsWorkProc(XtPointer)
{
int k;
for (k = 0; k < 2; k++)
{
// On the Form widget, later children are displayed on top of
// earlier children. A stop sign hiding an arrow gives more
// pleasing results than vice-versa, so place arrow glyph
// below sign glyphs.
Widget form_w = k ? code_form_w : source_form_w;
if (form_w == 0)
continue;
if (past_arrows[k] == 0)
{
past_arrows[k] =
create_glyph(form_w, "past_arrow",
past_arrow_bits,
past_arrow_width,
past_arrow_height);
return False;
}
if (plain_arrows[k] == 0)
{
plain_arrows[k] =
create_glyph(form_w, "plain_arrow",
arrow_bits,
arrow_width,
arrow_height);
return False;
}
if (grey_arrows[k] == 0)
{
grey_arrows[k] =
create_glyph(form_w, "grey_arrow",
grey_arrow_bits,
grey_arrow_width,
grey_arrow_height);
return False;
}
if (signal_arrows[k] == 0)
{
signal_arrows[k] =
create_glyph(form_w, "signal_arrow",
signal_arrow_bits,
signal_arrow_width,
signal_arrow_height);
return False;
}
if (drag_arrows[k] == 0)
{
drag_arrows[k] =
create_glyph(form_w, "drag_arrow",
drag_arrow_bits,
drag_arrow_width,
drag_arrow_height);
return False;
}
}
for (k = 0; k < 2; k++)
{
Widget form_w = k ? code_form_w : source_form_w;
if (form_w == 0)
continue;
int i;
for (i = 0; i < plain_stops[k].size() - 1; i++)
{
if (plain_stops[k][i] == 0)
{
plain_stops[k][i] =
create_glyph(form_w, "plain_stop",
stop_bits,
stop_width,
stop_height);
return False;
}
}
for (i = 0; i < plain_temps[k].size() - 1; i++)
{
if (plain_temps[k][i] == 0)
{
plain_temps[k][i] =
create_glyph(form_w, "plain_temp",
temp_bits,
temp_width,
temp_height);
return False;
}
}
for (i = 0; i < plain_conds[k].size() - 1; i++)
{
if (plain_conds[k][i] == 0)
{
plain_conds[k][i] =
create_glyph(form_w, "plain_cond",
cond_bits,
cond_width,
cond_height);
return False;
}
}
for (i = 0; i < grey_stops[k].size() - 1; i++)
{
if (grey_stops[k][i] == 0)
{
grey_stops[k][i] =
create_glyph(form_w, "grey_stop",
grey_stop_bits,
grey_stop_width,
grey_stop_height);
return False;
}
}
for (i = 0; i < grey_temps[k].size() - 1; i++)
{
if (grey_temps[k][i] == 0)
{
grey_temps[k][i] =
create_glyph(form_w, "grey_temp",
grey_temp_bits,
grey_temp_width,
grey_temp_height);
return False;
}
}
for (i = 0; i < grey_conds[k].size() - 1; i++)
{
if (grey_conds[k][i] == 0)
{
grey_conds[k][i] =
create_glyph(form_w, "grey_cond",
grey_cond_bits,
grey_cond_width,
grey_cond_height);
return False;
}
}
if (drag_stops[k] == 0)
{
drag_stops[k] =
create_glyph(form_w, "drag_stop",
drag_stop_bits,
drag_stop_width,
drag_stop_height);
return False;
}
if (drag_temps[k] == 0)
{
drag_temps[k] =
create_glyph(form_w, "drag_temp",
drag_temp_bits,
drag_temp_width,
drag_temp_height);
return False;
}
if (drag_conds[k] == 0)
{
drag_conds[k] =
create_glyph(form_w, "drag_cond",
drag_cond_bits,
drag_cond_width,
drag_cond_height);
return False;
}
}
return True; // all done
}
// Map stop sign GLYPH at position POS. Get widget from STOPS[COUNT];
// store location in POSITIONS. Return mapped widget (0 if none)
Widget SourceView::map_stop_at(Widget glyph, XmTextPosition pos,
WidgetArray& stops, int& count,
TextPositionArray& positions)
{
assert (is_source_widget(glyph) || is_code_widget(glyph));
Position x, y;
Boolean pos_displayed = XmTextPosToXY(glyph, pos, &x, &y);
if (pos_displayed)
{
while (stops[count] == 0)
{
if (CreateGlyphsWorkProc(0))
break;
}
Widget glyph = stops[count] ? stops[count++] : 0;
if (glyph != 0)
{
for (int i = 0; i < positions.size(); i++)
if (pos == positions[i])
x += multiple_stop_x_offset;
map_glyph(glyph, x + stop_x_offset, y);
positions += pos;
return glyph;
}
else
{
// Max number of glyphs exceeded
string msg = "Out of glyphs (used " +
itostring(stops.size() - 1) + " of " +
itostring(stops.size() - 1) + ")";
set_status(msg);
static bool warning_posted = false;
if (!warning_posted)
{
post_warning(msg, "out_of_glyphs_warning", glyph);
warning_posted = true;
}
}
}
return 0;
}
// Map arrow in GLYPH at POS. Return mapped arrow widget (0 if none)
Widget SourceView::map_arrow_at(Widget glyph, XmTextPosition pos)
{
assert (is_source_widget(glyph) || is_code_widget(glyph));
Position x, y;
Boolean pos_displayed = (pos != XmTextPosition(-1)
&& XmTextPosToXY(glyph, pos, &x, &y));
int k = int(is_code_widget(glyph));
Widget& signal_arrow = signal_arrows[k];
Widget& plain_arrow = plain_arrows[k];
Widget& grey_arrow = grey_arrows[k];
Widget& past_arrow = past_arrows[k];
while (signal_arrow == 0 || plain_arrow == 0 ||
grey_arrow == 0 || past_arrow == 0)
{
if (CreateGlyphsWorkProc(0))
break;
}
if (pos_displayed)
{
if (undo_buffer.showing_earlier_state())
{
map_glyph(past_arrow, x + arrow_x_offset, y);
unmap_glyph(grey_arrow);
unmap_glyph(signal_arrow);
unmap_glyph(plain_arrow);
return past_arrow;
}
else if (at_lowest_frame && signal_received)
{
map_glyph(signal_arrow, x + arrow_x_offset, y);
unmap_glyph(plain_arrow);
unmap_glyph(grey_arrow);
unmap_glyph(past_arrow);
return signal_arrow;
}
else if (at_lowest_frame)
{
map_glyph(plain_arrow, x + arrow_x_offset, y);
unmap_glyph(signal_arrow);
unmap_glyph(grey_arrow);
unmap_glyph(past_arrow);
return plain_arrow;
}
else
{
map_glyph(grey_arrow, x + arrow_x_offset, y);
unmap_glyph(signal_arrow);
unmap_glyph(plain_arrow);
unmap_glyph(past_arrow);
return grey_arrow;
}
}
else
{
unmap_glyph(signal_arrow);
unmap_glyph(plain_arrow);
unmap_glyph(grey_arrow);
unmap_glyph(past_arrow);
}
return 0;
}
// Copy glyph foreground and background colors from ORIGIN to GLYPH
void SourceView::copy_colors(Widget glyph, Widget origin)
{
if (origin == 0)
return;
Pixel background, foreground;
XtVaGetValues(origin,
XmNforeground, &foreground,
XmNbackground, &background,
NULL);
Pixmap pixmap =
XmGetPixmap(XtScreen(glyph), XtName(glyph), foreground, background);
if (pixmap != XmUNSPECIFIED_PIXMAP)
{
Pixmap old_pixmap;
XtVaGetValues(glyph, XmNlabelPixmap, &old_pixmap, NULL);
XmDestroyPixmap(XtScreen(glyph), old_pixmap);
XtVaSetValues(glyph, XmNlabelPixmap, pixmap, NULL);
}
}
// Map temporary stop sign at position POS. If ORIGIN is given, use
// colors from ORIGIN.
Widget SourceView::map_drag_stop_at(Widget glyph, XmTextPosition pos,
Widget origin)
{
assert (is_source_widget(glyph) || is_code_widget(glyph));
Position x, y;
Boolean pos_displayed =
(pos != XmTextPosition(-1) && XmTextPosToXY(glyph, pos, &x, &y));
int k = int(is_code_widget(glyph));
if (pos_displayed)
{
bool cond = (origin != 0 && string(XtName(origin)).contains("cond"));
bool temp = (origin != 0 && string(XtName(origin)).contains("temp"));
Widget& drag_stop =
temp ? drag_temps[k] :
cond ? drag_conds[k] :
drag_stops[k];
while (drag_stop == 0)
{
if (CreateGlyphsWorkProc(0))
break;
}
copy_colors(drag_stop, origin);
if (origin != 0)
{
static Position last_x = x + stop_x_offset;
Position origin_x = -1;
XtVaGetValues(origin, XmNx, &origin_x, NULL);
if (lesstif_version < 1000)
origin_x -= 2;
if (origin_x >= 0)
{
// Origin is mapped
x = last_x = origin_x;
}
else
{
// Origin is unmapped - use last recorded value
x = last_x;
}
}
else
{
x += stop_x_offset;
}
map_glyph(drag_stop, x, y);
if (temp)
{
unmap_glyph(drag_conds[k]);
unmap_glyph(drag_stops[k]);
}
else if (cond)
{
unmap_glyph(drag_temps[k]);
unmap_glyph(drag_stops[k]);
}
else
{
unmap_glyph(drag_conds[k]);
unmap_glyph(drag_temps[k]);
}
return drag_stop;
}
else
{
unmap_glyph(drag_conds[k]);
unmap_glyph(drag_temps[k]);
unmap_glyph(drag_stops[k]);
return 0;
}
}
// Map temporary arrow at position POS. If ORIGIN is given, use
// colors from ORIGIN.
Widget SourceView::map_drag_arrow_at(Widget glyph, XmTextPosition pos,
Widget origin)
{
assert (is_source_widget(glyph) || is_code_widget(glyph));
Position x, y;
Boolean pos_displayed = (pos != XmTextPosition(-1)
&& XmTextPosToXY(glyph, pos, &x, &y));
int k = int(is_code_widget(glyph));
Widget& drag_arrow = drag_arrows[k];
while (drag_arrow == 0)
{
if (CreateGlyphsWorkProc(0))
break;
}
copy_colors(drag_arrow, origin);
if (pos_displayed)
map_glyph(drag_arrow, x + arrow_x_offset, y);
else
unmap_glyph(drag_arrow);
return drag_arrow;
}
// Update glyphs after interval
void SourceView::UpdateGlyphsWorkProc(XtPointer client_data, XtIntervalId *id)
{
(void) id; // Use it
// Allow new invocations
XtIntervalId *proc_id = ((XtIntervalId *) client_data);
if (proc_id != 0)
{
assert(*proc_id == *id);
*proc_id = 0;
}
XtAppContext app_context = XtWidgetToApplicationContext(source_text_w);
if (XtAppPending(app_context) & (XtIMXEvent | XtIMAlternateInput))
{
// Other events pending - check if we shall change something
const WidgetArray& glyphs = glyphs_to_be_updated();
if (glyphs.size() > 0)
{
// Change is imminent - unmap all glyphs that will change
// and try again in 50ms
for (int i = 0; i < glyphs.size(); i++)
unmap_glyph(glyphs[i]);
XtIntervalId new_id =
XtAppAddTimeOut(app_context, 50,
UpdateGlyphsWorkProc, client_data);
if (proc_id != 0)
*proc_id = new_id;
return;
}
}
change_glyphs = true;
update_glyphs_now();
}
// The function that does the real work
void SourceView::update_glyphs_now()
{
// clog << "Updating glyphs...";
WidgetArray empty;
changed_glyphs = empty;
if (update_source_glyphs)
{
// Show current execution position
XmTextPosition pos = XmTextPosition(-1);
if (display_glyphs &&
(is_current_file(last_execution_file) ||
base_matches(last_execution_file, current_file_name)) &&
line_count > 0 &&
last_execution_line > 0 &&
last_execution_line <= line_count)
{
pos = pos_of_line(last_execution_line);
}
map_arrow_at(source_text_w, pos);
}
if (update_code_glyphs)
{
// Show current PC
XmTextPosition pos = XmTextPosition(-1);
if (display_glyphs && last_execution_pc != "")
pos = find_pc(last_execution_pc);
map_arrow_at(code_text_w, pos);
}
// Map breakpoint glyphs
for (int k = 0; k < 2; k++)
{
if (k == 0 && !update_source_glyphs)
continue;
if (k == 1 && !update_code_glyphs)
continue;
int plain_stops_count = 0;
int grey_stops_count = 0;
int plain_conds_count = 0;
int grey_conds_count = 0;
int plain_temps_count = 0;
int grey_temps_count = 0;
if (display_glyphs)
{
TextPositionArray positions;
MapRef ref;
for (BreakPoint *bp = bp_map.first(ref);
bp != 0;
bp = bp_map.next(ref))
{
Widget& bp_glyph = k ? bp->code_glyph() : bp->source_glyph();
Widget text_w = k ? code_text_w : source_text_w;
bp_glyph = 0;
XmTextPosition pos;
if (k == 0)
{
// Find source position
if (!bp_matches(bp)
|| line_count <= 0
|| bp->line_nr() <= 0
|| bp->line_nr() > line_count)
continue;
pos = pos_of_line(bp->line_nr());
}
else
{
// Find code position
if (bp->type() != BREAKPOINT)
continue;
pos = find_pc(bp->address());
}
if (bp->dispo() != BPKEEP)
{
// Temporary breakpoint
if (bp->enabled())
bp_glyph = map_stop_at(text_w, pos, plain_temps[k],
plain_temps_count, positions);
else
bp_glyph = map_stop_at(text_w, pos, grey_temps[k],
grey_temps_count, positions);
}
else if (bp->condition() != "" || bp->ignore_count() != 0)
{
// Conditional breakpoint
if (bp->enabled())
bp_glyph = map_stop_at(text_w, pos, plain_conds[k],
plain_conds_count, positions);
else
bp_glyph = map_stop_at(text_w, pos, grey_conds[k],
grey_conds_count, positions);
}
else
{
// Orindary breakpoint
if (bp->enabled())
bp_glyph = map_stop_at(text_w, pos, plain_stops[k],
plain_stops_count, positions);
else
bp_glyph = map_stop_at(text_w, pos, grey_stops[k],
grey_stops_count, positions);
}
}
}
// Unmap remaining breakpoint glyphs
Widget glyph;
while ((glyph = plain_stops[k][plain_stops_count++]))
unmap_glyph(glyph);
while ((glyph = grey_stops[k][grey_stops_count++]))
unmap_glyph(glyph);
while ((glyph = plain_conds[k][plain_conds_count++]))
unmap_glyph(glyph);
while ((glyph = grey_conds[k][grey_conds_count++]))
unmap_glyph(glyph);
while ((glyph = plain_temps[k][plain_temps_count++]))
unmap_glyph(glyph);
while ((glyph = grey_temps[k][grey_temps_count++]))
unmap_glyph(glyph);
}
if (change_glyphs)
{
update_source_glyphs = false;
update_code_glyphs = false;
}
// clog << "done.\n";
}
// Return all glyphs that would change
const WidgetArray& SourceView::glyphs_to_be_updated()
{
change_glyphs = false;
update_glyphs_now();
change_glyphs = true;
// clog << "Glyphs to be updated:";
// for (int i = 0; i < changed_glyphs.size(); i++)
// clog << " " << XtName(changed_glyphs[i]);
// clog << "\n";
return changed_glyphs;
}
// Change setting of display_glyphs
void SourceView::set_display_glyphs(bool set)
{
if (display_glyphs != set)
{
// Save current execution position
string file = last_execution_file;
int line = last_execution_line;
string pc = last_execution_pc;
bool stopped = at_lowest_frame;
bool signaled = signal_received;
if (XtIsRealized(source_text_w))
{
display_glyphs = false;
show_execution_position();
UpdateGlyphsWorkProc(0, 0);
display_glyphs = true;
refresh_bp_disp();
}
display_glyphs = set;
if (XtIsRealized(source_text_w))
{
StatusDelay delay(set ? "Enabling glyphs" : "Disabling glyphs");
refresh_bp_disp();
if (file != "")
show_execution_position(file + ":" + itostring(line),
stopped, signaled);
if (pc != "")
show_pc(pc, XmHIGHLIGHT_SELECTED);
}
}
}
// Change setting of display_line_numbers
void SourceView::set_display_line_numbers(bool set)
{
if (display_line_numbers != set)
{
display_line_numbers = set;
if (XtIsRealized(source_text_w))
{
StatusDelay delay(set ? "Enabling line numbers" :
"Disabling line numbers");
reload();
}
}
}
// Return help on a glyph
MString SourceView::help_on_glyph(Widget glyph, bool detailed)
{
XmTextPosition dummy;
return help_on_pos(glyph, 0, dummy, detailed);
}
// Return help on a breakpoint position
MString SourceView::help_on_pos(Widget w, XmTextPosition pos,
XmTextPosition& ref, bool detailed)
{
if (w == 0)
return MString(0, true);
int line_nr;
bool in_text;
int bp_nr;
string address;
bool pos_found = get_line_of_pos(w, pos, line_nr, address, in_text, bp_nr);
if (!pos_found || bp_nr == 0)
return MString(0, true);
ref = pos_of_line(line_nr) + 2;
return help_on_bp(bp_nr, detailed);
}
// Return help on a glyph
MString SourceView::help_on_bp(int bp_nr, bool detailed)
{
BreakPoint *bp = bp_map.get(bp_nr);
if (bp == 0)
return MString(0, true);
MString info;
switch (bp->type())
{
case BREAKPOINT:
info = rm("Breakpoint ");
break;
case WATCHPOINT:
info = rm("Watchpoint ");
break;
}
info += tt(itostring(bp->number()));
if (detailed)
{
if (bp->enabled())
info += rm(" (enabled");
else
info += rm(" (disabled");
string infos = bp->infos();
strip_space(infos);
infos.gsub("\n", "; ");
if (bp->infos() != "")
info += rm("; " + infos);
switch (bp->dispo())
{
case BPKEEP:
break;
case BPDEL:
info += rm("; delete when hit");
break;
case BPDIS:
info += rm("; disable when hit");
break;
}
info += rm(")");
}
return info;
}
// Glyph drag & drop
XmTextPosition SourceView::glyph_position(Widget glyph, XEvent *e,
bool normalize)
{
Widget text_w;
if (is_source_widget(glyph))
text_w = source_text_w;
else if (is_code_widget(glyph))
text_w = code_text_w;
else
return XmTextPosition(-1);
BoxPoint p = point(e);
if (glyph != source_text_w && glyph != code_text_w)
{
// Called from a glyph: add glyph position to event position
translate_glyph_pos(glyph, text_w, p[X], p[Y]);
}
// Get the position
XmTextPosition pos = XmTextXYToPos(text_w, p[X], p[Y]);
// Stay within viewable text +/-1 row, such that we don't scroll too fast
short rows = 0;
XmTextPosition current_top = 0;
XtVaGetValues(text_w,
XmNrows, &rows,
XmNtopCharacter, ¤t_top,
NULL);
const string& text = current_text(text_w);
XmTextPosition current_bottom = current_top;
while (current_bottom < int(text.length()) && rows > 0)
if (text[current_bottom++] == '\n')
rows--;
if (pos < current_top)
pos = max(current_top - 1, 0);
else if (pos > current_bottom)
pos = min(current_bottom + 1, text.length());
if (normalize)
{
const string& text = current_text(glyph);
pos = min(pos, text.length());
while (pos > 0 && text[pos - 1] != '\n')
pos--;
}
return pos;
}
// Data associated with current drag operation
// The Glyph being dragged
Widget SourceView::current_drag_origin = 0;
// The breakpoint being dragged, or 0 if execution position
int SourceView::current_drag_breakpoint = -1;
void SourceView::dragGlyphAct(Widget glyph, XEvent *e, String *params,
Cardinal *num_params)
{
if (e->type != ButtonPress && e->type != ButtonRelease)
return;
Widget text_w;
if (is_source_widget(glyph))
text_w = source_text_w;
else if (is_code_widget(glyph))
text_w = code_text_w;
else
return; // Bad widget
if (!XtIsRealized(text_w))
return;
// Move cursor to glyph position
XButtonEvent *event = &e->xbutton;
translate_glyph_pos(glyph, text_w, event->x, event->y);
event->window = XtWindow(text_w);
XtCallActionProc(text_w, "source-start-select-word", e,
params, *num_params);
// Check for double clicks
XtCallActionProc(text_w, "source-double-click", e,
params, *num_params);
// Now start the drag
int k;
for (k = 0; k < 2; k++)
{
if (glyph == grey_arrows[k] || glyph == past_arrows[k])
{
// Cannot drag last execution position
return;
}
else if (glyph == plain_arrows[k])
{
if (!gdb->has_jump_command() && !gdb->has_assign_command())
{
// Execution position cannot be dragged
return;
}
}
else if (glyph == drag_stops[k] ||
glyph == drag_conds[k] ||
glyph == drag_temps[k] ||
glyph == drag_arrows[k])
{
// Temp glyph cannot be dragged
return;
}
}
static Cursor move_cursor = XCreateFontCursor(XtDisplay(glyph), XC_fleur);
// clog << "Dragging " << XtName(glyph) << " [" << glyph << "]\n";
XDefineCursor(XtDisplay(glyph), XtWindow(glyph), move_cursor);
unmap_drag_stop(text_w);
unmap_drag_arrow(text_w);
current_drag_origin = glyph;
current_drag_breakpoint = 0;
// Check for breakpoint
MapRef ref;
for (BreakPoint *bp = bp_map.first(ref); bp != 0; bp = bp_map.next(ref))
{
if (glyph == bp->source_glyph() || glyph == bp->code_glyph())
{
current_drag_breakpoint = bp->number();
break;
}
}
}
void SourceView::followGlyphAct(Widget glyph, XEvent *e, String *, Cardinal *)
{
if (glyph != current_drag_origin)
return;
Widget text_w;
if (is_source_widget(glyph))
text_w = source_text_w;
else if (is_code_widget(glyph))
text_w = code_text_w;
else
return; // Bad widget
// Protect against more than one invocation per 50ms.
static Time last_time = 0;
if (time(e) - last_time < 50)
return;
last_time = time(e);
XmTextPosition pos = glyph_position(glyph, e);
// Make sure we see the position
ShowPosition(text_w, pos);
// Update glyphs in case we had to scroll
CheckScrollCB(glyph, XtPointer(0), XtPointer(0));
if (current_drag_breakpoint)
map_drag_stop_at(text_w, pos, glyph);
else
map_drag_arrow_at(text_w, pos, glyph);
}
void SourceView::dropGlyphAct (Widget glyph, XEvent *e,
String *params, Cardinal *num_params)
{
if (e->type != ButtonPress && e->type != ButtonRelease)
return;
if (glyph != current_drag_origin)
return;
Widget text_w;
if (is_source_widget(glyph))
text_w = source_text_w;
else if (is_code_widget(glyph))
text_w = code_text_w;
else
return; // Bad widget
if (!XtIsRealized(text_w))
return;
XUndefineCursor(XtDisplay(glyph), XtWindow(glyph));
// Unmap temp glyphs
unmap_drag_stop(text_w);
unmap_drag_arrow(text_w);
// Show all other glyphs
update_glyphs();
int k;
for (k = 0; k < 2; k++)
if (glyph == grey_arrows[k] ||
glyph == past_arrows[k] ||
glyph == drag_stops[k] ||
glyph == drag_conds[k] ||
glyph == drag_temps[k] ||
glyph == drag_arrows[k])
return;
XmTextPosition pos = glyph_position(glyph, e);
if (pos == XmTextPosition(-1))
return; // No position
int line_nr = 0;
bool in_text;
int bp_nr;
string address;
if (!get_line_of_pos(text_w, pos, line_nr, address, in_text, bp_nr))
return; // No location
if (text_w == code_text_w)
{
// Selection from code
if (address == "")
return; // No address
}
else
{
// Selection from source
if (line_nr == 0)
return; // No line
address = current_source_name() + ':' + itostring(line_nr);
}
// clog << "Dropping " << XtName(glyph) << " [" << glyph << "] at "
// << address << "\n";
if (text_w == code_text_w)
{
// Selection from code
if (address == "")
return; // No address
address = string('*') + address;
}
else
{
// Selection from source
if (line_nr == 0)
return; // No line
address = current_source_name() + ':' + itostring(line_nr);
}
string p = "move";
if (num_params != 0 && *num_params == 1)
p = params[0];
if (num_params != 0 && *num_params > 1)
cerr << "source-drop-glyph: too many parameters\n";
p.downcase();
bool copy = false;
if (p == "move")
copy = false;
else if (p == "copy")
copy = true;
else
cerr << "source-drop-glyph: unknown parameter " << quote(p) << "\n";
bool changed = false;
if (current_drag_breakpoint)
{
// Move breakpoint
changed = move_bp(current_drag_breakpoint, address, text_w, copy);
}
else
{
// Move exec pos
changed = move_pc(address, text_w);
}
if (changed)
{
// Make sure this position is kept visible
SetInsertionPosition(text_w, pos);
}
current_drag_origin = 0;
current_drag_breakpoint = 0;
}
// Report glyph state (for debugging)
void SourceView::log_glyph(Widget glyph, int n)
{
(void) n; // Use it
(void) glyph;
#if LOG_GLYPHS
if (glyph == 0)
return;
int left = 0;
int top = 0;
Position x = 0;
Position y = 0;
XtPointer user_data;
XtVaGetValues(glyph,
XmNuserData, &user_data,
XmNleftOffset, &left,
XmNtopOffset, &top,
XmNx, &x,
XmNy, &y,
NULL);
clog << XtName(glyph);
if (n >= 0)
clog << "s[" << n << "]";
clog << ": ";
if (user_data)
clog << "mapped";
else
clog << "unmapped";
clog << " at (" << left << ", " << top << " / "
<< x << ", " << y << ")\n";
#endif
}
void SourceView::log_glyphs()
{
#if LOG_GLYPHS
for (int k = 0; k < 2; k++)
{
if (k && !disassemble)
continue;
if (k == 0)
clog << "Source glyphs:\n";
else
clog << "\nCode glyphs:\n";
int i;
for (i = 0; i < plain_stops[k].size() - 1; i++)
log_glyph(plain_stops[k][i], i);
for (i = 0; i < grey_stops[k].size() - 1; i++)
log_glyph(grey_stops[k][i], i);
for (i = 0; i < plain_conds[k].size() - 1; i++)
log_glyph(plain_conds[k][i], i);
for (i = 0; i < grey_conds[k].size() - 1; i++)
log_glyph(grey_conds[k][i], i);
for (i = 0; i < plain_temps[k].size() - 1; i++)
log_glyph(plain_temps[k][i], i);
for (i = 0; i < grey_temps[k].size() - 1; i++)
log_glyph(grey_temps[k][i], i);
log_glyph(plain_arrows[k]);
log_glyph(grey_arrows[k]);
log_glyph(past_arrows[k]);
log_glyph(signal_arrows[k]);
log_glyph(drag_arrows[k]);
log_glyph(drag_stops[k]);
log_glyph(drag_conds[k]);
log_glyph(drag_temps[k]);
}
#endif
}
// Delete glyph (breakpoints)
void SourceView::deleteGlyphAct(Widget glyph, XEvent *, String *, Cardinal *)
{
IntArray bps;
MapRef ref;
for (BreakPoint *bp = bp_map.first(ref); bp != 0; bp = bp_map.next(ref))
{
if (glyph == bp->source_glyph() || glyph == bp->code_glyph())
{
bps += bp->number();
}
}
delete_bps(bps, glyph);
}
//----------------------------------------------------------------------------
// Machine code stuff
//----------------------------------------------------------------------------
// Clear the code cache
void SourceView::clear_code_cache()
{
static CodeCache empty;
code_cache = empty;
process_disassemble("No code.");
}
#if RUNTIME_REGEX
static regex rxnladdress("\n *" RXADDRESS);
#endif
static string first_address(const string& s)
{
int idx = index(s, rxnladdress, "\n");
if (idx < 0)
return "";
idx++;
int eol = s.index('\n', idx);
if (eol < 0)
eol = s.length();
string addr = ((string&)s).at(idx, eol - idx);
return addr.through(rxaddress);
}
static string last_address(const string& s)
{
int idx = index(s, rxnladdress, "\n", -1);
if (idx < 0)
return "";
idx++;
int eol = s.index('\n', idx);
if (eol < 0)
eol = s.length();
string addr = ((string&)s).at(idx, eol - idx);
return addr.through(rxaddress);
}
void SourceView::set_code(const string& code,
const string& start,
const string& end)
{
XmTextSetString(code_text_w, (String)code);
XmTextSetHighlight (code_text_w, 0, code.length(), XmHIGHLIGHT_NORMAL);
current_code = code;
current_code_start = start;
current_code_end = end;
last_pos_pc = 0;
last_start_highlight_pc = 0;
last_end_highlight_pc = 0;
}
// Process output of `disassemble' command
void SourceView::process_disassemble(const string& disassemble_output)
{
int count = ((string&)disassemble_output).freq('\n') + 1;
string *code_list = new string[count];
split((string &)disassemble_output, code_list, count, '\n');
string indented_code;
for (int i = 0; i < count; i++)
{
string& line = code_list[i];
untabify_if_needed(line);
if (line.length() > 0 && line[0] == '0')
line = replicate(' ', indent_amount(code_text_w)) + line;
indented_code += line + '\n';
}
set_code(indented_code,
first_address(disassemble_output),
last_address(disassemble_output));
if (cache_machine_code
&& current_code_start != ""
&& current_code_end != "")
code_cache += CodeCacheEntry(current_code_start,
current_code_end,
current_code);
}
// Search PC in the current code; return beginning of line if found
XmTextPosition SourceView::find_pc(const string& pc)
{
if (compare_address(pc, current_code_start) < 0
|| compare_address(pc, current_code_end) > 0)
return XmTextPosition(-1);
XmTextPosition pos = XmTextPosition(-1);
int i = 0;
while (i < int(current_code.length()))
{
int eol = current_code.index('\n', i);
if (eol < 0)
break;
int j = i;
while (j < int(current_code.length()) && isspace(current_code[j]))
j++;
if (j + 2 < int(current_code.length())
&& (is_address_start(current_code[j])))
{
// Use first word of line as address. Much faster than
// checking address regexps.
string address = current_code.at(j, eol - j);
int k = 0;
while (k < int(address.length()) && !isspace(address[k]))
k++;
address = address.before(k);
if (compare_address(pc, address) == 0)
{
pos = i;
break;
}
}
i = eol + 1;
}
return pos;
}
// Process `disassemble' output
void SourceView::refresh_codeOQC(const string& answer, void *client_data)
{
RefreshDisassembleInfo *info = (RefreshDisassembleInfo *)client_data;
if (answer == NO_GDB_ANSWER)
{
info->delay.outcome = "failed";
}
else
{
process_disassemble(answer);
if (find_pc(info->pc) != XmTextPosition(-1))
show_pc(info->pc, info->mode);
}
delete info;
}
void SourceView::normalize_address(string& addr)
{
addr.downcase();
if (addr.contains("0", 0))
addr = addr.after("0");
if (addr.contains("x", 0))
addr = addr.after("x");
if (addr.contains("h'", 0))
addr = addr.after("h'");
if (addr.contains("h", -1))
addr = addr.before(int(addr.length()) - 1);
addr.prepend("0x");
}
string SourceView::make_address(long pc)
{
char buffer[BUFSIZ];
sprintf(buffer, "0x%lx", (unsigned long) pc);
return string(buffer);
}
// Return FUNCTION and OFFSET at ADDRESS
void SourceView::get_func_at(const string& address, string& func, int& offset)
{
// GDB issues /i lines in the format
// `ADDR <FUNC[+OFFSET]> INSTRUCTIONS', as in
// `0xef7be49c <_IO_file_underflow+128>:\torcc %o0, %g0, %o2\n'
offset = 0;
func = gdb_question("x /i " + address);
if (func == NO_GDB_ANSWER)
return;
// Find func
func = func.after("<");
func = func.before(">");
// Find offset
int plus = func.index('+');
if (plus >= 0)
{
offset = atoi(func.chars() + plus + 1);
func = func.before(plus);
}
}
// Return TRUE iff the function at PC is larger than MAX_SIZE.
bool SourceView::function_is_larger_than(string pc, int max_size)
{
if (gdb->type() != GDB)
return false;
// Get function name at PC
normalize_address(pc);
string pc_func;
int pc_offset;
get_func_at(pc, pc_func, pc_offset);
if (pc_func == NO_GDB_ANSWER)
return true; // In doubt, treat function as `too large'.
if (pc_offset > max_size)
{
// We're already more than MAX_SIZE bytes away from the
// function start: function is too large.
return true;
}
// Get the function name at function start + MAX_SIZE. If this is
// the same name as the name at start, the function is too large.
unsigned long pc_l = strtoul(pc.chars(), NULL, 0);
unsigned long next_l = pc_l - pc_offset + max_size;
if (next_l < pc_l)
{
// Overflow
next_l = ULONG_MAX;
}
string next = make_address(next_l);
string next_func;
int next_offset;
get_func_at(next, next_func, next_offset);
if (next_func == NO_GDB_ANSWER)
return true; // In doubt, treat function as `too large'.
if (pc_func == next_func)
{
// We're still within the same function: function is too large.
return true;
}
return false;
}
// Show program counter location PC
// If MODE is given, highlight PC line.
// STOPPED indicates that the program just stopped.
// SIGNALED indicates that the program just received a signal.
void SourceView::show_pc(const string& pc, XmHighlightMode mode,
bool stopped, bool signaled)
{
last_shown_pc = pc;
if (mode == XmHIGHLIGHT_SELECTED)
last_execution_pc = pc;
if (stopped)
{
at_lowest_frame = true;
signal_received = signaled;
}
if (mode == XmHIGHLIGHT_SELECTED)
undo_buffer.add_address(pc, stopped);
else
undo_buffer.remove_address();
undo_buffer.add_state();
if (!disassemble)
return;
// clog << "Showing PC " << pc << "\n";
XmTextPosition pos = find_pc(pc);
// While PC not found, look for code in cache
for (int i = 0;
pos == XmTextPosition(-1) && i < code_cache.size();
i++)
{
const CodeCacheEntry& cce = code_cache[i];
if (compare_address(pc, cce.start) >= 0
&& compare_address(pc, cce.end) <= 0)
{
set_code(cce.code, cce.start, cce.end);
pos = find_pc(pc);
}
}
if (pos == XmTextPosition(-1))
{
// PC not found in current code: disassemble location
string start = pc;
string end = "";
if (app_data.max_disassemble > 0 &&
function_is_larger_than(pc, app_data.max_disassemble))
{
// Disassemble only MAX_DISASSEMBLE bytes after PC
unsigned long pc_l = strtoul(pc.chars(), NULL, 0);
unsigned long next_l = pc_l + app_data.max_disassemble;
if (next_l < pc_l)
{
// Overflow
next_l = ULONG_MAX;
}
end = make_address(next_l);
}
string msg = "Disassembling location " + start;
if (end != "")
msg += " to " + end;
RefreshDisassembleInfo *info =
new RefreshDisassembleInfo(pc, mode, msg);
gdb_command(gdb->disassemble_command(start, end), 0,
refresh_codeOQC, (void *)info);
return;
}
if (pos == XmTextPosition(-1))
return;
SetInsertionPosition(code_text_w, pos + indent_amount(code_text_w));
XmTextPosition pos_line_end = 0;
if (current_code != "")
pos_line_end = current_code.index('\n', pos) + 1;
// Clear old selection
if (last_start_highlight_pc)
{
XmTextSetHighlight (code_text_w,
last_start_highlight_pc,
last_end_highlight_pc,
XmHIGHLIGHT_NORMAL);
last_start_highlight_pc = 0;
last_end_highlight_pc = 0;
}
// Mark current line
if (mode == XmHIGHLIGHT_SELECTED)
{
if (!display_glyphs)
{
// Set new marker
int indent = indent_amount(code_text_w);
static const string marker = ">";
if (last_pos_pc)
{
static const string no_marker = " ";
XmTextReplace (code_text_w,
last_pos_pc + indent - no_marker.length(),
last_pos_pc + indent,
(String)no_marker);
}
XmTextReplace (code_text_w,
pos + indent - marker.length(),
pos + indent,
(String)marker);
if (pos_line_end)
{
XmTextSetHighlight (code_text_w,
pos, pos_line_end,
XmHIGHLIGHT_SELECTED);
last_start_highlight_pc = pos;
last_end_highlight_pc = pos_line_end;
}
last_pos_pc = pos;
}
}
if (mode == XmHIGHLIGHT_SELECTED)
update_glyphs(code_text_w);
}
void SourceView::set_disassemble(bool set)
{
if (disassemble != set)
{
disassemble = set;
if (!disassemble)
{
unmanage_paned_child(code_form_w);
}
else
{
manage_paned_child(code_form_w);
if (last_execution_pc != "")
show_pc(last_execution_pc, XmHIGHLIGHT_SELECTED);
else if (last_shown_pc != "")
show_pc(last_shown_pc);
else
lookup(line_of_cursor());
}
}
}
void SourceView::set_all_registers(bool set)
{
if (all_registers != set)
{
all_registers = set;
if (all_registers_w)
XmToggleButtonSetState(all_registers_w, (Boolean)set, False);
if (int_registers_w)
XmToggleButtonSetState(int_registers_w, !(Boolean)set, False);
refresh_registers();
}
}
// Some DBXes require `{ COMMAND; }', others `{ COMMAND }'.
string SourceView::command_list(string cmd)
{
if (gdb->has_when_semicolon())
return "{ " + cmd + "; }";
else
return "{ " + cmd + " }";
}
// Get the position of breakpoint NUM
string SourceView::bp_pos(int num)
{
BreakPoint *bp = bp_map.get(num);
if (bp == 0)
return "";
else
return bp->pos();
}
// True iff we have some selection
bool SourceView::have_selection()
{
XmTextPosition left, right;
return (XmTextGetSelectionPosition(source_text_w, &left, &right)
|| XmTextGetSelectionPosition(code_text_w, &left, &right))
&& left != right;
}
//----------------------------------------------------------------------------
// Session stuff
//----------------------------------------------------------------------------
int SourceView::max_breakpoint_number = 99;
// Return DDD commands to restore current state (breakpoints, etc.)
bool SourceView::get_state(ostream& os)
{
IntArray breakpoint_nrs;
bool ok = true;
// Restore breakpoints
MapRef ref;
for (BreakPoint *bp = bp_map.first(ref); bp != 0; bp = bp_map.next(ref))
breakpoint_nrs += bp->number();
if (breakpoint_nrs.size() > 0)
{
sort(breakpoint_nrs);
// If all breakpoint numbers are less than
// MAX_BREAKPOINT_NUMBER, insert `dummy' breakpoints that are
// immediately deleted after creation, such that the
// breakpoint numbers are preserved. Otherwise, begin
// numbering with 1.
int max_number = breakpoint_nrs[breakpoint_nrs.size() - 1];
bool restore_old_numbers = max_number < max_breakpoint_number;
int num = 1;
for (int i = 0; i < breakpoint_nrs.size(); i++)
{
BreakPoint *bp = bp_map.get(breakpoint_nrs[i]);
if (restore_old_numbers)
{
while (num < breakpoint_nrs[i])
ok = ok && bp->get_state(os, num++, true);
assert(num == breakpoint_nrs[i]);
}
ok = ok && bp->get_state(os, num++);
}
}
// Restore current cursor position
switch (gdb->type())
{
case GDB:
case PYDB:
os << "info line " << line_of_cursor() << '\n';
break;
case DBX:
case JDB:
case PERL:
break; // FIXME
case XDB:
os << "v " << line_of_cursor() << '\n';
break;
}
return ok;
}
void SourceView::reset_done(const string&, void *)
{
// All breakpoints should be deleted now -- clear all other information
clear_file_cache();
clear_code_cache();
clear_dbx_lookup_cache();
current_file_name = "";
// Reset execution positions
last_execution_file = "";
last_execution_line = 0;
last_execution_pc = "";
last_shown_pc = "";
// Reset frame info
current_frame = -1;
at_lowest_frame = true;
}
void SourceView::reset()
{
bool reset_later = false;
// Delete all breakpoints
if (gdb->has_delete_command())
{
string del = gdb->delete_command();
MapRef ref;
int n = 0;
for (BreakPoint *bp = bp_map.first(ref); bp != 0;
bp = bp_map.next(ref))
{
n++;
del += " " + itostring(bp->number());
}
if (n > 0)
{
Command c(del);
c.verbose = false;
c.prompt = false;
c.check = true;
c.priority = COMMAND_PRIORITY_INIT;
c.callback = reset_done;
gdb_command(c);
reset_later = true;
}
}
else if (gdb->has_clear_command())
{
MapRef ref;
for (BreakPoint *bp = bp_map.first(ref); bp != 0;
bp = bp_map.next(ref))
{
Command c(clear_command(bp->pos()));
c.verbose = false;
c.prompt = false;
c.check = true;
c.priority = COMMAND_PRIORITY_INIT;
if (bp_map.next(ref) == 0)
{
// Last command
c.callback = reset_done;
reset_later = true;
}
gdb_command(c);
}
}
if (!reset_later)
reset_done("", 0);
}