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  RunCommand(m_currentCommandString);
571  } else if (m_mayBeReexecuted != "") {
572  RunCommand(m_mayBeReexecuted);
573  }
574  break;
575 
576  default:
577  // Most other keys just add/insert a character into the command
578  // string:
579  if (key >= ' ') {
580  m_currentCommandString.insert(
581  m_currentCommandCursorPosition, 1, key);
582  m_currentCommandCursorPosition ++;
583  }
584  }
585 
586  if (key != '\n' && key != '\r')
588 
589  // Return value is true for newline/cr, false otherwise:
590  return key == '\n' || key == '\r';
591 }
592 
593 
594 void CommandInterpreter::ShowAvailableWords(const vector<string>& words)
595 {
596  m_GXemul->GetUI()->ShowDebugMessage("\n");
597 
598  const size_t n = words.size();
599  size_t i;
600 
601  // Find the longest word first:
602  size_t maxLen = 0;
603  for (i=0; i<n; ++i) {
604  size_t len = words[i].length();
605  if (len > maxLen)
606  maxLen = len;
607  }
608 
609  maxLen += 4;
610 
611  // Generate msg:
612  std::stringstream msg;
613  size_t lineLen = 0;
614  for (i=0; i<n; ++i) {
615  if (lineLen == 0)
616  msg << " ";
617 
618  size_t len = words[i].length();
619  msg << words[i];
620  lineLen += len;
621 
622  for (size_t j=len; j<maxLen; j++) {
623  msg << " ";
624  lineLen ++;
625  }
626 
627  if (lineLen >= 77 - maxLen || i == n-1) {
628  msg << "\n";
629  lineLen = 0;
630  }
631  }
632 
633  m_GXemul->GetUI()->ShowDebugMessage(msg.str());
634 }
635 
636 
638 {
639  m_GXemul->GetUI()->RedisplayInputLine(
640  m_currentCommandString, m_currentCommandCursorPosition);
641 }
642 
643 
644 void CommandInterpreter::ClearCurrentInputLineVisually()
645 {
646  string clearString = "";
647  clearString.insert((size_t)0, m_currentCommandString.length(), ' ');
648 
649  m_GXemul->GetUI()->RedisplayInputLine(
650  clearString, m_currentCommandCursorPosition);
651 }
652 
653 
655 {
656  m_currentCommandString = "";
657  m_currentCommandCursorPosition = 0;
658  m_historyEntryToCopyFrom = 0;
659 }
660 
661 
662 static void SplitIntoWords(const string& commandOrig,
663  string& commandName, vector<string>& arguments)
664 {
665  string command = commandOrig;
666 
667  arguments.clear();
668  commandName = "";
669  size_t pos = 0;
670 
671  // Surround '=' with white spaces, except when inside parentheses...
672  // NOTE/TODO: This will not be needed in the future (?), when a real
673  // expression evaluator has been [re]implemented.
674  int insideParenthesesCount = 0;
675  while (pos < command.length()) {
676  if (command[pos] == '(')
677  insideParenthesesCount ++;
678  if (command[pos] == ')')
679  insideParenthesesCount --;
680  if (command[pos] == '=' && insideParenthesesCount == 0) {
681  command.replace(pos, 1, " = ");
682  pos ++;
683  }
684 
685  pos ++;
686  }
687 
688  // Split command into words, ignoring all whitespace:
689  pos = 0;
690  while (pos < command.length()) {
691  // Skip initial whitespace:
692  while (pos < command.length() && command[pos] == ' ')
693  pos ++;
694 
695  if (pos >= command.length())
696  break;
697 
698  // This is a new word. Add all characters, until
699  // whitespace or end of string:
700  string newWord = "";
701  while (pos < command.length() && command[pos] != ' ') {
702  newWord += command[pos];
703  pos ++;
704  }
705 
706  if (commandName.empty())
707  commandName = newWord;
708  else
709  arguments.push_back(newWord);
710  }
711 }
712 
713 
714 void CommandInterpreter::VariableAssignment(const string& componentPath,
715  const string& variableName, const string& expression)
716 {
717  refcount_ptr<Component> component = m_GXemul->GetRootComponent()->
718  LookupPath(componentPath);
719 
720  StateVariable* var = component->GetVariable(variableName);
721  if (var == NULL) {
722  m_GXemul->GetUI()->ShowDebugMessage("Unknown variable '" +
723  variableName + "'? (Internal error.)\n");
724  throw std::exception();
725  }
726 
727  const refcount_ptr<Component> lightClone =
728  m_GXemul->GetRootComponent()->LightClone();
729 
730  // Attempt to assign the expression to the variable:
731  if (!component->SetVariableValue(variableName, expression))
732  m_GXemul->GetUI()->ShowDebugMessage("Assignment failed.\n");
733 
734  // ... and print all state change (in case a write to a variable had
735  // side effects, then this makes sure that the user sees all such side
736  // effects):
737  stringstream changeMessages;
738  m_GXemul->GetRootComponent()->DetectChanges(lightClone, changeMessages);
739 
740  string msg = changeMessages.str();
741  if (msg == "")
742  msg = "(No state change.)\n";
743 
744  m_GXemul->GetUI()->ShowDebugMessage(msg);
745 }
746 
747 
748 bool CommandInterpreter::RunComponentMethod(
749  const string& componentPathAndMethod, const vector<string>& arguments)
750 {
751  // Note: componentPathAndMethod may or may not have a method at
752  // the end!
753 
754  // Make several "smart" guesses:
755  refcount_ptr<Component> component;
756  string componentPath;
757  string methodName;
758 
759  do {
760  // 1. Assume that componentPathAndMethod is a full component
761  // path:
762  component = m_GXemul->GetRootComponent()->
763  LookupPath(componentPathAndMethod);
764  if (!component.IsNULL())
765  break;
766 
767  // 2. Assume that componentPathAndMethod is a component
768  // path, but it is not tab-completed yet:
769  string tabcompleted = componentPathAndMethod;
770  size_t tmpLen = tabcompleted.length();
771  if (TabComplete(tabcompleted, tmpLen)) {
772  component = m_GXemul->GetRootComponent()->
773  LookupPath(tabcompleted);
774  if (!component.IsNULL())
775  break;
776  }
777 
778  // If there is no period in the name, we can't continue
779  // with the following guesses.
780  if (componentPathAndMethod.find(".") == string::npos)
781  break;
782 
783  size_t pos = componentPathAndMethod.find_last_of('.');
784 
785  // 3. Assume full component path + ".method":
786  componentPath = componentPathAndMethod.substr(0, pos);
787  component = m_GXemul->GetRootComponent()->
788  LookupPath(componentPath);
789  if (!component.IsNULL()) {
790  methodName = componentPathAndMethod.substr(pos+1);
791  break;
792  }
793 
794  // 4. Assume non-tab-completed component path + ".method":
795  tabcompleted = componentPath;
796  tmpLen = tabcompleted.length();
797  if (TabComplete(tabcompleted, tmpLen)) {
798  component = m_GXemul->GetRootComponent()->
799  LookupPath(tabcompleted);
800  if (!component.IsNULL()) {
801  methodName =
802  componentPathAndMethod.substr(pos+1);
803  break;
804  }
805  }
806  } while (false);
807 
808  if (component.IsNULL())
809  return false;
810 
811  // No method given? Then show the component tree, and the component's
812  // state variables, and return.
813  if (methodName.empty()) {
814  m_GXemul->GetUI()->ShowDebugMessage(
815  component->GenerateTreeDump(""));
816 
817  // Retrieve the names of all the state variables:
818  vector<string> variableNames;
819  component->GetVariableNames(variableNames);
820 
821  stringstream ss;
822  ss << "\n";
823 
824  size_t maxLen = 0;
825  size_t i;
826  for (i=0; i<variableNames.size(); i++)
827  if (variableNames[i].length() > maxLen)
828  maxLen = variableNames[i].length();
829 
830  for (i=0; i<variableNames.size(); i++) {
831  const string& name = variableNames[i];
832  if (name == "name" || name == "template")
833  continue;
834 
835  ss << " " << name;
836  for (size_t j=name.length(); j<=maxLen; j++)
837  ss << " ";
838 
839  const StateVariable* var = component->GetVariable(name);
840  if (var == NULL)
841  ss << "= (unknown?)";
842  else
843  ss << "= " << var->ToString();
844 
845  ss << "\n";
846  }
847 
848  m_GXemul->GetUI()->ShowDebugMessage(ss.str());
849 
850  return true;
851  }
852 
853  // Now, it is possible that methodName is incomplete, so it has to
854  // be looked up as well:
855  vector<string> names;
856  component->GetMethodNames(names);
857  int nrOfMatches = 0;
858  string fullMatch;
859  for (size_t i=0; i<names.size(); ++i) {
860  if (names[i].substr(0, methodName.length()) == methodName) {
861  ++ nrOfMatches;
862  fullMatch = names[i];
863  }
864  }
865 
866  if (nrOfMatches == 1) {
867  // Execute it!
868  component->ExecuteMethod(m_GXemul, fullMatch, arguments);
869 
870  if (component->MethodMayBeReexecutedWithoutArgs(fullMatch))
871  m_mayBeReexecuted = componentPathAndMethod;
872 
873  return true;
874  }
875 
876  // Try variable names:
877  names.clear();
878  component->GetVariableNames(names);
879  nrOfMatches = 0;
880  fullMatch = "";
881  for (size_t i=0; i<names.size(); ++i) {
882  // Exact match?
883  if (names[i] == methodName) {
884  nrOfMatches = 1;
885  fullMatch = names[i];
886  break;
887  }
888 
889  // Partial match?
890  if (names[i].substr(0, methodName.length()) == methodName) {
891  ++ nrOfMatches;
892  fullMatch = names[i];
893  }
894  }
895 
896  stringstream ss;
897 
898  if (nrOfMatches == 1) {
899  if (arguments.size() > 0) {
900  if (arguments.size() == 1 ||
901  arguments[0] != "=") {
902  // TODO: Printing expressions, such as
903  // cpu.pc + 4
904 
905  m_GXemul->GetUI()->ShowDebugMessage(
906  "Syntax error. Variable assignment syntax"
907  " is:\n <variable> = <expression>\n");
908  return true;
909  }
910 
911  const StateVariable* var =
912  component->GetVariable(fullMatch);
913  if (var == NULL) {
914  m_GXemul->GetUI()->ShowDebugMessage(
915  "Unknown variable.\n");
916  return true;
917  }
918 
919  string assignment;
920  for (size_t i=1; i<arguments.size(); ++i)
921  assignment += arguments[i] + " ";
922 
923  VariableAssignment(component->GeneratePath(), fullMatch, assignment);
924 
925  return true;
926  }
927 
928  // Print the variable's name and value:
929  ss << fullMatch;
930 
931  const StateVariable* var = component->GetVariable(fullMatch);
932  if (var == NULL)
933  ss << " = (unknown variable?)";
934  else
935  ss << " = " << var->ToString();
936 
937  ss << "\n";
938 
939  m_GXemul->GetUI()->ShowDebugMessage(ss.str());
940 
941  return true;
942  }
943 
944  if (nrOfMatches > 1)
945  ss << methodName << ": ambiguous method or variable name of "
946  << component->GeneratePath() << ".\n";
947  else
948  ss << methodName << ": not a method or variable of "
949  << component->GeneratePath() << ".\n";
950 
951  m_GXemul->GetUI()->ShowDebugMessage(ss.str());
952 
953  return false;
954 }
955 
956 
957 bool CommandInterpreter::RunCommand(const string& command, bool* pSuccess)
958 {
959  string commandName;
960  vector<string> arguments;
961  SplitIntoWords(command, commandName, arguments);
962 
963  m_mayBeReexecuted = "";
964 
965  m_GXemul->GetUI()->ShowCommandMessage(command);
966 
967  // Find the command...
968  Commands::iterator it = m_commands.find(commandName);
969  if (it == m_commands.end()) {
970  // Not found? Then try to tab-complete the name...
971  string commandTabCompleted = commandName;
972  size_t tmpCursorPos = commandTabCompleted.length();
973  TabComplete(commandTabCompleted, tmpCursorPos);
974 
975  // remove any trailing space(s):
976  while (commandTabCompleted.length() > 0 &&
977  commandTabCompleted[commandTabCompleted.length()-1] == ' ')
978  commandTabCompleted.erase(
979  commandTabCompleted.length() - 1);
980 
981  // ... and try again:
982  it = m_commands.find(commandTabCompleted);
983  if (it == m_commands.end()) {
984  // If this is a component name [with an optional
985  // method name], then execute a method on it.
986  if (RunComponentMethod(commandName, arguments))
987  return true;
988  m_GXemul->GetUI()->ShowDebugMessage(commandName +
989  ": unknown command. Type help for help.\n");
990  return false;
991  }
992  }
993 
994  if (arguments.size() != 0 && (it->second)->GetArgumentFormat() == "") {
995  m_GXemul->GetUI()->ShowDebugMessage(commandName +
996  " takes no arguments. Type help " + commandName +
997  " for help on the syntax.\n");
998  return false;
999  }
1000 
1001  // ... and execute it:
1002  bool success = (it->second)->Execute(*m_GXemul, arguments);
1003  if (pSuccess != NULL)
1004  *pSuccess = success;
1005 
1006  if ((it->second)->MayBeReexecutedWithoutArgs())
1007  m_mayBeReexecuted = it->first;
1008 
1009  return true;
1010 }
1011 
1012 
1014 {
1015  return m_currentCommandString;
1016 }
1017 
1018 
1019 /*****************************************************************************/
1020 
1021 
1022 #ifdef WITHUNITTESTS
1023 
1024 static void Test_CommandInterpreter_AddKey_ReturnValue()
1025 {
1026  GXemul gxemul;
1028 
1029  UnitTest::Assert("addkey of regular char should return false",
1030  ci.AddKey('a') == false);
1031 
1032  UnitTest::Assert("addkey of nul char should return false",
1033  ci.AddKey('\0') == false);
1034 
1035  UnitTest::Assert("addkey of newline should return true",
1036  ci.AddKey('\n') == true);
1037 
1038  UnitTest::Assert("addkey of carriage return should return true too",
1039  ci.AddKey('\r') == true);
1040 }
1041 
1042 static void Test_CommandInterpreter_KeyBuffer()
1043 {
1044  GXemul gxemul;
1046 
1047  UnitTest::Assert("buffer should initially be empty",
1048  ci.GetCurrentCommandBuffer() == "");
1049 
1050  ci.AddKey('a'); // normal char
1051 
1052  UnitTest::Assert("buffer should contain 'a'",
1053  ci.GetCurrentCommandBuffer() == "a");
1054 
1055  ci.AddKey('\0'); // nul char should have no effect
1056 
1057  UnitTest::Assert("buffer should still contain only 'a'",
1058  ci.GetCurrentCommandBuffer() == "a");
1059 
1060  ci.AddKey('A'); // multiple chars
1061  ci.AddKey('B');
1062  UnitTest::Assert("buffer should contain 'aAB'",
1063  ci.GetCurrentCommandBuffer() == "aAB");
1064 
1065  ci.AddKey('\177'); // del
1066 
1067  UnitTest::Assert("buffer should contain 'aA' (del didn't work?)",
1068  ci.GetCurrentCommandBuffer() == "aA");
1069 
1070  ci.AddKey('\b'); // backspace
1071 
1072  UnitTest::Assert("buffer should contain 'a' again (BS didn't work)",
1073  ci.GetCurrentCommandBuffer() == "a");
1074 
1075  ci.AddKey('\b');
1076 
1077  UnitTest::Assert("buffer should now be empty '' again",
1078  ci.GetCurrentCommandBuffer() == "");
1079 
1080  ci.AddKey('\b'); // cannot be emptier than... well... empty :)
1081 
1082  UnitTest::Assert("buffer should still be empty",
1083  ci.GetCurrentCommandBuffer() == "");
1084 
1085  ci.AddKey('a');
1086 
1087  UnitTest::Assert("buffer should contain 'a' again",
1088  ci.GetCurrentCommandBuffer() == "a");
1089 
1090  ci.AddKey('Q');
1091 
1092  UnitTest::Assert("buffer should contain 'aQ'",
1093  ci.GetCurrentCommandBuffer() == "aQ");
1094 
1095  ci.AddKey('\n'); // newline should execute the command
1096 
1097  UnitTest::Assert("buffer should be empty after executing '\\n'",
1098  ci.GetCurrentCommandBuffer() == "");
1099 
1100  ci.AddKey('Z');
1101  ci.AddKey('Q');
1102 
1103  UnitTest::Assert("new command should have been possible",
1104  ci.GetCurrentCommandBuffer() == "ZQ");
1105 
1106  ci.AddKey('\r'); // carriage return should work like newline
1107 
1108  UnitTest::Assert("buffer should be empty after executing '\\r'",
1109  ci.GetCurrentCommandBuffer() == "");
1110 }
1111 
1112 static void Test_CommandInterpreter_KeyBuffer_CursorMovement()
1113 {
1114  GXemul gxemul;
1116 
1117  ci.AddKey('A');
1118  ci.AddKey('B');
1119  ci.AddKey('C');
1120  ci.AddKey('D');
1121  ci.AddKey('E');
1122  UnitTest::Assert("buffer should contain 'ABCDE'",
1123  ci.GetCurrentCommandBuffer(), "ABCDE");
1124 
1125  ci.AddKey('\2'); // CTRL-B should move back (left)
1126  ci.AddKey('\2');
1127  ci.AddKey('\2');
1128  UnitTest::Assert("buffer should still contain 'ABCDE'",
1129  ci.GetCurrentCommandBuffer(), "ABCDE");
1130 
1131  ci.AddKey('\b');
1132  UnitTest::Assert("buffer should now contain 'ACDE'",
1133  ci.GetCurrentCommandBuffer(), "ACDE");
1134 
1135  ci.AddKey('\6'); // CTRL-F should move forward (right)
1136  ci.AddKey('\6');
1137  UnitTest::Assert("buffer should still contain 'ACDE'",
1138  ci.GetCurrentCommandBuffer(), "ACDE");
1139 
1140  ci.AddKey('\b');
1141  UnitTest::Assert("buffer should now contain 'ACE'",
1142  ci.GetCurrentCommandBuffer(), "ACE");
1143 
1144  ci.AddKey('\1'); // CTRL-A should move to start of line
1145  UnitTest::Assert("buffer should still contain 'ACE'",
1146  ci.GetCurrentCommandBuffer(), "ACE");
1147 
1148  ci.AddKey('1');
1149  ci.AddKey('2');
1150  UnitTest::Assert("buffer should now contain '12ACE'",
1151  ci.GetCurrentCommandBuffer(), "12ACE");
1152 
1153  ci.AddKey('\5'); // CTRL-E should move to end of line
1154  UnitTest::Assert("buffer should still contain '12ACE'",
1155  ci.GetCurrentCommandBuffer(), "12ACE");
1156 
1157  ci.AddKey('x');
1158  ci.AddKey('y');
1159  UnitTest::Assert("buffer should now contain '12ACExy'",
1160  ci.GetCurrentCommandBuffer(), "12ACExy");
1161 
1162  ci.AddKey('\1'); // CTRL-A move to start of line again
1163  ci.AddKey('\6'); // CTRL-F move to the right
1164  ci.AddKey('\4'); // CTRL-D should remove character to the right
1165  UnitTest::Assert("buffer should now contain '1ACExy'",
1166  ci.GetCurrentCommandBuffer(), "1ACExy");
1167 }
1168 
1169 static void Test_CommandInterpreter_KeyBuffer_CtrlK()
1170 {
1171  GXemul gxemul;
1173 
1174  ci.AddKey('A');
1175  ci.AddKey('B');
1176  ci.AddKey('C');
1177  ci.AddKey('D');
1178  ci.AddKey('E');
1179  UnitTest::Assert("buffer should contain 'ABCDE'",
1180  ci.GetCurrentCommandBuffer(), "ABCDE");
1181 
1182  ci.AddKey('\2'); // CTRL-B should move back (left)
1183  ci.AddKey('\2');
1184  UnitTest::Assert("buffer should still contain 'ABCDE'",
1185  ci.GetCurrentCommandBuffer(), "ABCDE");
1186 
1187  ci.AddKey('\13'); // CTRL-K
1188  UnitTest::Assert("buffer should now contain 'ABC'",
1189  ci.GetCurrentCommandBuffer(), "ABC");
1190 
1191  ci.AddKey('X');
1192  ci.AddKey('\13'); // CTRL-K again, at end of line
1193  UnitTest::Assert("buffer should now contain 'ABCX'",
1194  ci.GetCurrentCommandBuffer(), "ABCX");
1195 
1196  ci.AddKey('\1'); // CTRL-A to move to start of line
1197  ci.AddKey('\13'); // CTRL-K again, should erase everything
1198  UnitTest::Assert("buffer should now be empty",
1199  ci.GetCurrentCommandBuffer(), "");
1200 }
1201 
1202 static void Test_CommandInterpreter_KeyBuffer_CtrlW()
1203 {
1204  GXemul gxemul;
1206 
1207  UnitTest::Assert("buffer should contain ''",
1208  ci.GetCurrentCommandBuffer(), "");
1209  ci.AddKey('\27'); // CTRL-W
1210  UnitTest::Assert("buffer should still contain ''",
1211  ci.GetCurrentCommandBuffer(), "");
1212 
1213  ci.AddKey('a');
1214  ci.AddKey('b');
1215  ci.AddKey('c');
1216  UnitTest::Assert("buffer should contain abc",
1217  ci.GetCurrentCommandBuffer(), "abc");
1218  ci.AddKey('\27'); // CTRL-W
1219  UnitTest::Assert("buffer should be empty again",
1220  ci.GetCurrentCommandBuffer(), "");
1221 
1222  ci.AddKey(' ');
1223  ci.AddKey(' ');
1224  ci.AddKey('a');
1225  ci.AddKey('b');
1226  ci.AddKey('c');
1227  UnitTest::Assert("buffer should contain ' abc'",
1228  ci.GetCurrentCommandBuffer(), " abc");
1229  ci.AddKey('\27'); // CTRL-W
1230  UnitTest::Assert("buffer should contain only two spaces",
1231  ci.GetCurrentCommandBuffer(), " ");
1232 
1233  ci.AddKey('a');
1234  ci.AddKey('b');
1235  ci.AddKey('c');
1236  ci.AddKey(' ');
1237  UnitTest::Assert("buffer should contain ' abc '",
1238  ci.GetCurrentCommandBuffer(), " abc ");
1239  ci.AddKey('\27'); // CTRL-W
1240  UnitTest::Assert("buffer should again contain only two spaces",
1241  ci.GetCurrentCommandBuffer(), " ");
1242 
1243  ci.AddKey('a');
1244  ci.AddKey('b');
1245  ci.AddKey('c');
1246  ci.AddKey('d');
1247  ci.AddKey(' ');
1248  ci.AddKey('e');
1249  ci.AddKey('f');
1250  ci.AddKey('g');
1251  ci.AddKey('h');
1252  ci.AddKey('i');
1253  ci.AddKey(' ');
1254  ci.AddKey('\2'); // CTRL-B = move left
1255  ci.AddKey('\2');
1256  ci.AddKey('\2');
1257  UnitTest::Assert("buffer should contain ' abcd efghi '",
1258  ci.GetCurrentCommandBuffer(), " abcd efghi ");
1259  ci.AddKey('\27'); // CTRL-W
1260  UnitTest::Assert("buffer should now contain ' abcd hi '",
1261  ci.GetCurrentCommandBuffer(), " abcd hi ");
1262 }
1263 
1264 static void Test_CommandInterpreter_CommandHistory()
1265 {
1266  GXemul gxemul;
1268 
1269  UnitTest::Assert("history should still be empty (1)",
1270  ci.AddLineToCommandHistory(""), 0);
1271 
1272  UnitTest::Assert("history should still be empty (2)",
1273  ci.AddLineToCommandHistory(""), 0);
1274 
1275  UnitTest::Assert("A: history line 0",
1276  ci.GetHistoryLine(0), "");
1277  UnitTest::Assert("A: history line 1 not set yet",
1278  ci.GetHistoryLine(1), "");
1279  UnitTest::Assert("A: history line 2 not set yet",
1280  ci.GetHistoryLine(2), "");
1281 
1282  UnitTest::Assert("history should contain one entry",
1283  ci.AddLineToCommandHistory("hello"), 1);
1284 
1285  UnitTest::Assert("B: history line 0",
1286  ci.GetHistoryLine(0), "");
1287  UnitTest::Assert("B: history line 1",
1288  ci.GetHistoryLine(1), "hello");
1289  UnitTest::Assert("B: history line 2 not set yet",
1290  ci.GetHistoryLine(2), "");
1291 
1292  UnitTest::Assert("history should contain two entries",
1293  ci.AddLineToCommandHistory("world"), 2);
1294 
1295  UnitTest::Assert("history should still contain two entries",
1296  ci.AddLineToCommandHistory("world"), 2);
1297 
1298  UnitTest::Assert("C: history line 0",
1299  ci.GetHistoryLine(0), "");
1300  UnitTest::Assert("C: history line 1",
1301  ci.GetHistoryLine(1), "world");
1302  UnitTest::Assert("C: history line 2",
1303  ci.GetHistoryLine(2), "hello");
1304 
1305  UnitTest::Assert("history should contain three entries",
1306  ci.AddLineToCommandHistory("hello"), 3);
1307 
1308  UnitTest::Assert("D: history line 0",
1309  ci.GetHistoryLine(0), "");
1310  UnitTest::Assert("D: history line 1",
1311  ci.GetHistoryLine(1), "hello");
1312  UnitTest::Assert("D: history line 2",
1313  ci.GetHistoryLine(2), "world");
1314 
1315  UnitTest::Assert("history should still contain three entries",
1316  ci.AddLineToCommandHistory(""), 3);
1317 }
1318 
1319 /**
1320  * \brief A dummy Command, for unit testing purposes
1321  */
1322 class DummyCommand2
1323  : public Command
1324 {
1325 public:
1326  DummyCommand2(int& valueRef)
1327  : Command("dummycommand", "[args]")
1328  , m_value(valueRef)
1329  {
1330  }
1331 
1332  ~DummyCommand2()
1333  {
1334  }
1335 
1336  bool Execute(GXemul& gxemul, const vector<string>& arguments)
1337  {
1338  m_value ++;
1339  return true;
1340  }
1341 
1342  string GetShortDescription() const
1343  {
1344  return "A dummy command used for unit testing.";
1345  }
1346 
1347  string GetLongDescription() const
1348  {
1349  return "This is just a dummy command used for unit testing.";
1350  }
1351 
1352 private:
1353  int& m_value;
1354 };
1355 
1356 /**
1357  * \brief A dummy Command, for unit testing purposes
1358  */
1359 class DummyCommand3
1360  : public Command
1361 {
1362 public:
1363  DummyCommand3(int& valueRef)
1364  : Command("dummycmd", "[args]")
1365  , m_value(valueRef)
1366  {
1367  }
1368 
1369  ~DummyCommand3()
1370  {
1371  }
1372 
1373  bool Execute(GXemul& gxemul, const vector<string>& arguments)
1374  {
1375  m_value ++;
1376  return true;
1377  }
1378 
1379  string GetShortDescription() const
1380  {
1381  return "A dummy command used for unit testing.";
1382  }
1383 
1384  string GetLongDescription() const
1385  {
1386  return "This is just a dummy command used for unit testing.";
1387  }
1388 
1389 private:
1390  int& m_value;
1391 };
1392 
1393 static void Test_CommandInterpreter_AddCommand()
1394 {
1395  GXemul gxemul;
1397 
1398  size_t nCommands = ci.GetCommands().size();
1399  UnitTest::Assert("there should be some commands already",
1400  nCommands > 0);
1401 
1402  ci.AddCommand(new VersionCommand);
1403 
1404  UnitTest::Assert("it should not be possible to have multiple commands"
1405  " with the same name",
1406  ci.GetCommands().size() == nCommands);
1407 
1408  int dummyInt = 42;
1409  ci.AddCommand(new DummyCommand2(dummyInt));
1410 
1411  UnitTest::Assert("it should be possible to add new commands",
1412  ci.GetCommands().size() == nCommands + 1);
1413 }
1414 
1415 static void Test_CommandInterpreter_TabCompletion_EmptyLine()
1416 {
1417  GXemul gxemul;
1419 
1420  ci.AddKey('\t');
1421  UnitTest::Assert("tab completion should not have produced anything",
1422  ci.GetCurrentCommandBuffer(), "");
1423 }
1424 
1425 static void Test_CommandInterpreter_TabCompletion_FullWord()
1426 {
1427  GXemul gxemul;
1429 
1430  ci.AddKey('d');
1431  ci.AddKey('u');
1432  ci.AddKey('m');
1433  ci.AddKey('m');
1434  ci.AddKey('Z');
1435  ci.AddKey('\2'); // CTRL-B = move left
1436  UnitTest::Assert("initial buffer contents mismatch",
1437  ci.GetCurrentCommandBuffer(), "dummZ");
1438 
1439  ci.AddKey('\t');
1440  UnitTest::Assert("tab completion should have failed",
1441  ci.GetCurrentCommandBuffer(), "dummZ");
1442 
1443  int dummyInt = 42;
1444  ci.AddCommand(new DummyCommand2(dummyInt));
1445 
1446  ci.AddKey('\t');
1447  UnitTest::Assert("tab completion should have succeeded",
1448  ci.GetCurrentCommandBuffer(), "dummycommandZ");
1449 
1450  ci.AddKey('X');
1451  UnitTest::Assert("tab completion should have placed cursor at end of"
1452  " the tab-completed word",
1453  ci.GetCurrentCommandBuffer(), "dummycommandXZ");
1454 }
1455 
1456 static void Test_CommandInterpreter_TabCompletion_SpacesFirstOnLine()
1457 {
1458  GXemul gxemul;
1460 
1461  ci.AddKey(' ');
1462  ci.AddKey(' ');
1463  ci.AddKey('v');
1464  ci.AddKey('e');
1465  ci.AddKey('r');
1466  ci.AddKey('s');
1467  UnitTest::Assert("initial buffer contents mismatch",
1468  ci.GetCurrentCommandBuffer(), " vers");
1469 
1470  ci.AddKey('\t');
1471  UnitTest::Assert("tab completion should have succeeded",
1472  ci.GetCurrentCommandBuffer(), " version ");
1473 }
1474 
1475 static void Test_CommandInterpreter_TabCompletion_Partial()
1476 {
1477  GXemul gxemul;
1479 
1480  ci.AddKey('d');
1481  ci.AddKey('u');
1482  ci.AddKey('m');
1483  ci.AddKey('m');
1484  ci.AddKey('Z');
1485  ci.AddKey('\2'); // CTRL-B = move left
1486  UnitTest::Assert("initial buffer contents mismatch",
1487  ci.GetCurrentCommandBuffer(), "dummZ");
1488 
1489  int dummyInt = 42;
1490  ci.AddCommand(new DummyCommand2(dummyInt));
1491  ci.AddCommand(new DummyCommand3(dummyInt));
1492 
1493  ci.AddKey('\t');
1494  UnitTest::Assert("tab completion should have partially succeeded",
1495  ci.GetCurrentCommandBuffer(), "dummycZ");
1496 }
1497 
1498 static void Test_CommandInterpreter_TabCompletion_C()
1499 {
1500  GXemul gxemul;
1502 
1503  ci.AddKey('c');
1504  UnitTest::Assert("initial buffer contents mismatch",
1505  ci.GetCurrentCommandBuffer(), "c");
1506 
1507  ci.AddKey('\t');
1508  UnitTest::Assert("tab completion should not have modified command",
1509  ci.GetCurrentCommandBuffer(), "c");
1510 
1511  // ... because there are at least two possible commands on the
1512  // letter c: close and continue.
1513 }
1514 
1515 static void Test_CommandInterpreter_TabCompletion_OnlyCommandAsFirstWord()
1516 {
1517  GXemul gxemul;
1519 
1520  ci.AddKey('v');
1521  ci.AddKey('e');
1522  ci.AddKey('r');
1523  ci.AddKey('s');
1524  UnitTest::Assert("initial buffer contents mismatch",
1525  ci.GetCurrentCommandBuffer(), "vers");
1526 
1527  ci.AddKey('\t');
1528  UnitTest::Assert("first tab completion should have succeeded",
1529  ci.GetCurrentCommandBuffer(), "version ");
1530 
1531  ci.AddKey('v');
1532  ci.AddKey('e');
1533  ci.AddKey('r');
1534  ci.AddKey('s');
1535  UnitTest::Assert("buffer contents mismatch",
1536  ci.GetCurrentCommandBuffer(), "version vers");
1537 
1538  ci.AddKey('\t');
1539  UnitTest::Assert("second tab completion should have failed",
1540  ci.GetCurrentCommandBuffer(), "version vers");
1541 }
1542 
1543 static void Test_CommandInterpreter_TabCompletion_ComponentName()
1544 {
1545  GXemul gxemul;
1547 
1548  ci.RunCommand("add testmips");
1549  UnitTest::Assert("initial buffer should be empty",
1550  ci.GetCurrentCommandBuffer(), "");
1551 
1552  ci.AddKey('c');
1553  ci.AddKey('p');
1554  ci.AddKey('\t');
1555  UnitTest::Assert("tab completion should have completed the "
1556  "component name",
1557  ci.GetCurrentCommandBuffer(), "cpu0");
1558 
1559  // Note: No space after component tab completion.
1560 }
1561 
1562 static void Test_CommandInterpreter_TabCompletion_ComponentNameAlreadyComplete()
1563 {
1564  GXemul gxemul;
1566 
1567  ci.RunCommand("add testmips");
1568  UnitTest::Assert("initial buffer should be empty",
1569  ci.GetCurrentCommandBuffer(), "");
1570 
1571  ci.AddKey('c');
1572  ci.AddKey('p');
1573  ci.AddKey('u');
1574  ci.AddKey('0');
1575  ci.AddKey('\t');
1576  UnitTest::Assert("tab completion should not have changed anything",
1577  ci.GetCurrentCommandBuffer(), "cpu0");
1578 
1579  // Note: No space after component tab completion.
1580 }
1581 
1582 static void Test_CommandInterpreter_TabCompletion_ComponentNameMultiple()
1583 {
1584  GXemul gxemul;
1586 
1587  ci.RunCommand("add testmips");
1588  ci.RunCommand("add mips_cpu mainbus0");
1589  UnitTest::Assert("initial buffer should be empty",
1590  ci.GetCurrentCommandBuffer(), "");
1591 
1592  ci.AddKey('c');
1593  ci.AddKey('p');
1594  ci.AddKey('\t');
1595  UnitTest::Assert("there are both cpu0 and cpu1, so don't expand all the way",
1596  ci.GetCurrentCommandBuffer(), "cpu");
1597 
1598  // Note: No space after component tab completion.
1599 }
1600 
1601 static void Test_CommandInterpreter_TabCompletion_ComponentNameMultipleParents()
1602 {
1603  GXemul gxemul;
1605 
1606  ci.RunCommand("add testmips"); // root.machine0
1607  ci.RunCommand("add testmips"); // root.machine1
1608  UnitTest::Assert("initial buffer should be empty",
1609  ci.GetCurrentCommandBuffer(), "");
1610 
1611  ci.AddKey('c');
1612  ci.AddKey('p');
1613  ci.AddKey('\t');
1614  UnitTest::Assert("there are cpu0 in both root.machine0 and root.machine1",
1615  ci.GetCurrentCommandBuffer(), "machine");
1616 
1617  // Note: No space after component tab completion.
1618 }
1619 
1620 static void Test_CommandInterpreter_TabCompletion_ComponentNameNonexist()
1621 {
1622  GXemul gxemul;
1624 
1625  ci.RunCommand("add dummy");
1626  UnitTest::Assert("initial buffer should be empty",
1627  ci.GetCurrentCommandBuffer(), "");
1628 
1629  ci.AddKey('r');
1630  ci.AddKey('o');
1631  ci.AddKey('o');
1632  ci.AddKey('t');
1633  ci.AddKey('.');
1634  ci.AddKey('X');
1635  ci.AddKey('\t');
1636  UnitTest::Assert("tab completion should not have succeeded",
1637  ci.GetCurrentCommandBuffer(), "root.X");
1638 }
1639 
1640 static void Test_CommandInterpreter_TabCompletion_ComponentNameAsArgument()
1641 {
1642  GXemul gxemul;
1644 
1645  ci.RunCommand("add dummy root");
1646  UnitTest::Assert("initial buffer should be empty",
1647  ci.GetCurrentCommandBuffer(), "");
1648 
1649  ci.AddKey('a');
1650  ci.AddKey('d');
1651  ci.AddKey('d');
1652  ci.AddKey(' ');
1653  ci.AddKey('d');
1654  ci.AddKey('u');
1655  ci.AddKey('m');
1656  ci.AddKey('m');
1657  ci.AddKey('y');
1658  ci.AddKey(' ');
1659  ci.AddKey('d');
1660  ci.AddKey('u');
1661  UnitTest::Assert("buffer contents mismatch",
1662  ci.GetCurrentCommandBuffer(), "add dummy du");
1663 
1664  ci.AddKey('\t');
1665  UnitTest::Assert("tab completion should have completed the "
1666  "component name",
1667  ci.GetCurrentCommandBuffer(), "add dummy dummy0");
1668 
1669  // Note: No space after component tab completion.
1670 }
1671 
1672 static void Test_CommandInterpreter_TabCompletion_CWithComponents()
1673 {
1674  GXemul gxemul;
1676 
1677  ci.RunCommand("add testmips");
1678 
1679  ci.AddKey('c');
1680  UnitTest::Assert("initial buffer contents mismatch",
1681  ci.GetCurrentCommandBuffer(), "c");
1682 
1683  ci.AddKey('\t');
1684  UnitTest::Assert("tab completion should not have modified command",
1685  ci.GetCurrentCommandBuffer(), "c");
1686 
1687  // ... because there are at least two possible commands on the
1688  // letter c: close and continue.
1689 }
1690 
1691 static void Test_CommandInterpreter_TabCompletion_roWithComponents()
1692 {
1693  GXemul gxemul;
1695 
1696  ci.RunCommand("add testmips");
1697 
1698  ci.AddKey('r');
1699  ci.AddKey('o');
1700  ci.AddKey('\t');
1701  // there are both "rom0" and "root".
1702  UnitTest::Assert("tab completion should not have expanded",
1703  ci.GetCurrentCommandBuffer(), "ro");
1704 
1705  ci.AddKey('o');
1706  ci.AddKey('\t');
1707  UnitTest::Assert("tab completion should have expanded to 'root'",
1708  ci.GetCurrentCommandBuffer(), "root");
1709 }
1710 
1711 static void Test_CommandInterpreter_TabCompletion_ComponentMethods_Empty()
1712 {
1713  GXemul gxemul;
1715 
1716  ci.RunCommand("add testmips");
1717 
1718  ci.AddKey('c');
1719  ci.AddKey('p');
1720  ci.AddKey('u');
1721  ci.AddKey('.');
1722  ci.AddKey('\t');
1723  UnitTest::Assert("tab completion should have caused expansion",
1724  ci.GetCurrentCommandBuffer(), "cpu0.");
1725 }
1726 
1727 static void Test_CommandInterpreter_TabCompletion_ComponentMethods()
1728 {
1729  GXemul gxemul;
1731 
1732  ci.RunCommand("add testmips");
1733 
1734  ci.AddKey('c');
1735  ci.AddKey('p');
1736  ci.AddKey('u');
1737  ci.AddKey('.');
1738  ci.AddKey('u');
1739  ci.AddKey('\t');
1740  UnitTest::Assert("tab completion should have caused expansion",
1741  ci.GetCurrentCommandBuffer(), "cpu0.unassemble");
1742 }
1743 
1744 static void Test_CommandInterpreter_TabCompletion_ComponentMethods_Middle()
1745 {
1746  GXemul gxemul;
1748 
1749  ci.RunCommand("add testmips");
1750 
1751  ci.AddKey('c');
1752  ci.AddKey('p');
1753  ci.AddKey('u');
1754  ci.AddKey('.');
1755  ci.AddKey('u');
1756  ci.AddKey('n');
1757  ci.AddKey('a');
1758  ci.AddKey('b');
1759  ci.AddKey('c');
1760  ci.AddKey('d');
1761  ci.AddKey('\2');
1762  ci.AddKey('\2');
1763  ci.AddKey('\2'); // cursor placed after "una"
1764  ci.AddKey('\t');
1765  UnitTest::Assert("tab completion should have caused expansion",
1766  ci.GetCurrentCommandBuffer(), "cpu0.unassemblebcd");
1767 }
1768 
1769 static void Test_CommandInterpreter_TabCompletion_ComponentMethods_Arg()
1770 {
1771  GXemul gxemul;
1773 
1774  ci.RunCommand("add testmips");
1775 
1776  ci.AddKey('c');
1777  ci.AddKey('p');
1778  ci.AddKey('u');
1779  ci.AddKey('.');
1780  ci.AddKey('u');
1781  ci.AddKey(' ');
1782  ci.AddKey('a');
1783  ci.AddKey('d');
1784  ci.AddKey('d');
1785  ci.AddKey('r');
1786  ci.AddKey('\2');
1787  ci.AddKey('\2');
1788  ci.AddKey('\2');
1789  ci.AddKey('\2');
1790  ci.AddKey('\2'); // cursor placed after "u"
1791  ci.AddKey('\t');
1792  UnitTest::Assert("tab completion should have caused expansion",
1793  ci.GetCurrentCommandBuffer(), "cpu0.unassemble addr");
1794 }
1795 
1796 static void Test_CommandInterpreter_TabCompletion_ComponentVariables()
1797 {
1798  GXemul gxemul;
1800 
1801  ci.RunCommand("add testmips");
1802 
1803  ci.AddKey('c');
1804  ci.AddKey('p');
1805  ci.AddKey('u');
1806  ci.AddKey('.');
1807  ci.AddKey('g');
1808  ci.AddKey('\t');
1809  UnitTest::Assert("tab completion should have caused expansion cpu -> cpu0",
1810  ci.GetCurrentCommandBuffer(), "cpu0.gp");
1811 }
1812 
1813 static void Test_CommandInterpreter_TabCompletion_ComponentVariables_Max()
1814 {
1815  GXemul gxemul;
1817 
1818  ci.RunCommand("add ram");
1819 
1820  ci.AddKey('r');
1821  ci.AddKey('a');
1822  ci.AddKey('m');
1823  ci.AddKey('.');
1824  ci.AddKey('m');
1825  ci.AddKey('e');
1826  ci.AddKey('m');
1827  ci.AddKey('o');
1828  ci.AddKey('\t');
1829  UnitTest::Assert("tab completion should have caused expansion ram -> ram0",
1830  ci.GetCurrentCommandBuffer(), "ram0.memoryMapped");
1831 }
1832 
1833 static void Test_CommandInterpreter_TabCompletion_ComponentVariables_Max2()
1834 {
1835  GXemul gxemul;
1837 
1838  ci.RunCommand("add ram");
1839 
1840  ci.AddKey('r');
1841  ci.AddKey('a');
1842  ci.AddKey('m');
1843  ci.AddKey('.');
1844  ci.AddKey('m');
1845  ci.AddKey('e');
1846  ci.AddKey('m');
1847  ci.AddKey('o');
1848  ci.AddKey(' ');
1849  ci.AddKey('2');
1850  ci.AddKey('\2');
1851  ci.AddKey('\2');
1852  ci.AddKey('\t');
1853  UnitTest::Assert("tab completion should have caused expansion ram -> ram0",
1854  ci.GetCurrentCommandBuffer(), "ram0.memoryMapped 2");
1855 
1856  ci.AddKey('X');
1857  UnitTest::Assert("cursor position after tab completion was wrong?",
1858  ci.GetCurrentCommandBuffer(), "ram0.memoryMappedX 2");
1859 }
1860 
1861 static void Test_CommandInterpreter_NonExistingCommand()
1862 {
1863  GXemul gxemul;
1865 
1866  UnitTest::Assert("nonexisting (nonsense) command should fail",
1867  ci.RunCommand("nonexistingcommand") == false);
1868 }
1869 
1870 static void Test_CommandInterpreter_SimpleCommand()
1871 {
1872  GXemul gxemul;
1874 
1875  UnitTest::Assert("simple command should succeed",
1876  ci.RunCommand("version") == true);
1877 
1878  UnitTest::Assert("simple command with whitespace should succeed",
1879  ci.RunCommand(" version ") == true);
1880 }
1881 
1882 static void Test_CommandInterpreter_SimpleCommand_NoArgsAllowed()
1883 {
1884  GXemul gxemul;
1886 
1887  UnitTest::Assert("simple command should succeed",
1888  ci.RunCommand("version") == true);
1889 
1890  UnitTest::Assert("simple command which does not take arguments should"
1891  " fail when attempt is made to execute it with arguments",
1892  ci.RunCommand("version hello") == false);
1893 }
1894 
1895 static void Test_CommandInterpreter_ComponentMethods()
1896 {
1897  GXemul gxemul;
1899 
1900  UnitTest::Assert("Huh? Could not add testmips.",
1901  ci.RunCommand("add testmips") == true);
1902 
1903  UnitTest::Assert("component method 1",
1904  ci.RunCommand("cpu") == true);
1905  UnitTest::Assert("component method 2",
1906  ci.RunCommand("cpu.u") == true);
1907  UnitTest::Assert("component method 3",
1908  ci.RunCommand("cpu.urk") == false);
1909  UnitTest::Assert("component method 4",
1910  ci.RunCommand("cpu.unassemble") == true);
1911  UnitTest::Assert("component method 5",
1912  ci.RunCommand("root.machine0.mainbus0.cpu") == true);
1913  UnitTest::Assert("component method 6",
1914  ci.RunCommand("root.machine0.mainbus0.cpu0") == true);
1915  UnitTest::Assert("component method 7",
1916  ci.RunCommand("root.machine0.mainbus0.cpu.u") == true);
1917  UnitTest::Assert("component method 8",
1918  ci.RunCommand("root.machine0.mainbus0.cpu0.unassemble") == true);
1919 }
1920 
1921 static void Test_CommandInterpreter_ComponentVariables_NoArgs()
1922 {
1923  GXemul gxemul;
1925 
1926  UnitTest::Assert("Huh? Could not add testmips.",
1927  ci.RunCommand("add testmips") == true);
1928 
1929  UnitTest::Assert("component variable 1",
1930  ci.RunCommand("cpu.nonexistant") == false);
1931  UnitTest::Assert("component variable 2",
1932  ci.RunCommand("cpu.gp") == true);
1933  UnitTest::Assert("component variable 3",
1934  ci.RunCommand("root.machine0.mainbus0.cpu0.g") == true);
1935 }
1936 
1937 static void Test_CommandInterpreter_ComponentVariables_Ambiguous()
1938 {
1939  GXemul gxemul;
1941 
1942  UnitTest::Assert("Huh? Could not add testmips.",
1943  ci.RunCommand("add testmipsk") == true);
1944 
1945  UnitTest::Assert("cpu.t should not work, there should be multiple matches",
1946  ci.RunCommand("cpu.t") == false);
1947 }
1948 
1949 static void Test_CommandInterpreter_ComponentVariables_PartialIsOk()
1950 {
1951  GXemul gxemul;
1953 
1954  UnitTest::Assert("Huh? Could not add testm88k.",
1955  ci.RunCommand("add testm88k") == true);
1956 
1957  // Make sure that r2 works, and doesn't complain about ambiguity.
1958  UnitTest::Assert("gpr 2",
1959  ci.RunCommand("cpu.r2") == true);
1960  UnitTest::Assert("gpr 29",
1961  ci.RunCommand("cpu.r29") == true);
1962 }
1963 
1965 {
1966  // Key and current buffer:
1967  UNITTEST(Test_CommandInterpreter_AddKey_ReturnValue);
1968  UNITTEST(Test_CommandInterpreter_KeyBuffer);
1969  UNITTEST(Test_CommandInterpreter_KeyBuffer_CursorMovement);
1970  UNITTEST(Test_CommandInterpreter_KeyBuffer_CtrlK);
1971  UNITTEST(Test_CommandInterpreter_KeyBuffer_CtrlW);
1972 
1973  // Command History:
1974  UNITTEST(Test_CommandInterpreter_CommandHistory);
1975 
1976  // AddCommand / GetCommands:
1977  UNITTEST(Test_CommandInterpreter_AddCommand);
1978 
1979  // Tab completion:
1980  UNITTEST(Test_CommandInterpreter_TabCompletion_EmptyLine);
1981  UNITTEST(Test_CommandInterpreter_TabCompletion_FullWord);
1982  UNITTEST(Test_CommandInterpreter_TabCompletion_SpacesFirstOnLine);
1983  UNITTEST(Test_CommandInterpreter_TabCompletion_Partial);
1984  UNITTEST(Test_CommandInterpreter_TabCompletion_C);
1985  UNITTEST(Test_CommandInterpreter_TabCompletion_OnlyCommandAsFirstWord);
1986  UNITTEST(Test_CommandInterpreter_TabCompletion_ComponentName);
1987  UNITTEST(Test_CommandInterpreter_TabCompletion_ComponentNameAlreadyComplete);
1988  UNITTEST(Test_CommandInterpreter_TabCompletion_ComponentNameMultiple);
1989  UNITTEST(Test_CommandInterpreter_TabCompletion_ComponentNameMultipleParents);
1990  UNITTEST(Test_CommandInterpreter_TabCompletion_ComponentNameNonexist);
1991  UNITTEST(Test_CommandInterpreter_TabCompletion_ComponentNameAsArgument);
1992  UNITTEST(Test_CommandInterpreter_TabCompletion_CWithComponents);
1993  UNITTEST(Test_CommandInterpreter_TabCompletion_roWithComponents);
1994  UNITTEST(Test_CommandInterpreter_TabCompletion_ComponentMethods_Empty);
1995  UNITTEST(Test_CommandInterpreter_TabCompletion_ComponentMethods);
1996  UNITTEST(Test_CommandInterpreter_TabCompletion_ComponentMethods_Middle);
1997  UNITTEST(Test_CommandInterpreter_TabCompletion_ComponentMethods_Arg);
1998  UNITTEST(Test_CommandInterpreter_TabCompletion_ComponentVariables);
1999  UNITTEST(Test_CommandInterpreter_TabCompletion_ComponentVariables_Max);
2000  UNITTEST(Test_CommandInterpreter_TabCompletion_ComponentVariables_Max2);
2001 
2002  // RunCommand:
2003  UNITTEST(Test_CommandInterpreter_NonExistingCommand);
2004  UNITTEST(Test_CommandInterpreter_SimpleCommand);
2005  UNITTEST(Test_CommandInterpreter_SimpleCommand_NoArgsAllowed);
2006  UNITTEST(Test_CommandInterpreter_ComponentMethods);
2007  UNITTEST(Test_CommandInterpreter_ComponentVariables_NoArgs);
2008  UNITTEST(Test_CommandInterpreter_ComponentVariables_Ambiguous);
2009  UNITTEST(Test_CommandInterpreter_ComponentVariables_PartialIsOk);
2010 }
2011 
2012 #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:216
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:54
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:631
StateVariable
StateVariables make up the persistent state of Component objects.
Definition: StateVariable.h:67
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:48
CommandInterpreter::AddCommand
void AddCommand(refcount_ptr< Command > command)
Adds a new Command to the command interpreter.
Definition: CommandInterpreter.cc:57
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:40
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
GXemul::GetRootComponent
refcount_ptr< Component > GetRootComponent()
Gets a pointer to the root configuration component.
Definition: GXemul.cc:667
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:957
CommandInterpreter::GetCurrentCommandBuffer
const string & GetCurrentCommandBuffer() const
Retrieves the current command buffer.
Definition: CommandInterpreter.cc:1013
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:48
GXemul::GetUI
UI * GetUI()
Gets a pointer to the GXemul instance' active UI.
Definition: GXemul.cc:661
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.
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:637
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:654
GXemul.h

Generated on Tue Mar 24 2020 14:04:48 for GXemul by doxygen 1.8.17