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 >
C/C++ Source or Header  |  1998-10-23  |  12KB  |  497 lines

  1. // $Id: Command.C,v 1.30 1998/10/23 20:30:13 zeller Exp $ -*- C++ -*-
  2. // DDD interface to GDB commands
  3.  
  4. // Copyright (C) 1996-1997 Technische Universitaet Braunschweig, Germany.
  5. // Written by Andreas Zeller <zeller@ips.cs.tu-bs.de>.
  6. // 
  7. // This file is part of DDD.
  8. // 
  9. // DDD is free software; you can redistribute it and/or
  10. // modify it under the terms of the GNU General Public
  11. // License as published by the Free Software Foundation; either
  12. // version 2 of the License, or (at your option) any later version.
  13. // 
  14. // DDD is distributed in the hope that it will be useful,
  15. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  17. // See the GNU General Public License for more details.
  18. // 
  19. // You should have received a copy of the GNU General Public
  20. // License along with DDD -- see the file COPYING.
  21. // If not, write to the Free Software Foundation, Inc.,
  22. // 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  23. // 
  24. // DDD is the data display debugger.
  25. // For details, see the DDD World-Wide-Web page, 
  26. // `http://www.cs.tu-bs.de/softech/ddd/',
  27. // or send a mail to the DDD developers <ddd@ips.cs.tu-bs.de>.
  28.  
  29. // Whenever I think of DDD, I'm reminded of that song by Trio that was 
  30. // featured in those Volkswagen commercials -- "Da Da Da".
  31. // 
  32. // Tom "Dee Dee Dee" Harrington <tph@rmi.net> -- rec.humor.oracle.d
  33.  
  34.  
  35. char Command_rcsid[] = 
  36.     "$Id: Command.C,v 1.30 1998/10/23 20:30:13 zeller Exp $";
  37.  
  38. #ifdef __GNUG__
  39. #pragma implementation
  40. #pragma implementation "Queue.h"
  41. #endif
  42.  
  43. #ifndef LOG_QUEUE
  44. #define LOG_QUEUE 0
  45. #endif
  46.  
  47. #include "Command.h"
  48. #include "comm-manag.h"
  49. #include "status.h"
  50. #include "exectty.h"
  51. #include "cmdtty.h"
  52. #include "findParent.h"
  53. #include "history.h"
  54. #include "ddd.h"
  55. #include "cook.h"
  56. #include "Queue.h"
  57. #include "windows.h"
  58. #include "GDBAgent.h"
  59. #include "TimeOut.h"
  60. #include "AppData.h"
  61. #include "disp-read.h"
  62.  
  63. #include <ctype.h>
  64. #include <string.h>
  65. #include <Xm/Xm.h>
  66. #include <Xm/Text.h>
  67. #include <X11/StringDefs.h>
  68.  
  69. // ANSI C++ doesn't like the XtIsRealized() macro
  70. #ifdef XtIsRealized
  71. #undef XtIsRealized
  72. #endif
  73.  
  74. // Origin of last command
  75. static Widget gdb_last_origin;
  76.  
  77. // True if a user command was processed
  78. static bool had_user_command = false;
  79.  
  80.  
  81. //-----------------------------------------------------------------------------
  82. // Auto command management
  83. //-----------------------------------------------------------------------------
  84.  
  85. // Strip auto-command prefix
  86. void strip_auto_command_prefix(string& cmd)
  87. {
  88.     // Neither of these ever changes, so make it static
  89.     static string echo = gdb->echo_command(app_data.auto_command_prefix);
  90.  
  91.     int i;
  92.     while ((i = cmd.index(echo)) >= 0)
  93.     {
  94.     cmd.at(i, echo.length()) = "";
  95.     int nl = cmd.index("\\n", i);
  96.     if (nl >= 0)
  97.         cmd.at(nl, 2) = "";
  98.     }
  99. }
  100.  
  101. // Add it
  102. void add_auto_command_prefix(string& cmd)
  103. {
  104.     cmd = gdb->echo_command(app_data.auto_command_prefix + cmd + "\n");
  105. }
  106.  
  107.  
  108. //-----------------------------------------------------------------------------
  109. // GDB command management
  110. //-----------------------------------------------------------------------------
  111.  
  112. static void ClearOriginCB(Widget w, XtPointer, XtPointer)
  113. {
  114.     if (gdb_last_origin == w)
  115.     gdb_last_origin = 0;
  116. }
  117.  
  118. // Translate frequently used commands
  119. void translate_command(string& command)
  120. {
  121.     switch(gdb->type())
  122.     {
  123.     case XDB:
  124.     // Although we have defined these as aliases, we translate
  125.     // them anyway.  This way, DDD can find the documentation
  126.     // (which won't work for aliases).
  127.     if (command == "run")
  128.         command = "r";
  129.     else if (command == "cont")
  130.         command = "c";
  131.     else if (command == "next")
  132.         command = "S";
  133.     else if (command == "step")
  134.         command = "s";
  135.     else if (command == "quit")
  136.         command = "q";
  137.     break;
  138.  
  139.     case JDB:
  140.     // Typing ^C at JDB kills it, which is not what the user
  141.     // expects.  Simply suspend all threads instead.
  142.     if (command == '\003')
  143.         command = "suspend";
  144.  
  145.     // FALL THROUGH
  146.     case DBX:
  147.     // DBX and JDB use `step up' instead of `finish'.
  148.     if (command == "finish")
  149.         command = "step up";
  150.     break;
  151.  
  152.     case PYDB:
  153.     // PYDB uses `return' instead of `finish'.
  154.     if (command == "finish")
  155.         command = "return";
  156.     break;
  157.  
  158.     case PERL:
  159.     // PERL only has one-letter commands
  160.     if (command == "run")
  161.         command = "r";
  162.     else if (command == "cont")
  163.         command = "c";
  164.     else if (command == "step")
  165.         command = "s";
  166.     else if (command == "next")
  167.         command = "n";
  168.     else if (command == "quit")
  169.         command = "q";
  170.     else if (command == "finish")
  171.         command = "r";
  172.     break;
  173.  
  174.     case GDB:
  175.     break;
  176.     }
  177.  
  178.     // When recording, realize certain commands as auto commands.
  179.     if (gdb->recording())
  180.     {
  181.     if (is_graph_cmd(command))
  182.     {
  183.         // Graph commands are handled by DDD.
  184.         add_auto_command_prefix(command);
  185.     }
  186.     else if (is_running_cmd(command, gdb))
  187.     {
  188.         // Running commands (typically, `continue') may issue
  189.         // display output or positions that must be interpreted by DDD.
  190.         add_auto_command_prefix(command);
  191.     }
  192.     }
  193. }
  194.  
  195. // Process command C; do it right now.
  196. static void do_gdb_command(Command& c)
  197. {
  198.     string cmd = c.command;
  199.     OQCProc callback       = c.callback;
  200.     OACProc extra_callback = c.extra_callback;
  201.     if (cmd.contains('\n'))
  202.     {
  203.     cmd = cmd.before('\n');
  204.     callback = 0;
  205.     extra_callback = 0;
  206.     }
  207.     c.command = c.command.after('\n');
  208.  
  209. #if LOG_QUEUE
  210.     clog << "Command " << quote(cmd) << "\n";
  211. #endif
  212.  
  213.     if (gdb->isReadyWithPrompt())
  214.     {
  215.     if (c.verbose)
  216.     {
  217.         set_status("");
  218.         had_user_command = true;
  219.     }
  220.  
  221.     if (cmd.length() == 1 && iscntrl(cmd[0]))
  222.     {
  223.         promptPosition = messagePosition = XmTextGetLastPosition(gdb_w);
  224.     }
  225.     else if (cmd.length() > 0 && c.echo)
  226.     {
  227.         add_to_history(cmd);
  228.     }
  229.  
  230.     if (!gdb->recording())
  231.         handle_running_commands(cmd, c.origin);
  232.  
  233.     if (cmd.length() == 0 && c.prompt)
  234.     {
  235.         _gdb_out(gdb->prompt());
  236.         return;
  237.     }
  238.     }
  239.  
  240.     gdb_keyboard_command = private_gdb_input;
  241.  
  242.     if (gdb_last_origin != 0)
  243.     {
  244.     XtRemoveCallback(gdb_last_origin, XtNdestroyCallback, 
  245.              ClearOriginCB, 0);
  246.     }
  247.  
  248.     gdb_last_origin = find_shell(gdb_keyboard_command ? gdb_w : c.origin);
  249.  
  250.     if (gdb_last_origin != 0)
  251.     {
  252.     XtAddCallback(gdb_last_origin, XtNdestroyCallback, 
  253.               ClearOriginCB, 0);
  254.     }
  255.  
  256.     translate_command(cmd);
  257.  
  258.     if (is_internal_command(cmd))
  259.     {
  260.     internal_command(cmd, callback, c.data, c.echo, c.verbose, c.prompt);
  261.     }
  262.     else
  263.     {
  264.     send_gdb_command(cmd, c.origin, callback, extra_callback, c.data, 
  265.              c.echo, c.verbose, c.prompt, c.check);
  266.     }
  267.     messagePosition = XmTextGetLastPosition(gdb_w);
  268. }
  269.  
  270. bool userInteractionSeen()
  271. {
  272.     return had_user_command;
  273. }
  274.  
  275.  
  276. //-----------------------------------------------------------------------------
  277. // Command queue
  278. //-----------------------------------------------------------------------------
  279.  
  280. void Command::add_destroy_callback()
  281. {
  282.     if (origin != 0)
  283.     XtAddCallback(origin, XtNdestroyCallback, clear_origin, 
  284.               (XtPointer)this);
  285. }
  286.  
  287. void Command::remove_destroy_callback()
  288. {
  289.     if (origin != 0)
  290.     XtRemoveCallback(origin, XtNdestroyCallback, clear_origin,
  291.              (XtPointer)this);
  292. }
  293.  
  294. void Command::clear_origin(Widget w, XtPointer client_data, XtPointer)
  295. {
  296.     (void) w;            // Use it
  297.  
  298.     // The widget is being destroyed.  Remove all references.
  299.     Command *command = (Command *)client_data;
  300.     assert(w == command->origin);
  301.     command->origin = 0;
  302. }
  303.  
  304. typedef Queue<Command> CommandQueue;
  305. typedef QueueIter<Command> CommandQueueIter;
  306.  
  307. static CommandQueue commandQueue;
  308.  
  309. #if LOG_QUEUE
  310. static ostream& operator<<(ostream& os, const CommandQueue& queue)
  311. {
  312.     os << "[";
  313.     bool first = true;
  314.     for (CommandQueueIter i = queue; i.ok(); i = i.next())
  315.     {
  316.     if (first)
  317.         first = false;
  318.     else
  319.         os << ", ";
  320.  
  321.     os << quote(i().command) << "<" << i().priority << ">";
  322.     }
  323.     return os << "]";
  324. }
  325. #endif
  326.  
  327. void clearCommandQueue()
  328. {
  329.     CommandQueue oldCommandQueue(commandQueue);
  330.     static CommandQueue emptyQueue;
  331.     commandQueue = emptyQueue;
  332.  
  333.     while (!oldCommandQueue.isEmpty())
  334.     {
  335.     const Command& cmd = oldCommandQueue.first();
  336.     if (cmd.callback != 0)
  337.     {
  338.         // We're deleting a command with associated callback.
  339.         // Call callback with NO_GDB_ANSWER such that it can clean
  340.         // up the associated data.  Commands added by the callback
  341.         // will be added to the command queue.
  342.         cmd.callback(NO_GDB_ANSWER, cmd.data);
  343.     }
  344.     oldCommandQueue -= cmd;
  345.     }
  346.  
  347. #if LOG_QUEUE
  348.     clog << "Command queue: " << commandQueue << "\n";
  349. #endif
  350. }
  351.  
  352. bool emptyCommandQueue()
  353. {
  354.     return commandQueue.isEmpty();
  355. }
  356.  
  357. static string last_user_reply = "";
  358.  
  359. // Last user reply to a `y or n' question
  360. string lastUserReply()
  361. {
  362.     return last_user_reply;
  363. }
  364.  
  365. void gdb_command(const Command& c0)
  366. {
  367.     Command c(c0);
  368.  
  369.     if (c.command.length() == 1 && iscntrl(c.command[0]) 
  370.     || c.command == "no" || c.command == "yes")
  371.     {
  372.      // User interaction -- execute immediately
  373.     last_user_reply = c.command;
  374.     do_gdb_command(c);
  375.  
  376.     if (last_user_reply != "yes")
  377.     {
  378.         // Probably some canceling command - clear remaining commands
  379.         clearCommandQueue();
  380.     }
  381.     }
  382.     else if (gdb->isReadyWithPrompt() && emptyCommandQueue())
  383.     {
  384.     do_gdb_command(c);
  385.     }
  386.  
  387.     if (c.command != "")
  388.     {
  389.     // Enqueue before first command with lower priority.  This
  390.     // ensures that user commands are placed at the end.
  391.     CommandQueueIter i(commandQueue);
  392.     CommandQueueIter pos(commandQueue);
  393.  
  394.     while (i.ok() && c.priority <= i().priority)
  395.     {
  396.         pos = i; i = i.next();
  397.     }
  398.  
  399.     if (!i.ok())
  400.     {
  401.         assert(!pos.ok() || pos().priority >= c.priority);
  402.  
  403.         // End of queue reached
  404.         commandQueue.enqueue_at_end(c);
  405.     }
  406.     else if (pos().priority >= c.priority)
  407.     {
  408.         assert(pos().priority >= c.priority && c.priority > i().priority);
  409.  
  410.         // Enqueue after POS
  411.         commandQueue.enqueue_after(c, pos);
  412.     }
  413.     else
  414.     {
  415.         CommandQueueIter start(commandQueue);
  416.         (void) start;    // Use it
  417.         assert(!start.ok() || start().priority < c.priority);
  418.  
  419.         // Higher priority than first element
  420.         commandQueue.enqueue_at_start(c);
  421.     }
  422.  
  423. #if LOG_QUEUE
  424.     clog << "Command queue: " << commandQueue << "\n";
  425. #endif
  426.     }
  427. }
  428.  
  429. void processCommandQueue(XtPointer, XtIntervalId *)
  430. {
  431.     if (emptyCommandQueue())
  432.     return;
  433.  
  434.     if (!gdb->isReadyWithPrompt())
  435.     {
  436.     // Try again later...
  437.     XtAppAddTimeOut(XtWidgetToApplicationContext(gdb_w), 
  438.             200, processCommandQueue, XtPointer(0));
  439.     return;
  440.     }
  441.  
  442.     Command& c = commandQueue.first();
  443.     if (c.command.contains('\n'))
  444.     {
  445.     do_gdb_command(c);
  446.     }
  447.     else
  448.     {
  449.     Command cmd(c);
  450.     commandQueue.dequeue(c);
  451.     do_gdb_command(cmd);
  452.     }
  453.  
  454.     gdb_keyboard_command = false;
  455.  
  456. #if LOG_QUEUE
  457.     clog << "Command queue: " << commandQueue << "\n";
  458. #endif
  459. }
  460.  
  461. // Wait for command queue to drain
  462. void syncCommandQueue()
  463. {
  464.     while (!(emptyCommandQueue() && gdb->isReadyWithPrompt()))
  465.     {
  466.     if (gdb->isReadyWithPrompt())
  467.         processCommandQueue();
  468.  
  469.     // This should ensure ESC and ^C can interrupt this queue.
  470.     // Hopefully we have some timer ready that checks for them...
  471.     process_emergencies();
  472.  
  473.     XtAppProcessEvent(XtWidgetToApplicationContext(command_shell),
  474.               XtIMTimer | XtIMAlternateInput);
  475.     }
  476. }
  477.  
  478. // Shell finder
  479. Widget find_shell(Widget w)
  480. {
  481.     if (w == 0)
  482.     w = gdb_last_origin;
  483.     if (w == 0)
  484.     return command_shell;
  485.  
  486.     Widget parent = findTopLevelShellParent(w);
  487.     if (parent == 0 || !XtIsRealized(parent))
  488.     return command_shell;
  489.  
  490.     XWindowAttributes xwa;
  491.     XGetWindowAttributes(XtDisplay(parent), XtWindow(parent), &xwa);
  492.     if (xwa.map_state != IsViewable)
  493.     return command_shell;
  494.  
  495.     return parent;
  496. }
  497.