I'm writing a shell-like interpreter using getchar()
for buffered input.
Enter
is pressed, the interpreter should process the buffer, then prompt for a new line.Ctrl+d
is pressed, the interpreter should process the buffer, then exit.The code below runs, but does not fully satisfy the second requirement.
#include <iostream>
using namespace std;
void shell() {
for (int input;;) {
// prompt at beginning of line
std::cout << ">>> ";
while ((input = getchar()) != '\n') {
// still in current line
if (input == EOF) {
// ctrl+d pressed at beginning of line
return;
}
std::cout << "[" << (char)input << "]";
}
// reached end of line
}
}
int main() {
cout << "before shell" << endl;
shell();
cout << "shell has exited" << endl;
return 0;
}
My issue is getchar()
only returns EOF
when the buffer is empty. Pressing Ctrl+d
mid-line causes getchar()
to return each buffered character except the EOF
character itself.
How can I determine if Ctrl+d
is pressed mid-line?
I've considered using a timeout. In this method, the interpreter assumes Ctrl+d
was pressed if getchar()
halts for too long after returning something other than a line feed. This is not my favorite approach because it requires threading, introduces latency, and the appropriate waiting period is unclear.
With a normal line there's a '\n'
at the end. Not so with a line pushed with Ctrl+D in Unix-land shell. So, for example,
#include <stdio.h>
#include <unistd.h> // read
void shell()
{
char line[256];
for( bool finished = false; not finished; )
{
printf( ">>> " );
//fgets( line, sizeof( line ), stdin );
fflush( stdout );
const int n_bytes = read( 0, line, sizeof( line ) - 1 );
line[n_bytes] = '\0';
char const* p = line;
finished = true;
while( char const input = *p++ )
{
if( input == '\n' )
{
finished = false;
break;
}
printf( "[%c]", input );
}
printf( "\n" );
}
}
auto main()
-> int
{
printf( "before shell\n" );
shell();
printf( "shell has exited\n" );
}
Leaving for you to fix the following:
FILE*
i/o. Note 1: read
is generally available also with Windows compilers. However,
Note 2: Ctrl+D for pushing the current line is a Unix-land convention. If you want your program to exhibit that behavior regardless of how it's run or on what system, you'll have to use some portable low level character input library such as ncurses.