4

I'm trying to simulate a command line shell. The user inputs a shell command they want to input, e.g. /bin/pwd and the code is meant to execute it.

The buffer is set to read a fixed number of chars (let's say 20 for example).

In the case that the user inputs more than 20 chars, the excess characters need to be flushed before the shell loops again.

I've been trying to do this like so:

int garbageCollector; while ((garbageCollector = getchar()) != '\n' && garbageCollector != EOF); 

But the problem is getChar() requires you to input a char first.

Is there a way to flush stdin that doesn't require user to input anything?

while (1) { // Prompt user for input ssize_t readIn = read(STDIN_FILENO, buffer, BUF_SIZE + 1); if (readIn < 0) { perror("read() failed"); return 2; } // Null-terminate and remove line return buffer[readIn - 1] = '\0'; char program[strlen(buffer)]; strcpy(program, buffer); printf("program is: %s\n", program); // Flush stdin int garbageCollector; while ((garbageCollector = getchar()) != '\n' && garbageCollector != EOF); // Create child process child = fork(); if (child < 0) { perror("fork() failed"); return 3; } // Start alarm that is triggered after timeout exceeded // which then kills child process signal(SIGALRM, killChild); alarm(timeout); if (child == 0) { // Child char* av[] = { program, NULL }; execve(program, av, NULL); } else { // Parent wait(NULL); alarm(0); // Reset alarm if program executed } memset(buffer, 0, BUF_SIZE); // Flush buffer } 
4
  • 2
    c-faq.com/stdio/stdinflush2.html Commented Jan 21, 2019 at 23:57
  • Note that 20-character command lines are not viable in the medium term — it is just too excruciatingly short. 2000 characters and maybe you'd be in the running. Commented Jan 22, 2019 at 0:33
  • Shells like Bash put the terminal into a non-canonical mode and read using that — not using the standard I/O functions. It's more akin to using the Curses library. Commented Jan 22, 2019 at 0:40
  • Yes the 20 char was just an example. How can I flush the buffer without standard I/O functions? Commented Jan 22, 2019 at 3:55

2 Answers 2

2

If non-POSIX portability is not a concern (note that this will not work with Windows - but I see you're using fork() which also will not work with Windows), you could temporarily make the file descriptor you're attempting to flush non-blocking and read all input from it:

int flush_in(FILE *file) { int ch; int flags; int fd; fd = fileno(file); flags = fcntl(fd, F_GETFL, 0); if (flags < 0) { return -1; } if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) { return -1; } do { ch = fgetc(file); } while (ch != EOF); clearerr(file); if (fcntl(fd, F_SETFL, flags)) { return -1; } return 0; } 

And then you would call flush_in(stdin).

Sign up to request clarification or add additional context in comments.

Comments

1

Someone in a related question suggested this:

fseek(stdin,0,SEEK_END); 

Seems to work on Mac and Windows but not Linux. For Linux, Daniel's suggestion works:

fflush(stdin); 

So, you can solve this at compile time and use either fflush or fseek depending on the OS you're compiling for.

#ifdef defined(_WIN32) || defined(__APPLE__) #define flush_stdin(stdin) fseek(stdin,0,SEEK_END) #elif defined(__linux__) || defined(__unix__) #define flush_stdin(stdin) fflush(stdin) #else #define flush_stdin(...) UNIMPLEMENTED 

7 Comments

Thanks for this. Just a quick aside- if I were to use only system calls, ie no library functions like fflush and fseek, then how could I implement this?
@doctopus I up voted craig's solution because it is better, and it will work on *nix / osx. If you need to do this on windows, use fflush, which does exactly what you want even if you pass stdin. fflush is apparently UB on *nix/osx but fairly widely implemented.
@doctopus Although, I see nothing wrong with my note at the bottom. Calling read until EOF will work anywhere.
when I implement your code at the bottom, it makes me go into an infinite loop where it keeps prompting for user input @okovko
@doctopus Never mind, I don't know what I was thinking. Need some sleep. :P
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.