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 / UndoBuffer.C < prev    next >
C/C++ Source or Header  |  1998-11-17  |  23KB  |  1,032 lines

  1. // $Id: UndoBuffer.C,v 1.46 1998/11/17 10:05:35 zeller Exp $ -*- C++ -*-
  2. // Undo/Redo buffer
  3.  
  4. // Copyright (C) 1998 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. char UndoBuffer_rcsid[] = 
  30.     "$Id: UndoBuffer.C,v 1.46 1998/11/17 10:05:35 zeller Exp $";
  31.  
  32. #ifdef __GNUG__
  33. #pragma implementation
  34. #endif
  35.  
  36. #include "UndoBuffer.h"
  37.  
  38. #include "BreakPoint.h"
  39. #include "Command.h"
  40. #include "DataDisp.h"
  41. #include "SourceView.h"
  42. #include "buttons.h"
  43. #include "cook.h"
  44. #include "ddd.h"
  45. #include "disp-read.h"
  46. #include "index.h"
  47. #include "misc.h"
  48. #include "regexps.h"
  49. #include "status.h"
  50. #include "string-fun.h"
  51.  
  52. #ifndef LOG_UNDO_BUFFER
  53. #define LOG_UNDO_BUFFER 0
  54. #endif
  55.  
  56. // This is the DDD Undo Buffer, a nasty and obfusticating piece of
  57. // DDD.  Basically, it attempts to combine the two concepts of a
  58. // command-based undo buffer (for undoing commands) and a state-based
  59. // undo buffer (for showing earlier program states) into one concept.
  60. // Problems occur at the intertwining of these concepts - i.e. mixing
  61. // commands and state.
  62. //
  63. // Don't touch it unless you have good reason to do so.
  64. //
  65. // Here are a few tests to keep you in a good mood:
  66. //
  67. // Break      Break       Break
  68. // Run        Run         Run
  69. // Display    Tbreak      Next
  70. // Set        Cont        Display
  71. // Next                   Down
  72. //                        Up
  73. //                        Next
  74. // 
  75. // Each command must be undoable.
  76. // 
  77. // Here's the general organization scheme:
  78. //
  79. // History
  80. // -------
  81. //
  82. // `history' contains:
  83. //
  84. // 0                       \ 
  85. // 1 ...                    > entries (states or commands) to be undone
  86. // (history_position - 1)  /
  87. // (history_position)      \ 
  88. // ...                      > entries (states or commands) to be redone
  89. // (history.size() - 1)    /
  90. //
  91. //
  92. // Entries
  93. // -------
  94. //
  95. // Each entry is a list of key/value pairs.
  96. //
  97. // Basically, there are three kinds of entries:
  98. // * COMMANDS with the command name in the COMMAND key.
  99. //   The command is to be executed when reached.
  100. // * POSITIONS with the position in the POS key.  Used in lookups.
  101. // * STATES with the an empty STATE key.  The current state is contained 
  102. //   in the remaining keys.
  103. //
  104. //
  105. // DDD starts a new entry each time the command source is set via
  106. // SET_SOURCE - the flag FORCE_NEW_ENTRY is set - *and* the new entry
  107. // contains either a command, a position, or a state key.  Up to then,
  108. // the current state is collected in the COLLECTOR variable.
  109. // 
  110. // Upon Undo, DDD executes the command at HISTORY_POSITION - 1 and
  111. // re-creates the earlier state at HISTORY_POSITION - 2.
  112. //
  113. // Upon Redo, DDD executes the command and recreates the state at
  114. // HISTORY_POSITION.
  115.  
  116.  
  117. #define REMAP_COMMAND "@remap "
  118.  
  119. UndoBuffer undo_buffer;
  120.  
  121. UndoBufferArray UndoBuffer::history;
  122.  
  123. // Last position in history + 1
  124. int UndoBuffer::history_position = 0;
  125.  
  126. // Current entry we're processing
  127. int UndoBuffer::current_entry = 0;
  128.  
  129. // Maximum depth (< 0 means unlimited)
  130. int UndoBuffer::max_history_depth = 100;
  131.  
  132. // Maximum allocation in bytes (< 0 means unlimited)
  133. int UndoBuffer::max_history_size = 2000000;
  134.  
  135. // True if we're undoing
  136. bool UndoBuffer::undoing = false;
  137.  
  138. // If true, ignore all changes and additions
  139. bool UndoBuffer::locked = false;
  140.  
  141. // If true, we're showing some older program state
  142. bool UndoBuffer::_showing_earlier_state = false;
  143.  
  144. // Current command source
  145. string UndoBuffer::current_source = "";
  146.  
  147. // Force new entry?
  148. bool UndoBuffer::force_new_entry = true;
  149.  
  150. // Collector for new state entries
  151. UndoBufferEntry UndoBuffer::collector;
  152.  
  153. //-----------------------------------------------------------------------
  154. // Position history
  155. //-----------------------------------------------------------------------
  156.  
  157. // True if ENTRY has any effect
  158. bool UndoBuffer::has_effect(const UndoBufferEntry& entry)
  159. {
  160.     for (StringStringAssocIter iter(entry); iter.ok(); iter++)
  161.     {
  162.     if (iter.key() != UB_SOURCE)
  163.         return true;
  164.     }
  165.  
  166.     return false;
  167. }
  168.  
  169. // Remove all later entries, except for exec positions
  170. void UndoBuffer::clear_after_position()
  171. {
  172.     UndoBufferArray new_history(history.size());
  173.     for (int i = 0; i < history.size(); i++)
  174.     {
  175.     if (i < history_position || 
  176.         (!history[i].has_command() && !history[i].has_pos()))
  177.     {
  178.         UndoBufferEntry entry = history[i];
  179.  
  180.         if (i >= history_position)
  181.         {
  182.         entry.remove(UB_POS);
  183.         entry.remove(UB_ADDRESS);
  184.         entry.remove(UB_COMMAND);
  185.         entry.remove(UB_EXEC_COMMAND);
  186.         }
  187.  
  188.         new_history += entry;
  189.     }
  190.     }
  191.  
  192.     history = new_history;
  193. }
  194.  
  195. // Remove all exec commands
  196. void UndoBuffer::clear_exec_commands()
  197. {
  198.     for (int i = 0; i < history.size(); i++)
  199.     {
  200.     UndoBufferEntry& entry = history[i];
  201.     entry.remove(UB_EXEC_COMMAND);
  202.     }
  203. }
  204.  
  205. // Remove all entries with no effect; 
  206. // also truncate history such that it fits into limits
  207. void UndoBuffer::cleanup()
  208. {
  209.     if (max_history_depth == 0 || max_history_size == 0)
  210.     {
  211.     clear();
  212.     return;
  213.     }
  214.  
  215.     int end = 0;        // Copy entries greater than END
  216.     if (max_history_depth >= 0)
  217.     {
  218.     // Truncate according to MAX_HISTORY_DEPTH
  219.     end = max(0, history.size() - max_history_depth);
  220.     }
  221.  
  222.     if (max_history_size >= 0)
  223.     {
  224.     // Truncate according to MAX_HISTORY_SIZE
  225.     int size = 0;
  226.  
  227.     for (int i = history.size() - 1; i >= end; i--)
  228.     {
  229.         UndoBufferEntry& entry = history[i];
  230.         if (!has_effect(entry))
  231.         continue;
  232.  
  233.         int alloc = entry.allocation();
  234.  
  235.         if (size + alloc > max_history_size)
  236.         {
  237.         end = i;
  238.         break;
  239.         }
  240.  
  241.         size += alloc;
  242.     }
  243.     }
  244.  
  245.     UndoBufferArray new_history(history.size() - end);
  246.     int old_history_position = history_position;
  247.  
  248.     for (int i = 0; i < history.size(); i++)
  249.     {
  250.     UndoBufferEntry& entry = history[i];
  251.  
  252.     if (i >= end && has_effect(entry))
  253.     {
  254.         new_history += entry;
  255.     }
  256.     else
  257.     {
  258.         // Ignore this entry
  259.         if (i < old_history_position)
  260.         history_position--;
  261.     }
  262.     }
  263.  
  264.     history = new_history;
  265. }
  266.  
  267. // Add new entry to history
  268. void UndoBuffer::add_entry(const UndoBufferEntry& entry)
  269. {
  270.     if (max_history_depth == 0)
  271.     return;
  272.  
  273.     // Remove all later entries
  274.     clear_after_position();
  275.  
  276.     if (!entry.has_command() && !entry.has_pos() && entry.has_state())
  277.     clear_exec_commands();
  278.  
  279.     // Add at end
  280.     history += entry;
  281.     history_position = history.size();
  282.  
  283.     // Clear commands without effect
  284.     cleanup();
  285.  
  286.     done();
  287. }
  288.  
  289. // Add status NAME/VALUE to history
  290. void UndoBuffer::add_status(const string& name, const string& value)
  291. {
  292.     if (locked)
  293.     return;
  294.  
  295. #if LOG_UNDO_BUFFER
  296.     clog << "Adding " << name << " = " << quote(value) << "\n";
  297. #endif
  298.  
  299.     collector[name] = value;
  300.  
  301.     if (!collector.has_command() && 
  302.     !collector.has_pos() && 
  303.     !collector.has_state())
  304.     return;            // Not enough stuff yet
  305.  
  306.     restore_current_state();
  307.  
  308.     if (force_new_entry || history.size() == 0)
  309.     {
  310.     collector[UB_SOURCE] = current_source;
  311.     add_entry(collector);
  312.  
  313.     force_new_entry = false;
  314.     }
  315.     else
  316.     {
  317.     // Replace last entry with collector
  318.     history[history.size() - 1] = collector;
  319.     }
  320.  
  321.     done();
  322. }
  323.  
  324. void UndoBuffer::remove_status(const string& name)
  325. {
  326.     if (locked)
  327.     return;
  328.  
  329. #if LOG_UNDO_BUFFER
  330.     if (collector.has(name))
  331.     clog << "Removing " << name << "\n";
  332. #endif
  333.  
  334.     collector.remove(name);
  335.  
  336.     if (!force_new_entry && history.size() > 0)
  337.     history[history.size() - 1].remove(name);
  338.  
  339.     done();
  340. }
  341.  
  342. // Add command COMMAND to history.
  343. void UndoBuffer::add_command(const string& command, bool exec)
  344. {
  345.     string c = command;
  346.     strip_space(c);
  347.     if (c == "")
  348.     return;
  349.  
  350.     string command_key = exec ? UB_EXEC_COMMAND : UB_COMMAND;
  351.  
  352.     if (!undoing)
  353.     {
  354.     // Regular command
  355.     add_status(command_key, c);
  356.     return;
  357.     }
  358.  
  359.     // We're called from undo or redo via process_command() - 
  360.     // replace currently executed command by its undo command.
  361.  
  362. #if LOG_UNDO_BUFFER
  363.     clog << "Adding " << command_key << " = " << quote(command) << "\n";
  364. #endif
  365.  
  366.     assert (history.size() > 0);
  367.     assert (current_entry >= 0 && current_entry < history.size());
  368.  
  369.     UndoBufferEntry& entry = history[current_entry];
  370.  
  371.     if (entry.has(command_key))
  372.     entry[command_key].prepend(c + '\n');
  373.     else
  374.     entry[command_key] = c;
  375.  
  376.     done();
  377. }
  378.  
  379. // Define COMMAND as command source
  380. void UndoBuffer::set_source(const string& command)
  381. {
  382. #if 0
  383.     clog << "Source:\t" << quote(command) << "\n";
  384. #endif
  385.  
  386.     current_source = action(command);
  387.  
  388.     if (locked)
  389.     return;
  390.  
  391.     force_new_entry = true;
  392.  
  393.     collector.remove(UB_COMMAND);
  394.     collector.remove(UB_EXEC_COMMAND);
  395.  
  396.     collector.remove(UB_POS);
  397.     collector.remove(UB_ADDRESS);
  398.  
  399.     collector.remove(UB_STATE);
  400. }
  401.  
  402. int UndoBuffer::allocation()
  403. {
  404.     int alloc = 0;
  405.  
  406.     for (int i = 0; i < history.size(); i++)
  407.     alloc += history[i].allocation();
  408.  
  409.     return alloc;
  410. }
  411.  
  412. void UndoBuffer::log()
  413. {
  414. #if LOG_UNDO_BUFFER
  415.     clog << "Undo buffer:\n";
  416.     for (int i = 0; i < history.size(); i++)
  417.     {
  418. #if 0
  419.     // Only log the first 2 items around the current position
  420.     if (abs(i - (history_position - 1)) > 2)
  421.         continue;
  422. #endif
  423.  
  424.     const UndoBufferEntry& entry = history[i];
  425.  
  426.     if (i == history_position)
  427.         clog << ">-------------\n";
  428.  
  429.     clog << i << '\t';
  430.  
  431.     bool first_line = true;
  432.     for (StringStringAssocIter iter(entry); iter.ok(); iter++)
  433.     {
  434.         if (!first_line)
  435.         clog << "\n\t";
  436.         clog << iter.key() << " = " << quote(iter.value());
  437.         first_line = false;
  438.     }
  439.     clog << "\n";
  440.     }
  441.  
  442. #if 0
  443.     clog << "Collector:";
  444.     for (StringStringAssocIter iter(collector); iter.ok(); iter++)
  445.     clog << "\n\t" << iter.key() << " = " << quote(iter.value());
  446.     clog << "\n";
  447. #endif
  448.  
  449.     if (undo_action() != NO_GDB_ANSWER)
  450.     clog << "Undo " << undo_action() << "\n";
  451.     if (redo_action() != NO_GDB_ANSWER)
  452.     clog << "Redo " << redo_action() << "\n";
  453.  
  454.     clog << "Allocated " << allocation() << " bytes\n";
  455.  
  456.     clog << "\n";
  457. #endif
  458. }
  459.  
  460. void UndoBuffer::remap_breakpoint(string& cmd, int old_bp, int new_bp)
  461. {
  462.     string old_num = "@" + itostring(old_bp) + "@";
  463.     string new_num = "@" + itostring(new_bp) + "@";
  464.     cmd.gsub(old_num, new_num);
  465. }
  466.  
  467. void UndoBuffer::remap_breakpoint(int old_bp, int new_bp)
  468. {
  469.     for (int i = 0; i < history.size(); i++)
  470.     {
  471.     if (history[i].has(UB_COMMAND))
  472.         remap_breakpoint(history[i][UB_COMMAND], old_bp, new_bp);
  473.     if (history[i].has(UB_EXEC_COMMAND))
  474.         remap_breakpoint(history[i][UB_EXEC_COMMAND], old_bp, new_bp);
  475.     }
  476. }
  477.  
  478. void UndoBuffer::add_breakpoint_state(ostream& os, BreakPoint *bp)
  479. {
  480.     os << REMAP_COMMAND << "@" << bp->number() << "@\n";
  481.     bp->get_state(os, bp->number());
  482. }
  483.  
  484. static void get_confirm(const string& complete_answer, void *qu_data)
  485. {
  486.     bool *flag = (bool *)qu_data;
  487.     *flag = complete_answer.contains("is on");
  488. }
  489.  
  490. bool UndoBuffer::process_command(UndoBufferEntry& entry)
  491. {
  492.     // Process command
  493.     string commands;
  494.     if (entry.has(UB_EXEC_COMMAND))
  495.     commands = entry[UB_EXEC_COMMAND];
  496.     else if (entry.has(UB_COMMAND))
  497.     commands = entry[UB_COMMAND];
  498.     else
  499.     return true;        // Nothing to do
  500.  
  501.     entry.remove(UB_COMMAND);
  502.     entry.remove(UB_EXEC_COMMAND);
  503.  
  504.     string original_commands = commands;
  505.     int bp_count = 0;
  506.     undoing = true;
  507.  
  508.     bool confirm = false;
  509.  
  510.     if (commands != "" && gdb->type() == GDB)
  511.     {
  512.     gdb_command("show confirm", 0, get_confirm, &confirm);
  513.     syncCommandQueue();
  514.     }
  515.  
  516.     if (confirm)
  517.     {
  518.     // Turn confirmation off during undo/redo.
  519.     gdb_question("set confirm off");
  520.     }
  521.  
  522.     while (commands != "")
  523.     {
  524.     string cmd;
  525.     if (commands.contains('\n'))
  526.         cmd = commands.before('\n');
  527.     else
  528.         cmd = commands;
  529.     commands = commands.after('\n');
  530.  
  531.     // Handle breakpoint remappings
  532.     if (cmd.contains(REMAP_COMMAND, 0))
  533.     {
  534.         int old_bp_nr = atoi(cmd.chars() + strlen(REMAP_COMMAND "@"));
  535.         int new_bp_nr = source_view->next_breakpoint_number() + bp_count++;
  536.  
  537.         remap_breakpoint(old_bp_nr, new_bp_nr);
  538.         remap_breakpoint(commands, old_bp_nr, new_bp_nr);
  539.  
  540.         continue;
  541.     }
  542.  
  543.     // Replace all occurrences of `@N@' by N
  544. #if RUNTIME_REGEX
  545.     static regex rxnum("@[0-9]+@");
  546. #endif
  547.     int i;
  548.     while ((i = index(cmd, rxnum, "@")) >= 0)
  549.     {
  550.         int num = atoi(cmd.chars() + i + 1);
  551.         int j = cmd.index('@', i + 1);
  552.         cmd.at(i, j - i + 1) = itostring(num);
  553.     }
  554.  
  555.     if (cmd.contains("set confirm"), 0)
  556.         confirm = false;    // Don't overwrite
  557.  
  558.     // Execute command.  This will result in new redo command(s)
  559.     // being passed to add_command().
  560.     Command c(cmd);
  561.     c.priority = COMMAND_PRIORITY_SYSTEM;
  562.     gdb_command(c);
  563.  
  564.     // Wait until this command is processed
  565.     syncCommandQueue();
  566.     }
  567.  
  568.     if (confirm)
  569.     gdb_question("set confirm on");
  570.  
  571.     if (!entry.has(UB_COMMAND) && !entry.has(UB_EXEC_COMMAND))
  572.     {
  573.     // We had an error during execution
  574.     return false;
  575.     }
  576.  
  577.     undoing = false;
  578.  
  579.     return true;
  580. }
  581.  
  582. bool UndoBuffer::process_state(UndoBufferEntry& entry)
  583. {
  584.     // Process previous state
  585.     string pos     = "";
  586.     string address = "";
  587.  
  588.     if (entry.has(UB_POS))
  589.     pos = entry[UB_POS];            // Generated by frame commands
  590.     else if (entry.has(UB_EXEC_POS))
  591.     pos = entry[UB_EXEC_POS];
  592.  
  593.     if (entry.has(UB_ADDRESS))
  594.     address = entry[UB_ADDRESS];    // Generated by frame commands
  595.     else if (entry.has(UB_EXEC_ADDRESS))
  596.     address = entry[UB_EXEC_ADDRESS];
  597.  
  598.     if (pos != "" || address != "")
  599.     {
  600.     string file = pos.before(':');
  601.     string line = pos.after(':');
  602.     source_view->goto_entry(file, atoi(line.chars()), address, true);
  603.     }
  604.     else
  605.     {
  606.     // No execution position found
  607.     source_view->clear_execution_position();
  608.     }
  609.  
  610.     // Process displays
  611.     StringArray displays;
  612.     StringArray values;
  613.     StringArray addrs;
  614.     for (StringStringAssocIter iter(entry); iter.ok(); iter++)
  615.     {
  616.     if (iter.key().contains(UB_DISPLAY_PREFIX, 0))
  617.     {
  618.         string name = iter.key().after(UB_DISPLAY_PREFIX);
  619.         const string& value = iter.value();
  620.         string addr = "";
  621.         if (entry.has(UB_DISPLAY_ADDRESS_PREFIX + name))
  622.         addr = entry[UB_DISPLAY_ADDRESS_PREFIX + name];
  623.  
  624.         displays += name;
  625.         values   += value;
  626.         addrs    += addr;
  627.     }
  628.     }
  629.     data_disp->update_displays(displays, values, addrs);
  630.  
  631.     string unknown = "(Unknown state)";
  632.  
  633.     // Process threads
  634.     string threads = unknown;
  635.     if (entry.has(UB_THREADS))
  636.     threads = entry[UB_THREADS];
  637.     source_view->process_threads(threads);
  638.  
  639.     // Process backtrace
  640.     string where = unknown;
  641.     if (entry.has(UB_WHERE))
  642.     where = entry[UB_WHERE];
  643.     source_view->process_where(where);
  644.  
  645.     // Process registers
  646.     string registers = unknown;
  647.     if (entry.has(UB_REGISTERS))
  648.     registers = entry[UB_REGISTERS];
  649.     source_view->process_registers(registers);
  650.  
  651.     locked = false;
  652.  
  653.     return true;
  654. }
  655.  
  656. bool UndoBuffer::process_frame(UndoBufferEntry& entry)
  657. {
  658.     // Process frame
  659.     if (entry.has(UB_FRAME))
  660.     {
  661.     string frame = entry[UB_FRAME];
  662.     source_view->process_frame(atoi(frame));
  663.     }
  664.     else
  665.     {
  666.     // No frame - assume current one
  667.     source_view->process_frame(0);
  668.     }
  669.  
  670.     return true;
  671. }
  672.  
  673. bool UndoBuffer::process_pos(UndoBufferEntry& entry)
  674. {
  675.     // Process position
  676.     string pos = "";
  677.     string address = "";
  678.  
  679.     if (entry.has(UB_POS))
  680.     pos = entry[UB_POS];
  681.  
  682.     if (entry.has(UB_ADDRESS))
  683.     address = entry[UB_ADDRESS];
  684.  
  685.     if (pos != "" || address != "")
  686.     {
  687.     string file = pos.before(':');
  688.     string line = pos.after(':');
  689.     source_view->goto_entry(file, atoi(line.chars()), address, false);
  690.     }
  691.  
  692.     return true;
  693. }
  694.  
  695. bool UndoBuffer::process_command(int entry)
  696. {
  697.     assert(OK());
  698.  
  699.     locked = true;
  700.  
  701.     current_entry = entry;
  702.     bool ok = process_command(history[entry]);
  703.  
  704.     locked = false;
  705.  
  706.     return ok;
  707. }
  708.  
  709. bool UndoBuffer::process_state(int entry)
  710. {
  711.     assert(OK());
  712.  
  713.     locked = true;
  714.  
  715.     current_entry = entry;
  716.     bool ok = process_state(history[entry]);
  717.  
  718.     locked = false;
  719.  
  720.     return ok;
  721. }
  722.  
  723. bool UndoBuffer::process_frame(int entry)
  724. {
  725.     assert(OK());
  726.  
  727.     locked = true;
  728.  
  729.     current_entry = entry;
  730.     bool ok = process_frame(history[entry]);
  731.  
  732.     locked = false;
  733.  
  734.     return ok;
  735. }
  736.  
  737. bool UndoBuffer::process_pos(int entry)
  738. {
  739.     assert(OK());
  740.  
  741.     locked = true;
  742.  
  743.     current_entry = entry;
  744.     bool ok = process_pos(history[entry]);
  745.  
  746.     locked = false;
  747.  
  748.     return ok;
  749. }
  750.  
  751. // Undo and redo commands
  752. void UndoBuffer::undo()
  753. {
  754.     if (locked)
  755.     return;
  756.  
  757.     if (history_position == 0)
  758.     {
  759.     set_status("Nothing to undo");
  760.     return;
  761.     }
  762.  
  763.     StatusDelay delay("Undoing " + undo_action());
  764.    
  765.     // Undo most recent command
  766.     bool have_command = history[history_position - 1].has_command();
  767.  
  768.     bool ok = process_command(history_position - 1);
  769.     if (ok && history_position > 1)
  770.     {
  771.     if (!have_command)
  772.         process_frame(history_position - 2);
  773.     process_pos(history_position - 2);
  774.     process_state(history_position - 2);
  775.     }
  776.  
  777.     if (!ok)
  778.     delay.outcome = "failed";
  779.  
  780.     history_position--;
  781.  
  782.     done(&delay);
  783. }
  784.  
  785. void UndoBuffer::redo()
  786. {
  787.     if (locked)
  788.     return;
  789.  
  790.     if (history_position >= history.size())
  791.     {
  792.     set_status("Nothing to redo");
  793.     return;
  794.     }
  795.  
  796.     StatusDelay delay("Redoing " + redo_action());
  797.  
  798.     // Redo next command and restore state
  799.     bool have_command = history[history_position].has_command();
  800.  
  801.     process_state(history_position);
  802.     if (!have_command)
  803.     process_frame(history_position);
  804.     process_pos(history_position);
  805.     bool ok = process_command(history_position);
  806.  
  807.     if (!ok)
  808.     delay.outcome = "failed";
  809.     
  810.     history_position++;
  811.  
  812.     done(&delay);
  813. }
  814.  
  815. // Action descriptions
  816. string UndoBuffer::action(const string& command)
  817. {
  818.     string c = command;
  819.  
  820.     while (c.contains(REMAP_COMMAND, 0))
  821.     c = c.after('\n');
  822.  
  823.     if (c.contains("graph ", 0))
  824.     c = c.after("graph ");
  825.  
  826.     if (c.contains(' '))
  827.     c = c.before(' ');
  828.  
  829.     strip_space(c);
  830.     c.capitalize();
  831.     return c;
  832. }
  833.  
  834. string UndoBuffer::undo_action()
  835. {
  836.     if (gdb->recording() || history_position == 0)
  837.     return NO_GDB_ANSWER;    // Nothing to undo
  838.  
  839.     const UndoBufferEntry& undo_entry = history[history_position - 1];
  840.  
  841.     if (!undo_entry.has_command() && history_position == 1)
  842.     return NO_GDB_ANSWER;    // No state to restore
  843.  
  844.     if (undo_entry.has(UB_SOURCE))
  845.     return action(undo_entry[UB_SOURCE]);
  846.  
  847.     return "";        // Generic action
  848. }
  849.  
  850. string UndoBuffer::redo_action()
  851. {
  852.     if (gdb->recording() || history_position >= history.size())
  853.     return NO_GDB_ANSWER;    // Nothing to redo
  854.  
  855.     const UndoBufferEntry& redo_entry = history[history_position];
  856.  
  857. #if 0
  858.     if (redo_entry.has(UB_EXEC_COMMAND))
  859.     return action(redo_entry[UB_EXEC_COMMAND]);
  860.     if (redo_entry.has(UB_COMMAND))
  861.     return action(redo_entry[UB_COMMAND]);
  862. #endif
  863.  
  864.     if (redo_entry.has(UB_SOURCE))
  865.     return action(redo_entry[UB_SOURCE]);
  866.  
  867.     return "";        // Generic action
  868. }
  869.  
  870. void UndoBuffer::showing_earlier_state(bool set, StatusMsg *msg)
  871. {
  872.     if (_showing_earlier_state == set)
  873.     {
  874.     if (set && msg != 0)
  875.         msg->outcome += ".  Still showing earlier program state";
  876.     return;
  877.     }
  878.  
  879.     _showing_earlier_state = set;
  880.  
  881.     if (msg != 0)
  882.     {
  883.     if (set)
  884.         msg->outcome = "failed.  Showing earlier program state instead";
  885.     else
  886.         msg->outcome += ".  Back in current program state";
  887.     }
  888.  
  889.     XtVaSetValues(data_disp->graph_edit, XtNdashedLines, set, NULL);
  890.     update_arg_buttons();
  891.     data_disp->refresh_args();
  892.     source_view->showing_earlier_state(set);
  893.  
  894.     if (!set)
  895.     {
  896.     // Re-activate user displays
  897.     data_disp->make_sane();
  898.     }
  899. }
  900.  
  901. // Refresh all settings
  902. void UndoBuffer::done(StatusMsg *msg)
  903. {
  904.     log();
  905.     assert(OK());
  906.  
  907.     // Check whether we're showing an earlier state
  908.     bool earlier = false;
  909.  
  910.     for (int i = history_position; i < history.size(); i++)
  911.     {
  912.     if (history[i].has_command())
  913.         continue;
  914.     if (history[i].has_pos())
  915.         continue;
  916.  
  917.     earlier = true;
  918.     break;
  919.     }
  920.  
  921.     showing_earlier_state(earlier, msg);
  922.     refresh_buttons();
  923. }
  924.  
  925. // Clear history
  926. void UndoBuffer::clear()
  927. {
  928.     static UndoBufferArray empty;
  929.     history          = empty;
  930.     history_position = 0;
  931.     locked           = false;
  932.  
  933.     done();
  934. }
  935.  
  936. // Clear execution positions - after a `run', for instance.
  937. // Keep commands only.
  938. void UndoBuffer::clear_exec_pos()
  939. {
  940.     if (locked)
  941.     return;
  942.  
  943.     UndoBufferArray new_history(history.size());
  944.     int old_history_position = history_position;
  945.     for (int i = 0; i < history.size(); i++)
  946.     {
  947.     const UndoBufferEntry& entry = history[i];
  948.     
  949.     if (entry.has(UB_COMMAND))
  950.     {
  951.         UndoBufferEntry new_entry;
  952.         new_entry[UB_COMMAND] = entry[UB_COMMAND];
  953.         if (entry.has(UB_SOURCE))
  954.         new_entry[UB_SOURCE] = entry[UB_SOURCE];
  955.  
  956.         new_history += new_entry;
  957.     }
  958.     else
  959.     {
  960.         // Ignore this entry
  961.         if (i < old_history_position)
  962.         history_position--;
  963.     }
  964.     }
  965.  
  966.     history = new_history;
  967.  
  968.     done();
  969. }
  970.  
  971. // Restore current program state
  972. void UndoBuffer::restore_current_state()
  973. {
  974.     if (locked)
  975.     return;
  976.  
  977.     assert(OK());
  978.  
  979.     if (!showing_earlier_state())
  980.     return;
  981.  
  982.     StatusDelay delay("Restoring state");
  983.  
  984.     clear_after_position();
  985.  
  986.     if (history.size() > 0)
  987.     process_state(history.size() - 1);
  988.  
  989.     history_position = history.size();
  990.  
  991.     done(&delay);
  992. }
  993.  
  994. // Return history of display NAME
  995. string UndoBuffer::display_history(const string& name)
  996. {
  997.     string key = UB_DISPLAY_PREFIX + name;
  998.     string answer = "";
  999.     string last_value = "";
  1000.  
  1001.     for (int i = 0; i < history.size(); i++)
  1002.     {
  1003.     const UndoBufferEntry& entry = history[i];
  1004.  
  1005.     if (entry.has(key) && entry[key] != last_value)
  1006.     {
  1007.         if (answer != "")
  1008.         answer += ", ";
  1009.         answer += entry[key];
  1010.         last_value = entry[key];
  1011.     }
  1012.     }
  1013.  
  1014.     return "history " + name + " = {" + answer + "}\n";
  1015. }
  1016.  
  1017. // Invariant
  1018. bool UndoBuffer::OK()
  1019. {
  1020.     // HISTORY_POSITION must be within bounds.
  1021.     assert(history_position >= 0);
  1022.     assert(history_position <= history.size());
  1023.  
  1024.     // Every entry must have some effect.
  1025.     for (int i = 0; i < history.size(); i++)
  1026.     {
  1027.     assert(has_effect(history[i]));
  1028.     }
  1029.  
  1030.     return true;
  1031. }
  1032.