CommandInterpreter.cc Source File

Back to the index.

CommandInterpreter.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2007-2010 Anders Gavare. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6  *
7  * 1. Redistributions of source code must retain the above copyright
8  * notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  * notice, this list of conditions and the following disclaimer in the
11  * documentation and/or other materials provided with the distribution.
12  * 3. The name of the author may not be used to endorse or promote products
13  * derived from this software without specific prior written permission.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27 
28 #include "assert.h"
29 #include <iostream>
30 
31 #include "GXemul.h"
32 #include "CommandInterpreter.h"
33 
34 // Built-in commands (autogenerated list by the configure script):
35 #include "../../commands_h.h"
36 
37 
39  : m_GXemul(owner)
40  , m_currentCommandCursorPosition(0)
41  , m_inEscapeSequence(false)
42  , m_historyEntryToCopyFrom(0)
43  , m_commandHistoryInsertPosition(0)
44  , m_commandHistoryMaxSize(100)
45 {
46  m_commandHistory.resize(m_commandHistoryMaxSize, "");
47 
48  // It would be bad to run without a working GXemul instance.
49  assert(m_GXemul != NULL);
50 
51  // Add the default built-in commands:
52  // (This list is autogenerated by the configure script.)
53 #include "../../commands.h"
54 }
55 
56 
58 {
59  m_commands[command->GetCommandName()] = command;
60 }
61 
62 
64 {
65  return m_commands;
66 }
67 
68 
70 {
71  if (command == "")
72  return m_commandHistoryInsertPosition;
73 
74  size_t lastInsertedPosition =
75  (m_commandHistoryInsertPosition - 1 + m_commandHistoryMaxSize)
76  % m_commandHistoryMaxSize;
77 
78  if (m_commandHistory[lastInsertedPosition] == command)
79  return m_commandHistoryInsertPosition;
80 
81  m_commandHistory[m_commandHistoryInsertPosition ++] = command;
82  m_commandHistoryInsertPosition %= m_commandHistoryMaxSize;
83 
84  return m_commandHistoryInsertPosition;
85 }
86 
87 
88 string CommandInterpreter::GetHistoryLine(int nStepsBack) const
89 {
90  if (nStepsBack == 0)
91  return "";
92 
93  int index = (m_commandHistoryInsertPosition - nStepsBack +
94  m_commandHistoryMaxSize) % m_commandHistoryMaxSize;
95 
96  return m_commandHistory[index];
97 }
98 
99 
100 bool CommandInterpreter::TabComplete(string& commandString,
101  size_t& cursorPosition, bool visibleShowAvailable)
102 {
103  string wordToComplete;
104  bool firstWordOnLine = true;
105 
106  size_t pos = cursorPosition;
107  while (pos > 0) {
108  pos --;
109  if (commandString[pos] == ' ')
110  break;
111  wordToComplete = commandString[pos] + wordToComplete;
112  }
113 
114  while (pos > 0) {
115  pos --;
116  if (commandString[pos] != ' ') {
117  firstWordOnLine = false;
118  break;
119  }
120  }
121 
122  bool completeCommands = firstWordOnLine;
123 
124  if (wordToComplete == "") {
125  if (!visibleShowAvailable)
126  return false;
127 
128  // Show all available words:
129 
130  if (completeCommands) {
131  // All available commands:
132  vector<string> allCommands;
133  for (Commands::const_iterator it = m_commands.begin();
134  it != m_commands.end(); ++it)
135  allCommands.push_back(it->first);
136 
137  ShowAvailableWords(allCommands);
138  }
139 
140  ShowAvailableWords(m_GXemul->GetRootComponent()->
141  FindPathByPartialMatch(""));
142 
143  return false;
144  }
145 
146  vector<string> matches;
147 
148  matches = m_GXemul->GetRootComponent()->
149  FindPathByPartialMatch(wordToComplete, true);
150 
151  if (completeCommands) {
152  Commands::const_iterator it = m_commands.begin();
153  for (; it != m_commands.end(); ++it) {
154  const string& commandName = it->first;
155  if (commandName.substr(0, wordToComplete.length())
156  == wordToComplete) {
157  matches.push_back(commandName);
158  }
159  }
160  }
161 
162  if (matches.size() == 0)
163  return false;
164 
165  string completedWord;
166 
167  // Single match, or multiple matches?
168  if (matches.size() == 1) {
169  // Insert the rest of the command name into the input line:
170  completedWord = matches[0];
171  } else {
172  // Figure out the longest possible match, and add that:
173  size_t i, n = matches.size();
174  for (size_t pos2 = 0; ; pos2 ++) {
175  if (pos2 >= matches[0].length())
176  break;
177  stringchar ch = matches[0][pos2];
178  for (i=1; i<n; i++) {
179  if (matches[i][pos2] != ch)
180  break;
181  }
182  if (i == n)
183  completedWord += ch;
184  else
185  break;
186  }
187 
188  // Show available words, so the user knows what there
189  // is to choose from.
190  if (visibleShowAvailable)
191  ShowAvailableWords(matches);
192  }
193 
194  // Erase the old (incomplete) word, and insert the completed word:
195  if (!completedWord.empty()) {
196  cursorPosition -= wordToComplete.length();
197  commandString.erase(cursorPosition, wordToComplete.length());
198  commandString.insert(cursorPosition, completedWord);
199  cursorPosition += completedWord.length();
200  }
201 
202  // Special case: If there was a single match, and we are at the end
203  // of the line, and this was a command, then add a space (" "). This
204  // behaviour feels better, and this is how other tab completors seems
205  // to work.
206  //
207  // NOTE: Don't add a space after component paths. Usually the user
208  // will want to type e.g. "cpu" + TAB, and get
209  // "root.machine0.mainbus0.cpu0" with no space, and then be able to
210  // add ".unassemble" or so manually.
211  if (matches.size() == 1 && cursorPosition == commandString.length()) {
212  bool isCommand = false;
213  Commands::const_iterator it = m_commands.begin();
214  for (; it != m_commands.end(); ++it) {
215  const string& commandName = it->first;
216  if (commandName == matches[0]) {
217  isCommand = true;
218  break;
219  }
220  }
221 
222  if (isCommand) {
223  commandString += " ";
224  cursorPosition ++;
225  }
226  }
227 
228  return matches.size() == 1;
229 }
230 
231 
232 bool CommandInterpreter::TabCompleteWithSubname(string& commandString,
233  size_t& cursorPosition, bool visibleShowAvailable)
234 {
235  if (cursorPosition == 0)
236  return false;
237 
238  int nStepsBack = 1;
239  size_t pos = cursorPosition - 1;
240 
241  while (pos > 0 && commandString[pos] != '.') {
242  pos --;
243  nStepsBack ++;
244  }
245 
246  if (pos == 0)
247  return false;
248 
249  // Here, pos is the position of the dot:
250  //
251  // cpu.u
252  // ^
253 
254  bool success = TabComplete(commandString, pos, visibleShowAvailable);
255  if (!success)
256  return false;
257 
258  // pos is now the new position of the dot:
259  //
260  // root.machine0.mainbus0.cpu.u
261  // ^
262 
263  // Look up the component:
264  int startOfComponentName = pos;
265  while (startOfComponentName >= 0 &&
266  commandString[startOfComponentName] != ' ')
267  -- startOfComponentName;
268 
269  if (startOfComponentName < 0)
270  startOfComponentName = 0;
271 
272  string componentName = commandString.substr(startOfComponentName,
273  pos - startOfComponentName);
274 
275  // std::cerr << "[" << componentName << "]\n";
276  refcount_ptr<Component> component = m_GXemul->GetRootComponent()->
277  LookupPath(componentName);
278 
279  cursorPosition = pos + nStepsBack;
280 
281  if (component.IsNULL())
282  return false;
283 
284  // Figure out the method or state name to expand:
285  size_t startOfMethodName = pos + 1;
286  size_t methodNameLen = 0;
287  while (startOfMethodName + methodNameLen < cursorPosition &&
288  commandString[startOfMethodName+methodNameLen] != ' ')
289  methodNameLen ++;
290 
291  string methodName = commandString.substr(startOfMethodName,
292  methodNameLen);
293 
294  // std::cerr << "{" << methodName << "}\n";
295 
296  vector<string> names;
297  vector<string> matchingNames;
298  component->GetMethodNames(names);
299  component->GetVariableNames(names);
300  int nrOfMatches = 0;
301  if (methodName.length() != 0) {
302  for (size_t i=0; i<names.size(); ++i) {
303  if (names[i].substr(0, methodName.length()) ==
304  methodName) {
305  ++ nrOfMatches;
306  matchingNames.push_back(names[i]);
307  }
308  }
309  } else {
310  matchingNames = names;
311  }
312 
313  if (matchingNames.size() == 0)
314  return false;
315 
316  // Replace the short name with a match as long as possible, e.g.
317  // "memo" will be replaced by "memoryMapped", if names
318  // "memoryMappedAddr" and "memoryMappedSize" are available.
319  string longestPossibleMatch = "";
320  size_t i, n = matchingNames.size();
321  for (size_t pos2 = 0; ; pos2 ++) {
322  if (pos2 >= matchingNames[0].length())
323  break;
324  stringchar ch = matchingNames[0][pos2];
325  for (i=1; i<n; i++) {
326  if (matchingNames[i][pos2] != ch)
327  break;
328  }
329  if (i == n)
330  longestPossibleMatch += ch;
331  else
332  break;
333  }
334 
335  commandString.replace(startOfMethodName, methodNameLen,
336  longestPossibleMatch);
337  cursorPosition += longestPossibleMatch.length() - methodNameLen;
338 
339  // A single match? Then we succeeded.
340  if (nrOfMatches == 1)
341  return true;
342 
343  // Show available methods and variable names:
344  if (visibleShowAvailable) {
345  vector<string> allNames;
346  vector<string> matchingNames2;
347 
348  component->GetMethodNames(allNames);
349  for (size_t j=0; j<allNames.size(); ++j) {
350  if (methodName.length() == 0 ||
351  allNames[j].substr(0, methodName.length()) == methodName)
352  matchingNames2.push_back(allNames[j]);
353  }
354 
355  if (matchingNames2.size() > 0) {
356  m_GXemul->GetUI()->ShowDebugMessage("\nMethods:");
357  ShowAvailableWords(matchingNames2);
358  }
359  }
360  if (visibleShowAvailable) {
361  vector<string> allNames;
362  vector<string> matchingNames2;
363 
364  component->GetVariableNames(allNames);
365  for (size_t j=0; j<allNames.size(); ++j) {
366  if (methodName.length() == 0 ||
367  allNames[j].substr(0, methodName.length()) == methodName)
368  matchingNames2.push_back(allNames[j]);
369  }
370 
371  if (matchingNames2.size() > 0) {
372  m_GXemul->GetUI()->ShowDebugMessage("\nVariables:");
373  ShowAvailableWords(matchingNames2);
374  }
375  }
376 
377  return false;
378 }
379 
380 
382 {
383  if (m_inEscapeSequence) {
384  m_escapeSequence += key;
385 
386  // Handle some common escape sequences, and convert
387  // them into simpler 1-byte keys/characters:
388 
389  if (m_escapeSequence == "[C") { // right
390  m_inEscapeSequence = false;
391  AddKey('\6'); // CTRL-F
392  } else if (m_escapeSequence == "[D") { // left
393  m_inEscapeSequence = false;
394  AddKey('\2'); // CTRL-B
395  } else if (m_escapeSequence == "OH") { // home
396  m_inEscapeSequence = false;
397  AddKey('\1'); // CTRL-A
398  } else if (m_escapeSequence == "[H") { // home
399  m_inEscapeSequence = false;
400  AddKey('\1'); // CTRL-A
401  } else if (m_escapeSequence == "OF") { // end
402  m_inEscapeSequence = false;
403  AddKey('\5'); // CTRL-E
404  } else if (m_escapeSequence == "[F") { // end
405  m_inEscapeSequence = false;
406  AddKey('\5'); // CTRL-E
407  } else if (m_escapeSequence == "[A") { // up
408  m_inEscapeSequence = false;
409  AddKey('\20'); // CTRL-P
410  } else if (m_escapeSequence == "[B") { // down
411  m_inEscapeSequence = false;
412  AddKey('\16'); // CTRL-N
413  } else if (m_escapeSequence.length() > 2) {
414  // Let's bail out of escape sequence handling...
415  //
416  // Note: If you trace execution here for some key that
417  // you feel _should_ be handled, please send me a mail
418  // about it.
419  //
420  m_inEscapeSequence = false;
421  AddKey('?');
422  }
423 
424  return false;
425  }
426 
427  switch (key) {
428 
429  case '\0':
430  // Add nothing, just reshow/update the command buffer.
431  break;
432 
433  case '\1': // CTRL-A: move to start of line
434  m_currentCommandCursorPosition = 0;
435  break;
436 
437  case '\2': // CTRL-B: move back (left)
438  if (m_currentCommandCursorPosition > 0)
439  m_currentCommandCursorPosition --;
440  break;
441 
442  case '\4': // CTRL-D: remove the character to the right
443  if (m_currentCommandCursorPosition <
444  m_currentCommandString.length())
445  m_currentCommandString.erase(
446  m_currentCommandCursorPosition, 1);
447  break;
448 
449  case '\5': // CTRL-E: move to end of line
450  m_currentCommandCursorPosition =
451  m_currentCommandString.length();
452  break;
453 
454  case '\6': // CTRL-F: move forward (right)
455  if (m_currentCommandCursorPosition <
456  m_currentCommandString.length())
457  m_currentCommandCursorPosition ++;
458  break;
459 
460  case '\13': // CTRL-K: kill to end of line
461  ClearCurrentInputLineVisually();
462  m_currentCommandString.resize(m_currentCommandCursorPosition);
463  break;
464 
465  case '\16': // CTRL-N: next in history (down)
466  ClearCurrentInputLineVisually();
467 
468  m_historyEntryToCopyFrom --;
469  if (m_historyEntryToCopyFrom < 0)
470  m_historyEntryToCopyFrom = 0;
471 
472  m_currentCommandString =
473  GetHistoryLine(m_historyEntryToCopyFrom);
474  m_currentCommandCursorPosition =
475  m_currentCommandString.length();
476  break;
477 
478  case '\20': // CTRL-P: previous in history (up)
479  ClearCurrentInputLineVisually();
480 
481  m_historyEntryToCopyFrom ++;
482  m_currentCommandString =
483  GetHistoryLine(m_historyEntryToCopyFrom);
484 
485  // We went too far? Then back down.
486  if (m_currentCommandString == "") {
487  m_historyEntryToCopyFrom --;
488  m_currentCommandString =
489  GetHistoryLine(m_historyEntryToCopyFrom);
490  }
491  m_currentCommandCursorPosition =
492  m_currentCommandString.length();
493  break;
494 
495  case '\24': // CTRL-T: show status
496  m_GXemul->GetUI()->ShowDebugMessage("\n");
497  RunCommand("status");
498  break;
499 
500  case '\27': // CTRL-W: remove current word (backspacing)
501  ClearCurrentInputLineVisually();
502 
503  // 1. Remove any spaces left to the cursor.
504  while (m_currentCommandCursorPosition > 0) {
505  if (m_currentCommandString[
506  m_currentCommandCursorPosition-1] == ' ') {
507  m_currentCommandCursorPosition --;
508  m_currentCommandString.erase(
509  m_currentCommandCursorPosition, 1);
510  } else {
511  break;
512  }
513  }
514 
515  // 2. Remove non-spaces left to the cusror, either until
516  // the cursor is at position 0, or until there is a
517  // space again.
518  while (m_currentCommandCursorPosition > 0) {
519  if (m_currentCommandString[
520  m_currentCommandCursorPosition-1] != ' ') {
521  m_currentCommandCursorPosition --;
522  m_currentCommandString.erase(
523  m_currentCommandCursorPosition, 1);
524  } else {
525  break;
526  }
527  }
528 
529  break;
530 
531  case '\177': // ASCII 127 (octal 177) = del
532  case '\b': // backspace
533  if (m_currentCommandCursorPosition > 0) {
534  m_currentCommandCursorPosition --;
535  m_currentCommandString.erase(
536  m_currentCommandCursorPosition, 1);
537  }
538  break;
539 
540  case '\33':
541  // Escape key handling:
542  m_inEscapeSequence = true;
543  m_escapeSequence = "";
544  break;
545 
546  case '\t':
547  // Tab completion, with visible word hints:
548  {
549  bool success = TabComplete(m_currentCommandString,
550  m_currentCommandCursorPosition, true);
551 
552  // Attempt to expand component-name + "." + optional
553  // method or variable name, "cpu.u", if the first
554  // tab-completion failed.
555  if (!success) {
556  TabCompleteWithSubname(m_currentCommandString,
557  m_currentCommandCursorPosition, true);
558  }
559  }
560  break;
561 
562  case '\n':
563  case '\r':
564  // Newline executes the command, if it is non-empty:
565  m_GXemul->GetUI()->InputLineDone();
566 
567  if (!m_currentCommandString.empty()) {
568  AddLineToCommandHistory(m_currentCommandString);
569  bool ignoredResult;
570  RunCommand(m_currentCommandString, &ignoredResult);
572  } else if (m_mayBeReexecuted != "") {
573  RunCommand(m_mayBeReexecuted);
574  }
575  break;
576 
577  default:
578  // Most other keys just add/insert a character into the command
579  // string:
580  if (key >= ' ') {
581  m_currentCommandString.insert(
582  m_currentCommandCursorPosition, 1, key);
583  m_currentCommandCursorPosition ++;
584  }
585  }
586 
587  if (key != '\n' && key != '\r')
589 
590  // Return value is true for newline/cr, false otherwise:
591  return key == '\n' || key == '\r';
592 }
593 
594 
595 void CommandInterpreter::ShowAvailableWords(const vector<string>& words)
596 {
597  m_GXemul->GetUI()->ShowDebugMessage("\n");
598 
599  const size_t n = words.size();
600  size_t i;
601 
602  // Find the longest word first:
603  size_t maxLen = 0;
604  for (i=0; i<n; ++i) {
605  size_t len = words[i].length();
606  if (len > maxLen)
607  maxLen = len;
608  }
609 
610  maxLen += 4;
611 
612  // Generate msg:
613  std::stringstream msg;
614  size_t lineLen = 0;
615  for (i=0; i<n; ++i) {
616  if (lineLen == 0)
617  msg << " ";
618 
619  size_t len = words[i].length();
620  msg << words[i];
621  lineLen += len;
622 
623  for (size_t j=len; j<maxLen; j++) {
624  msg << " ";
625  lineLen ++;
626  }
627 
628  if (lineLen >= 77 - maxLen || i == n-1) {
629  msg << "\n";
630  lineLen = 0;
631  }
632  }
633 
634  m_GXemul->GetUI()->ShowDebugMessage(msg.str());
635 }
636 
637 
639 {
640  m_GXemul->GetUI()->RedisplayInputLine(
641  m_currentCommandString, m_currentCommandCursorPosition);
642 }
643 
644 
645 void CommandInterpreter::ClearCurrentInputLineVisually()
646 {
647  string clearString = "";
648  clearString.insert((size_t)0, m_currentCommandString.length(), ' ');
649 
650  m_GXemul->GetUI()->RedisplayInputLine(
651  clearString, m_currentCommandCursorPosition);
652 }
653 
654 
656 {
657  m_currentCommandString = "";
658  m_currentCommandCursorPosition = 0;
659  m_historyEntryToCopyFrom = 0;
660 }
661 
662 
663 static void SplitIntoWords(const string& commandOrig,
664  string& commandName, vector<string>& arguments)
665 {
666  string command = commandOrig;
667 
668  arguments.clear();
669  commandName = "";
670  size_t pos = 0;
671 
672  // Surround '=' with white spaces, except when inside parentheses...
673  // NOTE/TODO: This will not be needed in the future (?), when a real
674  // expression evaluator has been [re]implemented.
675  int insideParenthesesCount = 0;
676  while (pos < command.length()) {
677  if (command[pos] == '(')
678  insideParenthesesCount ++;
679  if (command[pos] == ')')
680  insideParenthesesCount --;
681  if (command[pos] == '=' && insideParenthesesCount == 0) {
682  command.replace(pos, 1, " = ");
683  pos ++;
684  }
685 
686  pos ++;
687  }
688 
689  // Split command into words, ignoring all whitespace:
690  pos = 0;
691  while (pos < command.length()) {
692  // Skip initial whitespace:
693  while (pos < command.length() && command[pos] == ' ')
694  pos ++;
695 
696  if (pos >= command.length())
697  break;
698 
699  // This is a new word. Add all characters, until
700  // whitespace or end of string:
701  string newWord = "";
702  while (pos < command.length() && command[pos] != ' ') {
703  newWord += command[pos];
704  pos ++;
705  }
706 
707  if (commandName.empty())
708  commandName = newWord;
709  else
710  arguments.push_back(newWord);
711  }
712 }
713 
714 
715 void CommandInterpreter::VariableAssignment(const string& componentPath,
716  const string& variableName, const string& expression)
717 {
718  refcount_ptr<Component> component = m_GXemul->GetRootComponent()->
719  LookupPath(componentPath);
720 
721  StateVariable* var = component->GetVariable(variableName);
722  if (var == NULL) {
723  m_GXemul->GetUI()->ShowDebugMessage("Unknown variable '" +
724  variableName + "'? (Internal error.)\n");
725  throw std::exception();
726  }
727 
728  const refcount_ptr<Component> lightClone =
729  m_GXemul->GetRootComponent()->LightClone();
730 
731  // Attempt to assign the expression to the variable:
732  if (!component->SetVariableValue(variableName, expression))
733  m_GXemul->GetUI()->ShowDebugMessage("Assignment failed.\n");
734 
735  // ... and print all state change (in case a write to a variable had
736  // side effects, then this makes sure that the user sees all such side
737  // effects):
738  stringstream changeMessages;
739  m_GXemul->GetRootComponent()->DetectChanges(lightClone, changeMessages);
740 
741  string msg = changeMessages.str();
742  if (msg == "")
743  msg = "(No state change.)\n";
744 
745  m_GXemul->GetUI()->ShowDebugMessage(msg);
746 }
747 
748 
749 bool CommandInterpreter::RunComponentMethod(
750  const string& componentPathAndMethod, const vector<string>& arguments)
751 {
752  // Note: componentPathAndMethod may or may not have a method at
753  // the end!
754 
755  // Make several "smart" guesses:
756  refcount_ptr<Component> component;
757  string componentPath;
758  string methodName;
759 
760  do {
761  // 1. Assume that componentPathAndMethod is a full component
762  // path:
763  component = m_GXemul->GetRootComponent()->
764  LookupPath(componentPathAndMethod);
765  if (!component.IsNULL())
766  break;
767 
768  // 2. Assume that componentPathAndMethod is a component
769  // path, but it is not tab-completed yet:
770  string tabcompleted = componentPathAndMethod;
771  size_t tmpLen = tabcompleted.length();
772  if (TabComplete(tabcompleted, tmpLen)) {
773  component = m_GXemul->GetRootComponent()->
774  LookupPath(tabcompleted);
775  if (!component.IsNULL())
776  break;
777  }
778 
779  // If there is no period in the name, we can't continue
780  // with the following guesses.
781  if (componentPathAndMethod.find(".") == string::npos)
782  break;
783 
784  size_t pos = componentPathAndMethod.find_last_of('.');
785 
786  // 3. Assume full component path + ".method":
787  componentPath = componentPathAndMethod.substr(0, pos);
788  component = m_GXemul->GetRootComponent()->
789  LookupPath(componentPath);
790  if (!component.IsNULL()) {
791  methodName = componentPathAndMethod.substr(pos+1);
792  break;
793  }
794 
795  // 4. Assume non-tab-completed component path + ".method":
796  tabcompleted = componentPath;
797  tmpLen = tabcompleted.length();
798  if (TabComplete(tabcompleted, tmpLen)) {
799  component = m_GXemul->GetRootComponent()->
800  LookupPath(tabcompleted);
801  if (!component.IsNULL()) {
802  methodName =
803  componentPathAndMethod.substr(pos+1);
804  break;
805  }
806  }
807  } while (false);
808 
809  if (component.IsNULL())
810  return false;
811 
812  // No method given? Then show the component tree, and the component's
813  // state variables, and return.
814  if (methodName.empty()) {
815  m_GXemul->GetUI()->ShowDebugMessage(
816  component->GenerateTreeDump(""));
817 
818  // Retrieve the names of all the state variables:
819  vector<string> variableNames;
820  component->GetVariableNames(variableNames);
821 
822  stringstream ss;
823  ss << "\n";
824 
825  size_t maxLen = 0;
826  size_t i;
827  for (i=0; i<variableNames.size(); i++)
828  if (variableNames[i].length() > maxLen)
829  maxLen = variableNames[i].length();
830 
831  for (i=0; i<variableNames.size(); i++) {
832  const string& name = variableNames[i];
833  if (name == "name" || name == "template")
834  continue;
835 
836  ss << " " << name;
837  for (size_t j=name.length(); j<=maxLen; j++)
838  ss << " ";
839 
840  const StateVariable* var = component->GetVariable(name);
841  if (var == NULL)
842  ss << "= (unknown?)";
843  else
844  ss << "= " << var->ToString();
845 
846  ss << "\n";
847  }
848 
849  m_GXemul->GetUI()->ShowDebugMessage(ss.str());
850 
851  return true;
852  }
853 
854  // Now, it is possible that methodName is incomplete, so it has to
855  // be looked up as well:
856  vector<string> names;
857  component->GetMethodNames(names);
858  int nrOfMatches = 0;
859  string fullMatch;
860  for (size_t i=0; i<names.size(); ++i) {
861  if (names[i].substr(0, methodName.length()) == methodName) {
862  ++ nrOfMatches;
863  fullMatch = names[i];
864  }
865  }
866 
867  if (nrOfMatches == 1) {
868  // Execute it!
869  component->ExecuteMethod(m_GXemul, fullMatch, arguments);
870 
871  if (component->MethodMayBeReexecutedWithoutArgs(fullMatch))
872  m_mayBeReexecuted = componentPathAndMethod;
873 
874  return true;
875  }
876 
877  // Try variable names:
878  names.clear();
879  component->GetVariableNames(names);
880  nrOfMatches = 0;
881  fullMatch = "";
882  for (size_t i=0; i<names.size(); ++i) {
883  // Exact match?
884  if (names[i] == methodName) {
885  nrOfMatches = 1;
886  fullMatch = names[i];
887  break;
888  }
889 
890  // Partial match?
891  if (names[i].substr(0, methodName.length()) == methodName) {
892  ++ nrOfMatches;
893  fullMatch = names[i];
894  }
895  }
896 
897  stringstream ss;
898 
899  if (nrOfMatches == 1) {
900  if (arguments.size() > 0) {
901  if (arguments.size() == 1 ||
902  arguments[0] != "=") {
903  // TODO: Printing expressions, such as
904  // cpu.pc + 4
905 
906  m_GXemul->GetUI()->ShowDebugMessage(
907  "Syntax error. Variable assignment syntax"
908  " is:\n <variable> = <expression>\n");
909  return true;
910  }
911 
912  const StateVariable* var =
913  component->GetVariable(fullMatch);
914  if (var == NULL) {
915  m_GXemul->GetUI()->ShowDebugMessage(
916  "Unknown variable.\n");
917  return true;
918  }
919 
920  string assignment;
921  for (size_t i=1; i<arguments.size(); ++i)
922  assignment += arguments[i] + " ";
923 
924  VariableAssignment(component->GeneratePath(), fullMatch, assignment);
925 
926  return true;
927  }
928 
929  // Print the variable's name and value:
930  ss << fullMatch;
931 
932  const StateVariable* var = component->GetVariable(fullMatch);
933  if (var == NULL)
934  ss << " = (unknown variable?)";
935  else
936  ss << " = " << var->ToString();
937 
938  ss << "\n";
939 
940  m_GXemul->GetUI()->ShowDebugMessage(ss.str());
941 
942  return true;
943  }
944 
945  if (nrOfMatches > 1)
946  ss << methodName << ": ambiguous method or variable name of "
947  << component->GeneratePath() << ".\n";
948  else
949  ss << methodName << ": not a method or variable of "
950  << component->GeneratePath() << ".\n";
951 
952  m_GXemul->GetUI()->ShowDebugMessage(ss.str());
953 
954  return false;
955 }
956 
957 
958 bool CommandInterpreter::RunCommand(const string& command, bool* pSuccess)
959 {
960  string commandName;
961  vector<string> arguments;
962  SplitIntoWords(command, commandName, arguments);
963 
964  m_mayBeReexecuted = "";
965 
966  m_GXemul->GetUI()->ShowCommandMessage(command);
967 
968  // Find the command...
969  Commands::iterator it = m_commands.find(commandName);
970  if (it == m_commands.end()) {
971  // Not found? Then try to tab-complete the name...
972  string commandTabCompleted = commandName;
973  size_t tmpCursorPos = commandTabCompleted.length();
974  TabComplete(commandTabCompleted, tmpCursorPos);
975 
976  // remove any trailing space(s):
977  while (commandTabCompleted.length() > 0 &&
978  commandTabCompleted[commandTabCompleted.length()-1] == ' ')
979  commandTabCompleted.erase(
980  commandTabCompleted.length() - 1);
981 
982  // ... and try again:
983  it = m_commands.find(commandTabCompleted);
984  if (it == m_commands.end()) {
985  // If this is a component name [with an optional
986  // method name], then execute a method on it.
987  if (RunComponentMethod(commandName, arguments))
988  return true;
989  m_GXemul->GetUI()->ShowDebugMessage(commandName +
990  ": unknown command. Type help for help.\n");
991  return false;
992  }
993  }
994 
995  if (arguments.size() != 0 && (it->second)->GetArgumentFormat() == "") {
996  m_GXemul->GetUI()->ShowDebugMessage(commandName +
997  " takes no arguments. Type help " + commandName +
998  " for help on the syntax.\n");
999  return false;
1000  }
1001 
1002  // ... and execute it:
1003  bool success = (it->second)->Execute(*m_GXemul, arguments);
1004  if (pSuccess != NULL) {
1005  *pSuccess = success;
1006  } else {
1007  if (!success) {
1008  throw UnitTestFailedException("apa");
1009  }
1010  }
1011 
1012  if ((it->second)->MayBeReexecutedWithoutArgs())
1013  m_mayBeReexecuted = it->first;
1014 
1015  return true;
1016 }
1017 
1018 
1020 {
1021  return m_currentCommandString;
1022 }
1023 
1024 
1025 /*****************************************************************************/
1026 
1027 
1028 #ifdef WITHUNITTESTS
1029 
1030 static void Test_CommandInterpreter_AddKey_ReturnValue()
1031 {
1032  GXemul gxemul;
1034 
1035  UnitTest::Assert("addkey of regular char should return false",
1036  ci.AddKey('a') == false);
1037 
1038  UnitTest::Assert("addkey of nul char should return false",
1039  ci.AddKey('\0') == false);
1040 
1041  UnitTest::Assert("addkey of newline should return true",
1042  ci.AddKey('\n') == true);
1043 
1044  UnitTest::Assert("addkey of carriage return should return true too",
1045  ci.AddKey('\r') == true);
1046 }
1047 
1048 static void Test_CommandInterpreter_KeyBuffer()
1049 {
1050  GXemul gxemul;
1052 
1053  UnitTest::Assert("buffer should initially be empty",
1054  ci.GetCurrentCommandBuffer() == "");
1055 
1056  ci.AddKey('a'); // normal char
1057 
1058  UnitTest::Assert("buffer should contain 'a'",
1059  ci.GetCurrentCommandBuffer() == "a");
1060 
1061  ci.AddKey('\0'); // nul char should have no effect
1062 
1063  UnitTest::Assert("buffer should still contain only 'a'",
1064  ci.GetCurrentCommandBuffer() == "a");
1065 
1066  ci.AddKey('A'); // multiple chars
1067  ci.AddKey('B');
1068  UnitTest::Assert("buffer should contain 'aAB'",
1069  ci.GetCurrentCommandBuffer() == "aAB");
1070 
1071  ci.AddKey('\177'); // del
1072 
1073  UnitTest::Assert("buffer should contain 'aA' (del didn't work?)",
1074  ci.GetCurrentCommandBuffer() == "aA");
1075 
1076  ci.AddKey('\b'); // backspace
1077 
1078  UnitTest::Assert("buffer should contain 'a' again (BS didn't work)",
1079  ci.GetCurrentCommandBuffer() == "a");
1080 
1081  ci.AddKey('\b');
1082 
1083  UnitTest::Assert("buffer should now be empty '' again",
1084  ci.GetCurrentCommandBuffer() == "");
1085 
1086  ci.AddKey('\b'); // cannot be emptier than... well... empty :)
1087 
1088  UnitTest::Assert("buffer should still be empty",
1089  ci.GetCurrentCommandBuffer() == "");
1090 
1091  ci.AddKey('a');
1092 
1093  UnitTest::Assert("buffer should contain 'a' again",
1094  ci.GetCurrentCommandBuffer() == "a");
1095 
1096  ci.AddKey('Q');
1097 
1098  UnitTest::Assert("buffer should contain 'aQ'",
1099  ci.GetCurrentCommandBuffer() == "aQ");
1100 
1101  ci.AddKey('\n'); // newline should execute the command
1102 
1103  UnitTest::Assert("buffer should be empty after executing '\\n'",
1104  ci.GetCurrentCommandBuffer() == "");
1105 
1106  ci.AddKey('Z');
1107  ci.AddKey('Q');
1108 
1109  UnitTest::Assert("new command should have been possible",
1110  ci.GetCurrentCommandBuffer() == "ZQ");
1111 
1112  ci.AddKey('\r'); // carriage return should work like newline
1113 
1114  UnitTest::Assert("buffer should be empty after executing '\\r'",
1115  ci.GetCurrentCommandBuffer() == "");
1116 }
1117 
1118 static void Test_CommandInterpreter_KeyBuffer_CursorMovement()
1119 {
1120  GXemul gxemul;
1122 
1123  ci.AddKey('A');
1124  ci.AddKey('B');
1125  ci.AddKey('C');
1126  ci.AddKey('D');
1127  ci.AddKey('E');
1128  UnitTest::Assert("buffer should contain 'ABCDE'",
1129  ci.GetCurrentCommandBuffer(), "ABCDE");
1130 
1131  ci.AddKey('\2'); // CTRL-B should move back (left)
1132  ci.AddKey('\2');
1133  ci.AddKey('\2');
1134  UnitTest::Assert("buffer should still contain 'ABCDE'",
1135  ci.GetCurrentCommandBuffer(), "ABCDE");
1136 
1137  ci.AddKey('\b');
1138  UnitTest::Assert("buffer should now contain 'ACDE'",
1139  ci.GetCurrentCommandBuffer(), "ACDE");
1140 
1141  ci.AddKey('\6'); // CTRL-F should move forward (right)
1142  ci.AddKey('\6');
1143  UnitTest::Assert("buffer should still contain 'ACDE'",
1144  ci.GetCurrentCommandBuffer(), "ACDE");
1145 
1146  ci.AddKey('\b');
1147  UnitTest::Assert("buffer should now contain 'ACE'",
1148  ci.GetCurrentCommandBuffer(), "ACE");
1149 
1150  ci.AddKey('\1'); // CTRL-A should move to start of line
1151  UnitTest::Assert("buffer should still contain 'ACE'",
1152  ci.GetCurrentCommandBuffer(), "ACE");
1153 
1154  ci.AddKey('1');
1155  ci.AddKey('2');
1156  UnitTest::Assert("buffer should now contain '12ACE'",
1157  ci.GetCurrentCommandBuffer(), "12ACE");
1158 
1159  ci.AddKey('\5'); // CTRL-E should move to end of line
1160  UnitTest::Assert("buffer should still contain '12ACE'",
1161  ci.GetCurrentCommandBuffer(), "12ACE");
1162 
1163  ci.AddKey('x');
1164  ci.AddKey('y');
1165  UnitTest::Assert("buffer should now contain '12ACExy'",
1166  ci.GetCurrentCommandBuffer(), "12ACExy");
1167 
1168  ci.AddKey('\1'); // CTRL-A move to start of line again
1169  ci.AddKey('\6'); // CTRL-F move to the right
1170  ci.AddKey('\4'); // CTRL-D should remove character to the right
1171  UnitTest::Assert("buffer should now contain '1ACExy'",
1172  ci.GetCurrentCommandBuffer(), "1ACExy");
1173 }
1174 
1175 static void Test_CommandInterpreter_KeyBuffer_CtrlK()
1176 {
1177  GXemul gxemul;
1179 
1180  ci.AddKey('A');
1181  ci.AddKey('B');
1182  ci.AddKey('C');
1183  ci.AddKey('D');
1184  ci.AddKey('E');
1185  UnitTest::Assert("buffer should contain 'ABCDE'",
1186  ci.GetCurrentCommandBuffer(), "ABCDE");
1187 
1188  ci.AddKey('\2'); // CTRL-B should move back (left)
1189  ci.AddKey('\2');
1190  UnitTest::Assert("buffer should still contain 'ABCDE'",
1191  ci.GetCurrentCommandBuffer(), "ABCDE");
1192 
1193  ci.AddKey('\13'); // CTRL-K
1194  UnitTest::Assert("buffer should now contain 'ABC'",
1195  ci.GetCurrentCommandBuffer(), "ABC");
1196 
1197  ci.AddKey('X');
1198  ci.AddKey('\13'); // CTRL-K again, at end of line
1199  UnitTest::Assert("buffer should now contain 'ABCX'",
1200  ci.GetCurrentCommandBuffer(), "ABCX");
1201 
1202  ci.AddKey('\1'); // CTRL-A to move to start of line
1203  ci.AddKey('\13'); // CTRL-K again, should erase everything
1204  UnitTest::Assert("buffer should now be empty",
1205  ci.GetCurrentCommandBuffer(), "");
1206 }
1207 
1208 static void Test_CommandInterpreter_KeyBuffer_CtrlW()
1209 {
1210  GXemul gxemul;
1212 
1213  UnitTest::Assert("buffer should contain ''",
1214  ci.GetCurrentCommandBuffer(), "");
1215  ci.AddKey('\27'); // CTRL-W
1216  UnitTest::Assert("buffer should still contain ''",
1217  ci.GetCurrentCommandBuffer(), "");
1218 
1219  ci.AddKey('a');
1220  ci.AddKey('b');
1221  ci.AddKey('c');
1222  UnitTest::Assert("buffer should contain abc",
1223  ci.GetCurrentCommandBuffer(), "abc");
1224  ci.AddKey('\27'); // CTRL-W
1225  UnitTest::Assert("buffer should be empty again",
1226  ci.GetCurrentCommandBuffer(), "");
1227 
1228  ci.AddKey(' ');
1229  ci.AddKey(' ');
1230  ci.AddKey('a');
1231  ci.AddKey('b');
1232  ci.AddKey('c');
1233  UnitTest::Assert("buffer should contain ' abc'",
1234  ci.GetCurrentCommandBuffer(), " abc");
1235  ci.AddKey('\27'); // CTRL-W
1236  UnitTest::Assert("buffer should contain only two spaces",
1237  ci.GetCurrentCommandBuffer(), " ");
1238 
1239  ci.AddKey('a');
1240  ci.AddKey('b');
1241  ci.AddKey('c');
1242  ci.AddKey(' ');
1243  UnitTest::Assert("buffer should contain ' abc '",
1244  ci.GetCurrentCommandBuffer(), " abc ");
1245  ci.AddKey('\27'); // CTRL-W
1246  UnitTest::Assert("buffer should again contain only two spaces",
1247  ci.GetCurrentCommandBuffer(), " ");
1248 
1249  ci.AddKey('a');
1250  ci.AddKey('b');
1251  ci.AddKey('c');
1252  ci.AddKey('d');
1253  ci.AddKey(' ');
1254  ci.AddKey('e');
1255  ci.AddKey('f');
1256  ci.AddKey('g');
1257  ci.AddKey('h');
1258  ci.AddKey('i');
1259  ci.AddKey(' ');
1260  ci.AddKey('\2'); // CTRL-B = move left
1261  ci.AddKey('\2');
1262  ci.AddKey('\2');
1263  UnitTest::Assert("buffer should contain ' abcd efghi '",
1264  ci.GetCurrentCommandBuffer(), " abcd efghi ");
1265  ci.AddKey('\27'); // CTRL-W
1266  UnitTest::Assert("buffer should now contain ' abcd hi '",
1267  ci.GetCurrentCommandBuffer(), " abcd hi ");
1268 }
1269 
1270 static void Test_CommandInterpreter_CommandHistory()
1271 {
1272  GXemul gxemul;
1274 
1275  UnitTest::Assert("history should still be empty (1)",
1276  ci.AddLineToCommandHistory(""), 0);
1277 
1278  UnitTest::Assert("history should still be empty (2)",
1279  ci.AddLineToCommandHistory(""), 0);
1280 
1281  UnitTest::Assert("A: history line 0",
1282  ci.GetHistoryLine(0), "");
1283  UnitTest::Assert("A: history line 1 not set yet",
1284  ci.GetHistoryLine(1), "");
1285  UnitTest::Assert("A: history line 2 not set yet",
1286  ci.GetHistoryLine(2), "");
1287 
1288  UnitTest::Assert("history should contain one entry",
1289  ci.AddLineToCommandHistory("hello"), 1);
1290 
1291  UnitTest::Assert("B: history line 0",
1292  ci.GetHistoryLine(0), "");
1293  UnitTest::Assert("B: history line 1",
1294  ci.GetHistoryLine(1), "hello");
1295  UnitTest::Assert("B: history line 2 not set yet",
1296  ci.GetHistoryLine(2), "");
1297 
1298  UnitTest::Assert("history should contain two entries",
1299  ci.AddLineToCommandHistory("world"), 2);
1300 
1301  UnitTest::Assert("history should still contain two entries",
1302  ci.AddLineToCommandHistory("world"), 2);
1303 
1304  UnitTest::Assert("C: history line 0",
1305  ci.GetHistoryLine(0), "");
1306  UnitTest::Assert("C: history line 1",
1307  ci.GetHistoryLine(1), "world");
1308  UnitTest::Assert("C: history line 2",
1309  ci.GetHistoryLine(2), "hello");
1310 
1311  UnitTest::Assert("history should contain three entries",
1312  ci.AddLineToCommandHistory("hello"), 3);
1313 
1314  UnitTest::Assert("D: history line 0",
1315  ci.GetHistoryLine(0), "");
1316  UnitTest::Assert("D: history line 1",
1317  ci.GetHistoryLine(1), "hello");
1318  UnitTest::Assert("D: history line 2",
1319  ci.GetHistoryLine(2), "world");
1320 
1321  UnitTest::Assert("history should still contain three entries",
1322  ci.AddLineToCommandHistory(""), 3);
1323 }
1324 
1325 /**
1326  * \brief A dummy Command, for unit testing purposes
1327  */
1328 class DummyCommand2
1329  : public Command
1330 {
1331 public:
1332  DummyCommand2(int& valueRef)
1333  : Command("dummycommand", "[args]")
1334  , m_value(valueRef)
1335  {
1336  }
1337 
1338  ~DummyCommand2()
1339  {
1340  }
1341 
1342  bool Execute(GXemul& gxemul, const vector<string>& arguments)
1343  {
1344  m_value ++;
1345  return true;
1346  }
1347 
1348  string GetShortDescription() const
1349  {
1350  return "A dummy command used for unit testing.";
1351  }
1352 
1353  string GetLongDescription() const
1354  {
1355  return "This is just a dummy command used for unit testing.";
1356  }
1357 
1358 private:
1359  int& m_value;
1360 };
1361 
1362 /**
1363  * \brief A dummy Command, for unit testing purposes
1364  */
1365 class DummyCommand3
1366  : public Command
1367 {
1368 public:
1369  DummyCommand3(int& valueRef)
1370  : Command("dummycmd", "[args]")
1371  , m_value(valueRef)
1372  {
1373  }
1374 
1375  ~DummyCommand3()
1376  {
1377  }
1378 
1379  bool Execute(GXemul& gxemul, const vector<string>& arguments)
1380  {
1381  m_value ++;
1382  return true;
1383  }
1384 
1385  string GetShortDescription() const
1386  {
1387  return "A dummy command used for unit testing.";
1388  }
1389 
1390  string GetLongDescription() const
1391  {
1392  return "This is just a dummy command used for unit testing.";
1393  }
1394 
1395 private:
1396  int& m_value;
1397 };
1398 
1399 static void Test_CommandInterpreter_AddCommand()
1400 {
1401  GXemul gxemul;
1403 
1404  size_t nCommands = ci.GetCommands().size();
1405  UnitTest::Assert("there should be some commands already",
1406  nCommands > 0);
1407 
1408  ci.AddCommand(new VersionCommand);
1409 
1410  UnitTest::Assert("it should not be possible to have multiple commands"
1411  " with the same name",
1412  ci.GetCommands().size() == nCommands);
1413 
1414  int dummyInt = 42;
1415  ci.AddCommand(new DummyCommand2(dummyInt));
1416 
1417  UnitTest::Assert("it should be possible to add new commands",
1418  ci.GetCommands().size() == nCommands + 1);
1419 }
1420 
1421 static void Test_CommandInterpreter_TabCompletion_EmptyLine()
1422 {
1423  GXemul gxemul;
1425 
1426  ci.AddKey('\t');
1427  UnitTest::Assert("tab completion should not have produced anything",
1428  ci.GetCurrentCommandBuffer(), "");
1429 }
1430 
1431 static void Test_CommandInterpreter_TabCompletion_FullWord()
1432 {
1433  GXemul gxemul;
1435 
1436  ci.AddKey('d');
1437  ci.AddKey('u');
1438  ci.AddKey('m');
1439  ci.AddKey('m');
1440  ci.AddKey('Z');
1441  ci.AddKey('\2'); // CTRL-B = move left
1442  UnitTest::Assert("initial buffer contents mismatch",
1443  ci.GetCurrentCommandBuffer(), "dummZ");
1444 
1445  ci.AddKey('\t');
1446  UnitTest::Assert("tab completion should have failed",
1447  ci.GetCurrentCommandBuffer(), "dummZ");
1448 
1449  int dummyInt = 42;
1450  ci.AddCommand(new DummyCommand2(dummyInt));
1451 
1452  ci.AddKey('\t');
1453  UnitTest::Assert("tab completion should have succeeded",
1454  ci.GetCurrentCommandBuffer(), "dummycommandZ");
1455 
1456  ci.AddKey('X');
1457  UnitTest::Assert("tab completion should have placed cursor at end of"
1458  " the tab-completed word",
1459  ci.GetCurrentCommandBuffer(), "dummycommandXZ");
1460 }
1461 
1462 static void Test_CommandInterpreter_TabCompletion_SpacesFirstOnLine()
1463 {
1464  GXemul gxemul;
1466 
1467  ci.AddKey(' ');
1468  ci.AddKey(' ');
1469  ci.AddKey('v');
1470  ci.AddKey('e');
1471  ci.AddKey('r');
1472  ci.AddKey('s');
1473  UnitTest::Assert("initial buffer contents mismatch",
1474  ci.GetCurrentCommandBuffer(), " vers");
1475 
1476  ci.AddKey('\t');
1477  UnitTest::Assert("tab completion should have succeeded",
1478  ci.GetCurrentCommandBuffer(), " version ");
1479 }
1480 
1481 static void Test_CommandInterpreter_TabCompletion_Partial()
1482 {
1483  GXemul gxemul;
1485 
1486  ci.AddKey('d');
1487  ci.AddKey('u');
1488  ci.AddKey('m');
1489  ci.AddKey('m');
1490  ci.AddKey('Z');
1491  ci.AddKey('\2'); // CTRL-B = move left
1492  UnitTest::Assert("initial buffer contents mismatch",
1493  ci.GetCurrentCommandBuffer(), "dummZ");
1494 
1495  int dummyInt = 42;
1496  ci.AddCommand(new DummyCommand2(dummyInt));
1497  ci.AddCommand(new DummyCommand3(dummyInt));
1498 
1499  ci.AddKey('\t');
1500  UnitTest::Assert("tab completion should have partially succeeded",
1501  ci.GetCurrentCommandBuffer(), "dummycZ");
1502 }
1503 
1504 static void Test_CommandInterpreter_TabCompletion_C()
1505 {
1506  GXemul gxemul;
1508 
1509  ci.AddKey('c');
1510  UnitTest::Assert("initial buffer contents mismatch",
1511  ci.GetCurrentCommandBuffer(), "c");
1512 
1513  ci.AddKey('\t');
1514  UnitTest::Assert("tab completion should not have modified command",
1515  ci.GetCurrentCommandBuffer(), "c");
1516 
1517  // ... because there are at least two possible commands on the
1518  // letter c: close and continue.
1519 }
1520 
1521 static void Test_CommandInterpreter_TabCompletion_OnlyCommandAsFirstWord()
1522 {
1523  GXemul gxemul;
1525 
1526  ci.AddKey('v');
1527  ci.AddKey('e');
1528  ci.AddKey('r');
1529  ci.AddKey('s');
1530  UnitTest::Assert("initial buffer contents mismatch",
1531  ci.GetCurrentCommandBuffer(), "vers");
1532 
1533  ci.AddKey('\t');
1534  UnitTest::Assert("first tab completion should have succeeded",
1535  ci.GetCurrentCommandBuffer(), "version ");
1536 
1537  ci.AddKey('v');
1538  ci.AddKey('e');
1539  ci.AddKey('r');
1540  ci.AddKey('s');
1541  UnitTest::Assert("buffer contents mismatch",
1542  ci.GetCurrentCommandBuffer(), "version vers");
1543 
1544  ci.AddKey('\t');
1545  UnitTest::Assert("second tab completion should have failed",
1546  ci.GetCurrentCommandBuffer(), "version vers");
1547 }
1548 
1549 static void Test_CommandInterpreter_TabCompletion_ComponentName()
1550 {
1551  GXemul gxemul;
1553 
1554  ci.RunCommand("add testm88k");
1555  UnitTest::Assert("initial buffer should be empty",
1556  ci.GetCurrentCommandBuffer(), "");
1557 
1558  ci.AddKey('c');
1559  ci.AddKey('p');
1560  ci.AddKey('\t');
1561  UnitTest::Assert("tab completion should have completed the "
1562  "component name",
1563  ci.GetCurrentCommandBuffer(), "cpu0");
1564 
1565  // Note: No space after component tab completion.
1566 }
1567 
1568 static void Test_CommandInterpreter_TabCompletion_ComponentNameAlreadyComplete()
1569 {
1570  GXemul gxemul;
1572 
1573  ci.RunCommand("add testm88k");
1574  UnitTest::Assert("initial buffer should be empty",
1575  ci.GetCurrentCommandBuffer(), "");
1576 
1577  ci.AddKey('c');
1578  ci.AddKey('p');
1579  ci.AddKey('u');
1580  ci.AddKey('0');
1581  ci.AddKey('\t');
1582  UnitTest::Assert("tab completion should not have changed anything",
1583  ci.GetCurrentCommandBuffer(), "cpu0");
1584 
1585  // Note: No space after component tab completion.
1586 }
1587 
1588 static void Test_CommandInterpreter_TabCompletion_ComponentNameMultiple()
1589 {
1590  GXemul gxemul;
1592 
1593  ci.RunCommand("add testm88k");
1594  ci.RunCommand("add m88k_cpu mainbus0");
1595  UnitTest::Assert("initial buffer should be empty",
1596  ci.GetCurrentCommandBuffer(), "");
1597 
1598  ci.AddKey('c');
1599  ci.AddKey('p');
1600  ci.AddKey('\t');
1601  UnitTest::Assert("there are both cpu0 and cpu1, so don't expand all the way",
1602  ci.GetCurrentCommandBuffer(), "cpu");
1603 
1604  // Note: No space after component tab completion.
1605 }
1606 
1607 static void Test_CommandInterpreter_TabCompletion_ComponentNameMultipleParents()
1608 {
1609  GXemul gxemul;
1611 
1612  ci.RunCommand("add testm88k"); // root.machine0
1613  ci.RunCommand("add testm88k"); // root.machine1
1614  UnitTest::Assert("initial buffer should be empty",
1615  ci.GetCurrentCommandBuffer(), "");
1616 
1617  ci.AddKey('c');
1618  ci.AddKey('p');
1619  ci.AddKey('\t');
1620  UnitTest::Assert("there are cpu0 in both root.machine0 and root.machine1",
1621  ci.GetCurrentCommandBuffer(), "machine");
1622 
1623  // Note: No space after component tab completion.
1624 }
1625 
1626 static void Test_CommandInterpreter_TabCompletion_ComponentNameNonexist()
1627 {
1628  GXemul gxemul;
1630 
1631  ci.RunCommand("add dummy");
1632  UnitTest::Assert("initial buffer should be empty",
1633  ci.GetCurrentCommandBuffer(), "");
1634 
1635  ci.AddKey('r');
1636  ci.AddKey('o');
1637  ci.AddKey('o');
1638  ci.AddKey('t');
1639  ci.AddKey('.');
1640  ci.AddKey('X');
1641  ci.AddKey('\t');
1642  UnitTest::Assert("tab completion should not have succeeded",
1643  ci.GetCurrentCommandBuffer(), "root.X");
1644 }
1645 
1646 static void Test_CommandInterpreter_TabCompletion_ComponentNameAsArgument()
1647 {
1648  GXemul gxemul;
1650 
1651  ci.RunCommand("add dummy root");
1652  UnitTest::Assert("initial buffer should be empty",
1653  ci.GetCurrentCommandBuffer(), "");
1654 
1655  ci.AddKey('a');
1656  ci.AddKey('d');
1657  ci.AddKey('d');
1658  ci.AddKey(' ');
1659  ci.AddKey('d');
1660  ci.AddKey('u');
1661  ci.AddKey('m');
1662  ci.AddKey('m');
1663  ci.AddKey('y');
1664  ci.AddKey(' ');
1665  ci.AddKey('d');
1666  ci.AddKey('u');
1667  UnitTest::Assert("buffer contents mismatch",
1668  ci.GetCurrentCommandBuffer(), "add dummy du");
1669 
1670  ci.AddKey('\t');
1671  UnitTest::Assert("tab completion should have completed the "
1672  "component name",
1673  ci.GetCurrentCommandBuffer(), "add dummy dummy0");
1674 
1675  // Note: No space after component tab completion.
1676 }
1677 
1678 static void Test_CommandInterpreter_TabCompletion_CWithComponents()
1679 {
1680  GXemul gxemul;
1682 
1683  ci.RunCommand("add testm88k");
1684 
1685  ci.AddKey('c');
1686  UnitTest::Assert("initial buffer contents mismatch",
1687  ci.GetCurrentCommandBuffer(), "c");
1688 
1689  ci.AddKey('\t');
1690  UnitTest::Assert("tab completion should not have modified command",
1691  ci.GetCurrentCommandBuffer(), "c");
1692 
1693  // ... because there are at least two possible commands on the
1694  // letter c: close and continue.
1695 }
1696 
1697 static void Test_CommandInterpreter_TabCompletion_roWithComponents()
1698 {
1699  GXemul gxemul;
1701 
1702  ci.RunCommand("add testm88k");
1703 
1704  ci.AddKey('r');
1705  ci.AddKey('o');
1706  ci.AddKey('\t');
1707  // there are both "rom0" and "root".
1708  UnitTest::Assert("tab completion should not have expanded",
1709  ci.GetCurrentCommandBuffer(), "ro");
1710 
1711  ci.AddKey('o');
1712  ci.AddKey('\t');
1713  UnitTest::Assert("tab completion should have expanded to 'root'",
1714  ci.GetCurrentCommandBuffer(), "root");
1715 }
1716 
1717 static void Test_CommandInterpreter_TabCompletion_ComponentMethods_Empty()
1718 {
1719  GXemul gxemul;
1721 
1722  ci.RunCommand("add testm88k");
1723 
1724  ci.AddKey('c');
1725  ci.AddKey('p');
1726  ci.AddKey('u');
1727  ci.AddKey('.');
1728  ci.AddKey('\t');
1729  UnitTest::Assert("tab completion should have caused expansion",
1730  ci.GetCurrentCommandBuffer(), "cpu0.");
1731 }
1732 
1733 static void Test_CommandInterpreter_TabCompletion_ComponentMethods()
1734 {
1735  GXemul gxemul;
1737 
1738  ci.RunCommand("add testm88k");
1739 
1740  ci.AddKey('c');
1741  ci.AddKey('p');
1742  ci.AddKey('u');
1743  ci.AddKey('.');
1744  ci.AddKey('u');
1745  ci.AddKey('\t');
1746  UnitTest::Assert("tab completion should have caused expansion",
1747  ci.GetCurrentCommandBuffer(), "cpu0.unassemble");
1748 }
1749 
1750 static void Test_CommandInterpreter_TabCompletion_ComponentMethods_Middle()
1751 {
1752  GXemul gxemul;
1754 
1755  ci.RunCommand("add testm88k");
1756 
1757  ci.AddKey('c');
1758  ci.AddKey('p');
1759  ci.AddKey('u');
1760  ci.AddKey('.');
1761  ci.AddKey('u');
1762  ci.AddKey('n');
1763  ci.AddKey('a');
1764  ci.AddKey('b');
1765  ci.AddKey('c');
1766  ci.AddKey('d');
1767  ci.AddKey('\2');
1768  ci.AddKey('\2');
1769  ci.AddKey('\2'); // cursor placed after "una"
1770  ci.AddKey('\t');
1771  UnitTest::Assert("tab completion should have caused expansion",
1772  ci.GetCurrentCommandBuffer(), "cpu0.unassemblebcd");
1773 }
1774 
1775 static void Test_CommandInterpreter_TabCompletion_ComponentMethods_Arg()
1776 {
1777  GXemul gxemul;
1779 
1780  ci.RunCommand("add testm88k");
1781 
1782  ci.AddKey('c');
1783  ci.AddKey('p');
1784  ci.AddKey('u');
1785  ci.AddKey('.');
1786  ci.AddKey('u');
1787  ci.AddKey(' ');
1788  ci.AddKey('a');
1789  ci.AddKey('d');
1790  ci.AddKey('d');
1791  ci.AddKey('r');
1792  ci.AddKey('\2');
1793  ci.AddKey('\2');
1794  ci.AddKey('\2');
1795  ci.AddKey('\2');
1796  ci.AddKey('\2'); // cursor placed after "u"
1797  ci.AddKey('\t');
1798  UnitTest::Assert("tab completion should have caused expansion",
1799  ci.GetCurrentCommandBuffer(), "cpu0.unassemble addr");
1800 }
1801 
1802 static void Test_CommandInterpreter_TabCompletion_ComponentVariables()
1803 {
1804  GXemul gxemul;
1806 
1807  ci.RunCommand("add testm88k");
1808 
1809  ci.AddKey('c');
1810  ci.AddKey('p');
1811  ci.AddKey('u');
1812  ci.AddKey('.');
1813  ci.AddKey('s');
1814  ci.AddKey('h');
1815  ci.AddKey('\t');
1816  UnitTest::Assert("tab completion should have caused expansion cpu -> cpu0",
1817  ci.GetCurrentCommandBuffer(), "cpu0.showFunctionTrace");
1818 }
1819 
1820 static void Test_CommandInterpreter_TabCompletion_ComponentVariables_Max()
1821 {
1822  GXemul gxemul;
1824 
1825  ci.RunCommand("add ram");
1826 
1827  ci.AddKey('r');
1828  ci.AddKey('a');
1829  ci.AddKey('m');
1830  ci.AddKey('.');
1831  ci.AddKey('m');
1832  ci.AddKey('e');
1833  ci.AddKey('m');
1834  ci.AddKey('o');
1835  ci.AddKey('\t');
1836  UnitTest::Assert("tab completion should have caused expansion ram -> ram0",
1837  ci.GetCurrentCommandBuffer(), "ram0.memoryMapped");
1838 }
1839 
1840 static void Test_CommandInterpreter_TabCompletion_ComponentVariables_Max2()
1841 {
1842  GXemul gxemul;
1844 
1845  ci.RunCommand("add ram");
1846 
1847  ci.AddKey('r');
1848  ci.AddKey('a');
1849  ci.AddKey('m');
1850  ci.AddKey('.');
1851  ci.AddKey('m');
1852  ci.AddKey('e');
1853  ci.AddKey('m');
1854  ci.AddKey('o');
1855  ci.AddKey(' ');
1856  ci.AddKey('2');
1857  ci.AddKey('\2');
1858  ci.AddKey('\2');
1859  ci.AddKey('\t');
1860  UnitTest::Assert("tab completion should have caused expansion ram -> ram0",
1861  ci.GetCurrentCommandBuffer(), "ram0.memoryMapped 2");
1862 
1863  ci.AddKey('X');
1864  UnitTest::Assert("cursor position after tab completion was wrong?",
1865  ci.GetCurrentCommandBuffer(), "ram0.memoryMappedX 2");
1866 }
1867 
1868 static void Test_CommandInterpreter_NonExistingCommand()
1869 {
1870  GXemul gxemul;
1872 
1873  UnitTest::Assert("nonexisting (nonsense) command should fail",
1874  ci.RunCommand("nonexistingcommand") == false);
1875 }
1876 
1877 static void Test_CommandInterpreter_SimpleCommand()
1878 {
1879  GXemul gxemul;
1881 
1882  UnitTest::Assert("simple command should succeed",
1883  ci.RunCommand("version") == true);
1884 
1885  UnitTest::Assert("simple command with whitespace should succeed",
1886  ci.RunCommand(" version ") == true);
1887 }
1888 
1889 static void Test_CommandInterpreter_SimpleCommand_NoArgsAllowed()
1890 {
1891  GXemul gxemul;
1893 
1894  UnitTest::Assert("simple command should succeed",
1895  ci.RunCommand("version") == true);
1896 
1897  UnitTest::Assert("simple command which does not take arguments should"
1898  " fail when attempt is made to execute it with arguments",
1899  ci.RunCommand("version hello") == false);
1900 }
1901 
1902 static void Test_CommandInterpreter_ComponentMethods()
1903 {
1904  GXemul gxemul;
1906 
1907  UnitTest::Assert("Huh? Could not add testm88k.",
1908  ci.RunCommand("add testm88k") == true);
1909 
1910  UnitTest::Assert("component method 1",
1911  ci.RunCommand("cpu") == true);
1912  UnitTest::Assert("component method 2",
1913  ci.RunCommand("cpu.u") == true);
1914  UnitTest::Assert("component method 3",
1915  ci.RunCommand("cpu.urk") == false);
1916  UnitTest::Assert("component method 4",
1917  ci.RunCommand("cpu.unassemble") == true);
1918  UnitTest::Assert("component method 5",
1919  ci.RunCommand("root.machine0.mainbus0.cpu") == true);
1920  UnitTest::Assert("component method 6",
1921  ci.RunCommand("root.machine0.mainbus0.cpu0") == true);
1922  UnitTest::Assert("component method 7",
1923  ci.RunCommand("root.machine0.mainbus0.cpu.u") == true);
1924  UnitTest::Assert("component method 8",
1925  ci.RunCommand("root.machine0.mainbus0.cpu0.unassemble") == true);
1926 }
1927 
1928 static void Test_CommandInterpreter_ComponentVariables_NoArgs()
1929 {
1930  GXemul gxemul;
1932 
1933  UnitTest::Assert("Huh? Could not add testm88k.",
1934  ci.RunCommand("add testm88k") == true);
1935 
1936  UnitTest::Assert("component variable 1",
1937  ci.RunCommand("cpu.nonexistant") == false);
1938  UnitTest::Assert("component variable 2",
1939  ci.RunCommand("cpu.pau") == true);
1940  UnitTest::Assert("component variable 3",
1941  ci.RunCommand("root.machine0.mainbus0.cpu0.pau") == true);
1942 }
1943 
1944 static void Test_CommandInterpreter_ComponentVariables_Ambiguous()
1945 {
1946  GXemul gxemul;
1948 
1949  UnitTest::Assert("Huh? Could not add testmips.",
1950  ci.RunCommand("add testm88k") == true);
1951 
1952  UnitTest::Assert("cpu.f should not work, there should be multiple matches",
1953  ci.RunCommand("cpu.f") == false);
1954 }
1955 
1956 static void Test_CommandInterpreter_ComponentVariables_PartialIsOk()
1957 {
1958  GXemul gxemul;
1960 
1961  UnitTest::Assert("Huh? Could not add testm88k.",
1962  ci.RunCommand("add testm88k") == true);
1963 
1964  // Make sure that r2 works, and doesn't complain about ambiguity.
1965  UnitTest::Assert("gpr 2",
1966  ci.RunCommand("cpu.r2") == true);
1967  UnitTest::Assert("gpr 29",
1968  ci.RunCommand("cpu.r29") == true);
1969 }
1970 
1972 {
1973  // Key and current buffer:
1974  UNITTEST(Test_CommandInterpreter_AddKey_ReturnValue);
1975  UNITTEST(Test_CommandInterpreter_KeyBuffer);
1976  UNITTEST(Test_CommandInterpreter_KeyBuffer_CursorMovement);
1977  UNITTEST(Test_CommandInterpreter_KeyBuffer_CtrlK);
1978  UNITTEST(Test_CommandInterpreter_KeyBuffer_CtrlW);
1979 
1980  // Command History:
1981  UNITTEST(Test_CommandInterpreter_CommandHistory);
1982 
1983  // AddCommand / GetCommands:
1984  UNITTEST(Test_CommandInterpreter_AddCommand);
1985 
1986  // Tab completion:
1987  UNITTEST(Test_CommandInterpreter_TabCompletion_EmptyLine);
1988  UNITTEST(Test_CommandInterpreter_TabCompletion_FullWord);
1989  UNITTEST(Test_CommandInterpreter_TabCompletion_SpacesFirstOnLine);
1990  UNITTEST(Test_CommandInterpreter_TabCompletion_Partial);
1991  UNITTEST(Test_CommandInterpreter_TabCompletion_C);
1992  UNITTEST(Test_CommandInterpreter_TabCompletion_OnlyCommandAsFirstWord);
1993  UNITTEST(Test_CommandInterpreter_TabCompletion_ComponentName);
1994  UNITTEST(Test_CommandInterpreter_TabCompletion_ComponentNameAlreadyComplete);
1995  UNITTEST(Test_CommandInterpreter_TabCompletion_ComponentNameMultiple);
1996  UNITTEST(Test_CommandInterpreter_TabCompletion_ComponentNameMultipleParents);
1997  UNITTEST(Test_CommandInterpreter_TabCompletion_ComponentNameNonexist);
1998  UNITTEST(Test_CommandInterpreter_TabCompletion_ComponentNameAsArgument);
1999  UNITTEST(Test_CommandInterpreter_TabCompletion_CWithComponents);
2000  UNITTEST(Test_CommandInterpreter_TabCompletion_roWithComponents);
2001  UNITTEST(Test_CommandInterpreter_TabCompletion_ComponentMethods_Empty);
2002  UNITTEST(Test_CommandInterpreter_TabCompletion_ComponentMethods);
2003  UNITTEST(Test_CommandInterpreter_TabCompletion_ComponentMethods_Middle);
2004  UNITTEST(Test_CommandInterpreter_TabCompletion_ComponentMethods_Arg);
2005  UNITTEST(Test_CommandInterpreter_TabCompletion_ComponentVariables);
2006  UNITTEST(Test_CommandInterpreter_TabCompletion_ComponentVariables_Max);
2007  UNITTEST(Test_CommandInterpreter_TabCompletion_ComponentVariables_Max2);
2008 
2009  // RunCommand:
2010  UNITTEST(Test_CommandInterpreter_NonExistingCommand);
2011  UNITTEST(Test_CommandInterpreter_SimpleCommand);
2012  UNITTEST(Test_CommandInterpreter_SimpleCommand_NoArgsAllowed);
2013  UNITTEST(Test_CommandInterpreter_ComponentMethods);
2014  UNITTEST(Test_CommandInterpreter_ComponentVariables_NoArgs);
2015  UNITTEST(Test_CommandInterpreter_ComponentVariables_Ambiguous);
2016  UNITTEST(Test_CommandInterpreter_ComponentVariables_PartialIsOk);
2017 }
2018 
2019 #endif
Commands
map< string, refcount_ptr< Command > > Commands
Definition: Command.h:135
refcount_ptr::IsNULL
bool IsNULL() const
Checks whether or not an object is referenced by the reference counted pointer.
Definition: refcount_ptr.h:218
Component::GenerateTreeDump
string GenerateTreeDump(const string &branchTemplate, bool htmlLinksForClassNames=false, string prefixForComponentUrls="") const
Generates an ASCII tree dump of a component tree.
Definition: Component.cc:459
stringchar
char stringchar
Definition: misc.h:59
UI::ShowCommandMessage
virtual void ShowCommandMessage(const string &command)=0
Shows a command being executed.
GXemul
The main emulator class.
Definition: GXemul.h:55
CommandInterpreter::AddKey
bool AddKey(stringchar key)
Adds a character (keypress) to the current command buffer.
Definition: CommandInterpreter.cc:381
GXemul::GetCommandInterpreter
CommandInterpreter & GetCommandInterpreter()
Gets a reference to the CommandInterpreter.
Definition: GXemul.cc:623
StateVariable
StateVariables make up the persistent state of Component objects.
Definition: StateVariable.h:69
CommandInterpreter::CommandInterpreter
CommandInterpreter(GXemul *owner)
Constructs a CommandInterpreter.
Definition: CommandInterpreter.cc:38
refcount_ptr< Command >
UNITTESTS
#define UNITTESTS(class)
Helper for unit test case execution.
Definition: UnitTest.h:184
Command
A Command is a named function, executed by the CommandInterpreter.
Definition: Command.h:51
CommandInterpreter::AddCommand
void AddCommand(refcount_ptr< Command > command)
Adds a new Command to the command interpreter.
Definition: CommandInterpreter.cc:57
GXemul::GetRootComponent
refcount_ptr< Component > GetRootComponent()
Gets a pointer to the root configuration component.
Definition: GXemul.cc:659
UNITTEST
#define UNITTEST(functionname)
Helper for unit test case execution.
Definition: UnitTest.h:217
VersionCommand
A Command which prints the version of the application.
Definition: VersionCommand.h:42
Component::GetMethodNames
virtual void GetMethodNames(vector< string > &names) const
Retrieves a component's implemented method names.
Definition: Component.cc:393
UnitTest::Assert
static void Assert(const string &strFailMessage, bool condition)
Asserts that a boolean condition is correct.
Definition: UnitTest.cc:40
Component::ExecuteMethod
virtual void ExecuteMethod(GXemul *gxemul, const string &methodName, const vector< string > &arguments)
Executes a method on the component.
Definition: Component.cc:406
Component::GetVariableNames
void GetVariableNames(vector< string > &names) const
Retrieves a component's state variable names.
Definition: Component.cc:941
UI::ShowDebugMessage
virtual void ShowDebugMessage(const string &msg)=0
Shows a debug message.
Component::DetectChanges
void DetectChanges(const refcount_ptr< Component > &oldClone, ostream &changeMessages) const
Compare an older clone to the current tree, to find changes.
Definition: Component.cc:198
Command::GetCommandName
const string & GetCommandName() const
Gets the name of the command.
Definition: Command.h:70
Command::GetLongDescription
virtual string GetLongDescription() const =0
Returns a long description/help message for the command.
StateVariable::ToString
string ToString() const
Returns the variable as a readable string.
Definition: StateVariable.cc:229
Component::SetVariableValue
bool SetVariableValue(const string &name, const string &expression)
Sets a variable to a new value.
Definition: Component.cc:1030
CommandInterpreter::RunCommand
bool RunCommand(const string &command, bool *pSuccess=NULL)
Runs a command, given as a string.
Definition: CommandInterpreter.cc:958
CommandInterpreter::GetCurrentCommandBuffer
const string & GetCurrentCommandBuffer() const
Retrieves the current command buffer.
Definition: CommandInterpreter.cc:1019
CommandInterpreter::GetCommands
const Commands & GetCommands() const
Gets a collection of all commands.
Definition: CommandInterpreter.cc:63
CommandInterpreter
An interactive command interpreter, which run Commands.
Definition: CommandInterpreter.h:50
GXemul::GetUI
UI * GetUI()
Gets a pointer to the GXemul instance' active UI.
Definition: GXemul.cc:653
Component::GeneratePath
string GeneratePath() const
Generates a string representation of the path to the Component.
Definition: Component.cc:686
Component::MethodMayBeReexecutedWithoutArgs
virtual bool MethodMayBeReexecutedWithoutArgs(const string &methodName) const
Returns whether a method name may be re-executed without args.
Definition: Component.cc:399
Command::Execute
virtual bool Execute(GXemul &gxemul, const vector< string > &arguments)=0
Executes the command on a given GXemul instance.
UnitTestFailedException
An exception thrown by unit test cases that fail.
Definition: UnitTest.h:41
Component::LightClone
const refcount_ptr< Component > LightClone() const
Makes a light clone of the component and all its children.
Definition: Component.cc:192
CommandInterpreter::AddLineToCommandHistory
int AddLineToCommandHistory(const string &command)
Adds a command line to the command history.
Definition: CommandInterpreter.cc:69
CommandInterpreter::GetHistoryLine
string GetHistoryLine(int nStepsBack) const
Retrieves a line from the command history.
Definition: CommandInterpreter.cc:88
Component::GetVariable
StateVariable * GetVariable(const string &name)
Gets a pointer to a state variable.
Definition: Component.cc:949
CommandInterpreter.h
CommandInterpreter::ReshowCurrentCommandBuffer
void ReshowCurrentCommandBuffer()
Re-displays the current command buffer.
Definition: CommandInterpreter.cc:638
Command::GetShortDescription
virtual string GetShortDescription() const =0
Returns a short (one-line) description of the command.
UI::InputLineDone
virtual void InputLineDone()=0
Executed by the CommandInterpreter when a line has been completed (with a newline).
UI::RedisplayInputLine
virtual void RedisplayInputLine(const string &inputline, size_t cursorPosition)=0
Redisplays the interactive command input line.
CommandInterpreter::ClearCurrentCommandBuffer
void ClearCurrentCommandBuffer()
Clears the current command buffer.
Definition: CommandInterpreter.cc:655
GXemul.h

Generated on Tue Aug 25 2020 19:25:06 for GXemul by doxygen 1.8.18