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
/
Command.C
< prev
next >
Wrap
C/C++ Source or Header
|
1998-10-23
|
12KB
|
497 lines
// $Id: Command.C,v 1.30 1998/10/23 20:30:13 zeller Exp $ -*- C++ -*-
// DDD interface to GDB commands
// Copyright (C) 1996-1997 Technische Universitaet Braunschweig, Germany.
// Written by 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>.
// Whenever I think of DDD, I'm reminded of that song by Trio that was
// featured in those Volkswagen commercials -- "Da Da Da".
//
// Tom "Dee Dee Dee" Harrington <tph@rmi.net> -- rec.humor.oracle.d
char Command_rcsid[] =
"$Id: Command.C,v 1.30 1998/10/23 20:30:13 zeller Exp $";
#ifdef __GNUG__
#pragma implementation
#pragma implementation "Queue.h"
#endif
#ifndef LOG_QUEUE
#define LOG_QUEUE 0
#endif
#include "Command.h"
#include "comm-manag.h"
#include "status.h"
#include "exectty.h"
#include "cmdtty.h"
#include "findParent.h"
#include "history.h"
#include "ddd.h"
#include "cook.h"
#include "Queue.h"
#include "windows.h"
#include "GDBAgent.h"
#include "TimeOut.h"
#include "AppData.h"
#include "disp-read.h"
#include <ctype.h>
#include <string.h>
#include <Xm/Xm.h>
#include <Xm/Text.h>
#include <X11/StringDefs.h>
// ANSI C++ doesn't like the XtIsRealized() macro
#ifdef XtIsRealized
#undef XtIsRealized
#endif
// Origin of last command
static Widget gdb_last_origin;
// True if a user command was processed
static bool had_user_command = false;
//-----------------------------------------------------------------------------
// Auto command management
//-----------------------------------------------------------------------------
// Strip auto-command prefix
void strip_auto_command_prefix(string& cmd)
{
// Neither of these ever changes, so make it static
static string echo = gdb->echo_command(app_data.auto_command_prefix);
int i;
while ((i = cmd.index(echo)) >= 0)
{
cmd.at(i, echo.length()) = "";
int nl = cmd.index("\\n", i);
if (nl >= 0)
cmd.at(nl, 2) = "";
}
}
// Add it
void add_auto_command_prefix(string& cmd)
{
cmd = gdb->echo_command(app_data.auto_command_prefix + cmd + "\n");
}
//-----------------------------------------------------------------------------
// GDB command management
//-----------------------------------------------------------------------------
static void ClearOriginCB(Widget w, XtPointer, XtPointer)
{
if (gdb_last_origin == w)
gdb_last_origin = 0;
}
// Translate frequently used commands
void translate_command(string& command)
{
switch(gdb->type())
{
case XDB:
// Although we have defined these as aliases, we translate
// them anyway. This way, DDD can find the documentation
// (which won't work for aliases).
if (command == "run")
command = "r";
else if (command == "cont")
command = "c";
else if (command == "next")
command = "S";
else if (command == "step")
command = "s";
else if (command == "quit")
command = "q";
break;
case JDB:
// Typing ^C at JDB kills it, which is not what the user
// expects. Simply suspend all threads instead.
if (command == '\003')
command = "suspend";
// FALL THROUGH
case DBX:
// DBX and JDB use `step up' instead of `finish'.
if (command == "finish")
command = "step up";
break;
case PYDB:
// PYDB uses `return' instead of `finish'.
if (command == "finish")
command = "return";
break;
case PERL:
// PERL only has one-letter commands
if (command == "run")
command = "r";
else if (command == "cont")
command = "c";
else if (command == "step")
command = "s";
else if (command == "next")
command = "n";
else if (command == "quit")
command = "q";
else if (command == "finish")
command = "r";
break;
case GDB:
break;
}
// When recording, realize certain commands as auto commands.
if (gdb->recording())
{
if (is_graph_cmd(command))
{
// Graph commands are handled by DDD.
add_auto_command_prefix(command);
}
else if (is_running_cmd(command, gdb))
{
// Running commands (typically, `continue') may issue
// display output or positions that must be interpreted by DDD.
add_auto_command_prefix(command);
}
}
}
// Process command C; do it right now.
static void do_gdb_command(Command& c)
{
string cmd = c.command;
OQCProc callback = c.callback;
OACProc extra_callback = c.extra_callback;
if (cmd.contains('\n'))
{
cmd = cmd.before('\n');
callback = 0;
extra_callback = 0;
}
c.command = c.command.after('\n');
#if LOG_QUEUE
clog << "Command " << quote(cmd) << "\n";
#endif
if (gdb->isReadyWithPrompt())
{
if (c.verbose)
{
set_status("");
had_user_command = true;
}
if (cmd.length() == 1 && iscntrl(cmd[0]))
{
promptPosition = messagePosition = XmTextGetLastPosition(gdb_w);
}
else if (cmd.length() > 0 && c.echo)
{
add_to_history(cmd);
}
if (!gdb->recording())
handle_running_commands(cmd, c.origin);
if (cmd.length() == 0 && c.prompt)
{
_gdb_out(gdb->prompt());
return;
}
}
gdb_keyboard_command = private_gdb_input;
if (gdb_last_origin != 0)
{
XtRemoveCallback(gdb_last_origin, XtNdestroyCallback,
ClearOriginCB, 0);
}
gdb_last_origin = find_shell(gdb_keyboard_command ? gdb_w : c.origin);
if (gdb_last_origin != 0)
{
XtAddCallback(gdb_last_origin, XtNdestroyCallback,
ClearOriginCB, 0);
}
translate_command(cmd);
if (is_internal_command(cmd))
{
internal_command(cmd, callback, c.data, c.echo, c.verbose, c.prompt);
}
else
{
send_gdb_command(cmd, c.origin, callback, extra_callback, c.data,
c.echo, c.verbose, c.prompt, c.check);
}
messagePosition = XmTextGetLastPosition(gdb_w);
}
bool userInteractionSeen()
{
return had_user_command;
}
//-----------------------------------------------------------------------------
// Command queue
//-----------------------------------------------------------------------------
void Command::add_destroy_callback()
{
if (origin != 0)
XtAddCallback(origin, XtNdestroyCallback, clear_origin,
(XtPointer)this);
}
void Command::remove_destroy_callback()
{
if (origin != 0)
XtRemoveCallback(origin, XtNdestroyCallback, clear_origin,
(XtPointer)this);
}
void Command::clear_origin(Widget w, XtPointer client_data, XtPointer)
{
(void) w; // Use it
// The widget is being destroyed. Remove all references.
Command *command = (Command *)client_data;
assert(w == command->origin);
command->origin = 0;
}
typedef Queue<Command> CommandQueue;
typedef QueueIter<Command> CommandQueueIter;
static CommandQueue commandQueue;
#if LOG_QUEUE
static ostream& operator<<(ostream& os, const CommandQueue& queue)
{
os << "[";
bool first = true;
for (CommandQueueIter i = queue; i.ok(); i = i.next())
{
if (first)
first = false;
else
os << ", ";
os << quote(i().command) << "<" << i().priority << ">";
}
return os << "]";
}
#endif
void clearCommandQueue()
{
CommandQueue oldCommandQueue(commandQueue);
static CommandQueue emptyQueue;
commandQueue = emptyQueue;
while (!oldCommandQueue.isEmpty())
{
const Command& cmd = oldCommandQueue.first();
if (cmd.callback != 0)
{
// We're deleting a command with associated callback.
// Call callback with NO_GDB_ANSWER such that it can clean
// up the associated data. Commands added by the callback
// will be added to the command queue.
cmd.callback(NO_GDB_ANSWER, cmd.data);
}
oldCommandQueue -= cmd;
}
#if LOG_QUEUE
clog << "Command queue: " << commandQueue << "\n";
#endif
}
bool emptyCommandQueue()
{
return commandQueue.isEmpty();
}
static string last_user_reply = "";
// Last user reply to a `y or n' question
string lastUserReply()
{
return last_user_reply;
}
void gdb_command(const Command& c0)
{
Command c(c0);
if (c.command.length() == 1 && iscntrl(c.command[0])
|| c.command == "no" || c.command == "yes")
{
// User interaction -- execute immediately
last_user_reply = c.command;
do_gdb_command(c);
if (last_user_reply != "yes")
{
// Probably some canceling command - clear remaining commands
clearCommandQueue();
}
}
else if (gdb->isReadyWithPrompt() && emptyCommandQueue())
{
do_gdb_command(c);
}
if (c.command != "")
{
// Enqueue before first command with lower priority. This
// ensures that user commands are placed at the end.
CommandQueueIter i(commandQueue);
CommandQueueIter pos(commandQueue);
while (i.ok() && c.priority <= i().priority)
{
pos = i; i = i.next();
}
if (!i.ok())
{
assert(!pos.ok() || pos().priority >= c.priority);
// End of queue reached
commandQueue.enqueue_at_end(c);
}
else if (pos().priority >= c.priority)
{
assert(pos().priority >= c.priority && c.priority > i().priority);
// Enqueue after POS
commandQueue.enqueue_after(c, pos);
}
else
{
CommandQueueIter start(commandQueue);
(void) start; // Use it
assert(!start.ok() || start().priority < c.priority);
// Higher priority than first element
commandQueue.enqueue_at_start(c);
}
#if LOG_QUEUE
clog << "Command queue: " << commandQueue << "\n";
#endif
}
}
void processCommandQueue(XtPointer, XtIntervalId *)
{
if (emptyCommandQueue())
return;
if (!gdb->isReadyWithPrompt())
{
// Try again later...
XtAppAddTimeOut(XtWidgetToApplicationContext(gdb_w),
200, processCommandQueue, XtPointer(0));
return;
}
Command& c = commandQueue.first();
if (c.command.contains('\n'))
{
do_gdb_command(c);
}
else
{
Command cmd(c);
commandQueue.dequeue(c);
do_gdb_command(cmd);
}
gdb_keyboard_command = false;
#if LOG_QUEUE
clog << "Command queue: " << commandQueue << "\n";
#endif
}
// Wait for command queue to drain
void syncCommandQueue()
{
while (!(emptyCommandQueue() && gdb->isReadyWithPrompt()))
{
if (gdb->isReadyWithPrompt())
processCommandQueue();
// This should ensure ESC and ^C can interrupt this queue.
// Hopefully we have some timer ready that checks for them...
process_emergencies();
XtAppProcessEvent(XtWidgetToApplicationContext(command_shell),
XtIMTimer | XtIMAlternateInput);
}
}
// Shell finder
Widget find_shell(Widget w)
{
if (w == 0)
w = gdb_last_origin;
if (w == 0)
return command_shell;
Widget parent = findTopLevelShellParent(w);
if (parent == 0 || !XtIsRealized(parent))
return command_shell;
XWindowAttributes xwa;
XGetWindowAttributes(XtDisplay(parent), XtWindow(parent), &xwa);
if (xwa.map_state != IsViewable)
return command_shell;
return parent;
}