ghidra/Ghidra/Features/Decompiler/src/decompile/cpp/ifaceterm.cc

271 lines
6.9 KiB
C++

/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "ifaceterm.hh"
namespace ghidra {
IfaceTerm::IfaceTerm(const string &prmpt,istream &is,ostream &os)
: IfaceStatus(prmpt,os)
{
sptr = &is;
#ifdef __TERMINAL__
struct termios ittypass;
is_terminal = true;
// ifd = fileno( (FILE *)sptr->rdbuf() );
ifd = 0; // The above line doesn't work on some systems
// and since ifd will almost always refer to stdin...
if (0>tcgetattr(ifd,&itty)) { // Get original terminal settings
if (errno == EBADF)
throw IfaceError("Bad input file descriptor passed to iface");
else if (errno == ENOTTY) {
is_terminal = false;
return;
}
throw IfaceError("Unknown error with input file stream");
}
// Build terminal settings for entering passphrase
ittypass = itty;
ittypass.c_lflag &= ~((tcflag_t)ECHO); // Turn off echo
ittypass.c_lflag &= ~((tcflag_t)ICANON); // Turn of buffered input
ittypass.c_cc[VMIN] = 1; // Buffer only one character at a time
ittypass.c_cc[VTIME] = 0; // Do not time out
if (0>tcsetattr(ifd,TCSANOW,&ittypass))
throw IfaceError("Unable to set terminal attributes");
#endif
}
IfaceTerm::~IfaceTerm(void)
{
while(!inputstack.empty()) {
delete sptr;
sptr = inputstack.back();
inputstack.pop_back();
}
#ifdef __TERMINAL__
if (is_terminal) {
tcsetattr(ifd,TCSANOW,&itty); // Restore original terminal settings
}
#endif
}
/// Respond to a TAB key press and try to 'complete' any existing tokens.
/// The method is handed the current state of the command-line in a string, and
/// it updates the command-line in place.
///
/// \param line is current command-line and will hold the final completion
/// \param cursor is the current position of the cursor
/// \return the (possibly new) position of the cursor, after completion
int4 IfaceTerm::doCompletion(string &line,int4 cursor)
{
vector<string> fullcommand;
istringstream s(line);
string tok;
vector<IfaceCommand *>::const_iterator first,last;
int4 oldsize,match;
first = comlist.begin();
last = comlist.end();
match = expandCom(fullcommand,s,first,last); // Try to expand the command
if (match == 0) {
*optr << endl << "Invalid command" << endl;
return cursor; // No change to command line
}
// At least one match
oldsize = line.size();
wordsToString(line,fullcommand);
if (match<0)
match = -match;
else
line += ' '; // Provide extra space if command word is complete
if (!s.eof()) { // Read any additional parameters back to command line
s >> tok >> ws;
line += tok; // Assume first space is present before extra params
}
while(!s.eof()) {
line += ' '; // Provide space between extra parameters
s >> tok >> ws;
line += tok;
}
if (oldsize < line.size()) // If we have expanded at all
return line.size(); // Just display expansion
if (match > 1) { // If more than one possible command
string complete; // Display all possible completions
*optr << endl;
for(;first!=last;++first) {
(*first)->commandString(complete); // Get possible completion
*optr << complete << endl;
}
}
else // Command is unique and expanded
*optr << endl << "Command is complete" << endl;
return line.size();
}
void IfaceTerm::readLine(string &line)
{
char val;
int4 escval;
int4 cursor,lastlen,i;
bool onecharecho;
int4 hist;
string saveline;
line.erase();
cursor = 0;
hist = 0;
do {
onecharecho = false;
lastlen = line.size();
val = sptr->get();
if (sptr->eof())
val = '\n';
switch(val) {
case 0x01: // C-a
cursor = 0; // Jump to beginning
break;
case 0x02: // C-b
if (cursor>0)
cursor -= 1; // back up one char
break;
case 0x03: // C-c
line.erase(); // As if we cleared the line and hit return
cursor = 0;
val = 0x0a;
onecharecho = true;
break;
case 0x04: // C-d
line.erase(cursor,1); // Delete character cursor is on
break;
case 0x05: // C-e
cursor = line.size(); // Jump to end
break;
case 0x06: // C-f
if (cursor < line.size())
cursor += 1; // Move forward one character
break;
case 0x07: // C-g
break; // do nothing
case 0x09: // TAB
cursor = doCompletion(line,cursor);
break;
case 0x0a: // Newline
case 0x0d: // Carriage return
cursor = line.size();
onecharecho = true;
break;
case 0x0b: // C-k
line.erase(cursor);
break;
case 0x0c: // C-l
break;
case 0x0e: // C-n
if (hist >0) {
hist -= 1; // Get more recent history
if (hist>0)
getHistory(line,hist-1);
else
line = saveline;
cursor = line.size();
}
break;
case 0x10: // C-p
if (hist < getHistorySize()) {
hist += 1; // Get more ancient history
if (hist==1)
saveline = line;
getHistory(line,hist-1);
cursor = line.size();
}
break;
case 0x12: // C-r
break;
case 0x15: // C-u
line.erase(0,cursor); // Erase up to cursor
cursor = 0;
break;
case 0x1b: // Escape character
escval = sptr->get();
escval <<= 8;
escval += sptr->get();
switch(escval) {
case 0x4f44: // left arrow
if (cursor>0)
cursor -= 1;
break;
case 0x4f43: // right arrow
if (cursor<line.size())
cursor += 1;
break;
}
break;
case 0x08:
case 0x7f: // delete
if (cursor!=0)
line.erase(--cursor,1);
break;
default:
line.insert(cursor++,1,val); // Insert single character
if (cursor == line.size())
onecharecho = true;
break;
}
if (onecharecho)
optr->put(val); // Echo most characters
else {
optr->put('\r'); // Ontop of old line
writePrompt();
*optr << line; // print new line
for(i=line.size();i<lastlen;++i)
optr->put(' '); // Erase any old characters
for(i=i-cursor;i>0;--i)
optr->put('\b'); // Put cursor in the right place
}
} while(val != '\n');
}
void IfaceTerm::pushScript(istream *iptr,const string &newprompt)
{
inputstack.push_back(sptr);
sptr = iptr;
IfaceStatus::pushScript(iptr,newprompt);
}
void IfaceTerm::popScript(void)
{
delete sptr;
sptr = inputstack.back();
inputstack.pop_back();
IfaceStatus::popScript();
}
bool IfaceTerm::isStreamFinished(void) const
{
if (done||inerror) return true;
return sptr->eof();
}
} // End namespace ghidra