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 
CommandInterpreter(GXemul * owner)38 CommandInterpreter::CommandInterpreter(GXemul* owner)
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 
AddCommand(refcount_ptr<Command> command)57 void CommandInterpreter::AddCommand(refcount_ptr<Command> command)
58 {
59 	m_commands[command->GetCommandName()] = command;
60 }
61 
62 
GetCommands() const63 const Commands& CommandInterpreter::GetCommands() const
64 {
65 	return m_commands;
66 }
67 
68 
AddLineToCommandHistory(const string & command)69 int CommandInterpreter::AddLineToCommandHistory(const string& command)
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 
GetHistoryLine(int nStepsBack) const88 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 
TabComplete(string & commandString,size_t & cursorPosition,bool visibleShowAvailable)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 
TabCompleteWithSubname(string & commandString,size_t & cursorPosition,bool visibleShowAvailable)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 
AddKey(stringchar key)381 bool CommandInterpreter::AddKey(stringchar key)
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);
571 			ClearCurrentCommandBuffer();
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')
588 		ReshowCurrentCommandBuffer();
589 
590 	// Return value is true for newline/cr, false otherwise:
591 	return key == '\n' || key == '\r';
592 }
593 
594 
ShowAvailableWords(const vector<string> & words)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 
ReshowCurrentCommandBuffer()638 void CommandInterpreter::ReshowCurrentCommandBuffer()
639 {
640 	m_GXemul->GetUI()->RedisplayInputLine(
641 	    m_currentCommandString, m_currentCommandCursorPosition);
642 }
643 
644 
ClearCurrentInputLineVisually()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 
ClearCurrentCommandBuffer()655 void CommandInterpreter::ClearCurrentCommandBuffer()
656 {
657 	m_currentCommandString = "";
658 	m_currentCommandCursorPosition = 0;
659 	m_historyEntryToCopyFrom = 0;
660 }
661 
662 
SplitIntoWords(const string & commandOrig,string & commandName,vector<string> & arguments)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 
VariableAssignment(const string & componentPath,const string & variableName,const string & expression)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 
RunComponentMethod(const string & componentPathAndMethod,const vector<string> & arguments)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 
RunCommand(const string & command,bool * pSuccess)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 
GetCurrentCommandBuffer() const1019 const string& CommandInterpreter::GetCurrentCommandBuffer() const
1020 {
1021 	return m_currentCommandString;
1022 }
1023 
1024 
1025 /*****************************************************************************/
1026 
1027 
1028 #ifdef WITHUNITTESTS
1029 
Test_CommandInterpreter_AddKey_ReturnValue()1030 static void Test_CommandInterpreter_AddKey_ReturnValue()
1031 {
1032 	GXemul gxemul;
1033 	CommandInterpreter& ci = gxemul.GetCommandInterpreter();
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 
Test_CommandInterpreter_KeyBuffer()1048 static void Test_CommandInterpreter_KeyBuffer()
1049 {
1050 	GXemul gxemul;
1051 	CommandInterpreter& ci = gxemul.GetCommandInterpreter();
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 
Test_CommandInterpreter_KeyBuffer_CursorMovement()1118 static void Test_CommandInterpreter_KeyBuffer_CursorMovement()
1119 {
1120 	GXemul gxemul;
1121 	CommandInterpreter& ci = gxemul.GetCommandInterpreter();
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 
Test_CommandInterpreter_KeyBuffer_CtrlK()1175 static void Test_CommandInterpreter_KeyBuffer_CtrlK()
1176 {
1177 	GXemul gxemul;
1178 	CommandInterpreter& ci = gxemul.GetCommandInterpreter();
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 
Test_CommandInterpreter_KeyBuffer_CtrlW()1208 static void Test_CommandInterpreter_KeyBuffer_CtrlW()
1209 {
1210 	GXemul gxemul;
1211 	CommandInterpreter& ci = gxemul.GetCommandInterpreter();
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 
Test_CommandInterpreter_CommandHistory()1270 static void Test_CommandInterpreter_CommandHistory()
1271 {
1272 	GXemul gxemul;
1273 	CommandInterpreter& ci = gxemul.GetCommandInterpreter();
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:
DummyCommand2(int & valueRef)1332 	DummyCommand2(int& valueRef)
1333 		: Command("dummycommand", "[args]")
1334 		, m_value(valueRef)
1335 	{
1336 	}
1337 
~DummyCommand2()1338 	~DummyCommand2()
1339 	{
1340 	}
1341 
Execute(GXemul & gxemul,const vector<string> & arguments)1342 	bool Execute(GXemul& gxemul, const vector<string>& arguments)
1343 	{
1344 		m_value ++;
1345 		return true;
1346 	}
1347 
GetShortDescription() const1348 	string GetShortDescription() const
1349 	{
1350 		return "A dummy command used for unit testing.";
1351 	}
1352 
GetLongDescription() const1353 	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:
DummyCommand3(int & valueRef)1369 	DummyCommand3(int& valueRef)
1370 		: Command("dummycmd", "[args]")
1371 		, m_value(valueRef)
1372 	{
1373 	}
1374 
~DummyCommand3()1375 	~DummyCommand3()
1376 	{
1377 	}
1378 
Execute(GXemul & gxemul,const vector<string> & arguments)1379 	bool Execute(GXemul& gxemul, const vector<string>& arguments)
1380 	{
1381 		m_value ++;
1382 		return true;
1383 	}
1384 
GetShortDescription() const1385 	string GetShortDescription() const
1386 	{
1387 		return "A dummy command used for unit testing.";
1388 	}
1389 
GetLongDescription() const1390 	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 
Test_CommandInterpreter_AddCommand()1399 static void Test_CommandInterpreter_AddCommand()
1400 {
1401 	GXemul gxemul;
1402 	CommandInterpreter& ci = gxemul.GetCommandInterpreter();
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 
Test_CommandInterpreter_TabCompletion_EmptyLine()1421 static void Test_CommandInterpreter_TabCompletion_EmptyLine()
1422 {
1423 	GXemul gxemul;
1424 	CommandInterpreter& ci = gxemul.GetCommandInterpreter();
1425 
1426 	ci.AddKey('\t');
1427 	UnitTest::Assert("tab completion should not have produced anything",
1428 	    ci.GetCurrentCommandBuffer(), "");
1429 }
1430 
Test_CommandInterpreter_TabCompletion_FullWord()1431 static void Test_CommandInterpreter_TabCompletion_FullWord()
1432 {
1433 	GXemul gxemul;
1434 	CommandInterpreter& ci = gxemul.GetCommandInterpreter();
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 
Test_CommandInterpreter_TabCompletion_SpacesFirstOnLine()1462 static void Test_CommandInterpreter_TabCompletion_SpacesFirstOnLine()
1463 {
1464 	GXemul gxemul;
1465 	CommandInterpreter& ci = gxemul.GetCommandInterpreter();
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 
Test_CommandInterpreter_TabCompletion_Partial()1481 static void Test_CommandInterpreter_TabCompletion_Partial()
1482 {
1483 	GXemul gxemul;
1484 	CommandInterpreter& ci = gxemul.GetCommandInterpreter();
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 
Test_CommandInterpreter_TabCompletion_C()1504 static void Test_CommandInterpreter_TabCompletion_C()
1505 {
1506 	GXemul gxemul;
1507 	CommandInterpreter& ci = gxemul.GetCommandInterpreter();
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 
Test_CommandInterpreter_TabCompletion_OnlyCommandAsFirstWord()1521 static void Test_CommandInterpreter_TabCompletion_OnlyCommandAsFirstWord()
1522 {
1523 	GXemul gxemul;
1524 	CommandInterpreter& ci = gxemul.GetCommandInterpreter();
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 
Test_CommandInterpreter_TabCompletion_ComponentName()1549 static void Test_CommandInterpreter_TabCompletion_ComponentName()
1550 {
1551 	GXemul gxemul;
1552 	CommandInterpreter& ci = gxemul.GetCommandInterpreter();
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 
Test_CommandInterpreter_TabCompletion_ComponentNameAlreadyComplete()1568 static void Test_CommandInterpreter_TabCompletion_ComponentNameAlreadyComplete()
1569 {
1570 	GXemul gxemul;
1571 	CommandInterpreter& ci = gxemul.GetCommandInterpreter();
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 
Test_CommandInterpreter_TabCompletion_ComponentNameMultiple()1588 static void Test_CommandInterpreter_TabCompletion_ComponentNameMultiple()
1589 {
1590 	GXemul gxemul;
1591 	CommandInterpreter& ci = gxemul.GetCommandInterpreter();
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 
Test_CommandInterpreter_TabCompletion_ComponentNameMultipleParents()1607 static void Test_CommandInterpreter_TabCompletion_ComponentNameMultipleParents()
1608 {
1609 	GXemul gxemul;
1610 	CommandInterpreter& ci = gxemul.GetCommandInterpreter();
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 
Test_CommandInterpreter_TabCompletion_ComponentNameNonexist()1626 static void Test_CommandInterpreter_TabCompletion_ComponentNameNonexist()
1627 {
1628 	GXemul gxemul;
1629 	CommandInterpreter& ci = gxemul.GetCommandInterpreter();
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 
Test_CommandInterpreter_TabCompletion_ComponentNameAsArgument()1646 static void Test_CommandInterpreter_TabCompletion_ComponentNameAsArgument()
1647 {
1648 	GXemul gxemul;
1649 	CommandInterpreter& ci = gxemul.GetCommandInterpreter();
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 
Test_CommandInterpreter_TabCompletion_CWithComponents()1678 static void Test_CommandInterpreter_TabCompletion_CWithComponents()
1679 {
1680 	GXemul gxemul;
1681 	CommandInterpreter& ci = gxemul.GetCommandInterpreter();
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 
Test_CommandInterpreter_TabCompletion_roWithComponents()1697 static void Test_CommandInterpreter_TabCompletion_roWithComponents()
1698 {
1699 	GXemul gxemul;
1700 	CommandInterpreter& ci = gxemul.GetCommandInterpreter();
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 
Test_CommandInterpreter_TabCompletion_ComponentMethods_Empty()1717 static void Test_CommandInterpreter_TabCompletion_ComponentMethods_Empty()
1718 {
1719 	GXemul gxemul;
1720 	CommandInterpreter& ci = gxemul.GetCommandInterpreter();
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 
Test_CommandInterpreter_TabCompletion_ComponentMethods()1733 static void Test_CommandInterpreter_TabCompletion_ComponentMethods()
1734 {
1735 	GXemul gxemul;
1736 	CommandInterpreter& ci = gxemul.GetCommandInterpreter();
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 
Test_CommandInterpreter_TabCompletion_ComponentMethods_Middle()1750 static void Test_CommandInterpreter_TabCompletion_ComponentMethods_Middle()
1751 {
1752 	GXemul gxemul;
1753 	CommandInterpreter& ci = gxemul.GetCommandInterpreter();
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 
Test_CommandInterpreter_TabCompletion_ComponentMethods_Arg()1775 static void Test_CommandInterpreter_TabCompletion_ComponentMethods_Arg()
1776 {
1777 	GXemul gxemul;
1778 	CommandInterpreter& ci = gxemul.GetCommandInterpreter();
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 
Test_CommandInterpreter_TabCompletion_ComponentVariables()1802 static void Test_CommandInterpreter_TabCompletion_ComponentVariables()
1803 {
1804 	GXemul gxemul;
1805 	CommandInterpreter& ci = gxemul.GetCommandInterpreter();
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 
Test_CommandInterpreter_TabCompletion_ComponentVariables_Max()1820 static void Test_CommandInterpreter_TabCompletion_ComponentVariables_Max()
1821 {
1822 	GXemul gxemul;
1823 	CommandInterpreter& ci = gxemul.GetCommandInterpreter();
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 
Test_CommandInterpreter_TabCompletion_ComponentVariables_Max2()1840 static void Test_CommandInterpreter_TabCompletion_ComponentVariables_Max2()
1841 {
1842 	GXemul gxemul;
1843 	CommandInterpreter& ci = gxemul.GetCommandInterpreter();
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 
Test_CommandInterpreter_NonExistingCommand()1868 static void Test_CommandInterpreter_NonExistingCommand()
1869 {
1870 	GXemul gxemul;
1871 	CommandInterpreter& ci = gxemul.GetCommandInterpreter();
1872 
1873 	UnitTest::Assert("nonexisting (nonsense) command should fail",
1874 	    ci.RunCommand("nonexistingcommand") == false);
1875 }
1876 
Test_CommandInterpreter_SimpleCommand()1877 static void Test_CommandInterpreter_SimpleCommand()
1878 {
1879 	GXemul gxemul;
1880 	CommandInterpreter& ci = gxemul.GetCommandInterpreter();
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 
Test_CommandInterpreter_SimpleCommand_NoArgsAllowed()1889 static void Test_CommandInterpreter_SimpleCommand_NoArgsAllowed()
1890 {
1891 	GXemul gxemul;
1892 	CommandInterpreter& ci = gxemul.GetCommandInterpreter();
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 
Test_CommandInterpreter_ComponentMethods()1902 static void Test_CommandInterpreter_ComponentMethods()
1903 {
1904 	GXemul gxemul;
1905 	CommandInterpreter& ci = gxemul.GetCommandInterpreter();
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 
Test_CommandInterpreter_ComponentVariables_NoArgs()1928 static void Test_CommandInterpreter_ComponentVariables_NoArgs()
1929 {
1930 	GXemul gxemul;
1931 	CommandInterpreter& ci = gxemul.GetCommandInterpreter();
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 
Test_CommandInterpreter_ComponentVariables_Ambiguous()1944 static void Test_CommandInterpreter_ComponentVariables_Ambiguous()
1945 {
1946 	GXemul gxemul;
1947 	CommandInterpreter& ci = gxemul.GetCommandInterpreter();
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 
Test_CommandInterpreter_ComponentVariables_PartialIsOk()1956 static void Test_CommandInterpreter_ComponentVariables_PartialIsOk()
1957 {
1958 	GXemul gxemul;
1959 	CommandInterpreter& ci = gxemul.GetCommandInterpreter();
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 
UNITTESTS(CommandInterpreter)1971 UNITTESTS(CommandInterpreter)
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
2020