C| how to check if my input buffer(stdin) is empty? - c

I want to know how to check if my input buffer (perhaps its called stdin) is empty or not.
I dont want the program to stop if the buffer is empty, and I dont want the input to necessarily end with \n, therefore just using scanf is not enough.
I tried searching on google and on this website but no answer was enough.
I tried using feof(stdin) like this:
int main()
{
char c,x;
int num;
scanf("%c",&c);
scanf("%c",&x);
num=feof(stdin);
printf("%d",num);
}
but all it did was printing 0 no matter the input. adding fflush(stdin) after the second scanf gave the same result.
other answers suggested using select and poll but I couldnt find any explanations for those functions.
Some other forum told me to use getchar() but I think they misunderstood my question.
if you suggest I use select/poll, could you please add an explanation about how to use those?

Here is the code for solving this:
fseek (stdin, 0, SEEK_END);
num = ftell (stdin);
fseek will put the pointer at the end of the stdin input buffer. ftell will return the size of file.

If you don't want to block on an empty stdin you should be able to fcntl it to O_NONBLOCK and treat it like any other non-blocking I/O. At that point a call to something like fgetc should return immediately, either with a value or EAGAIN if the stream is empty.

int ch = getc(stdin);
if (ch == EOF)
puts("stdin is empty");
else
ungetc(ch, stdin);
Try this, ungetc(ch, stdin); is added to eliminate the side effect.

You can use select() to handle the blocking issue and the man page select(2) has a decent example that polls stdin. That still doesn't address the problem of needing a line-delimiter ('\n'). This is actually due to the way the terminal handles input.
On Linux you can use termios,
#include <stdio.h>
#include <unistd.h>
#include <termios.h>
// immediate mode getchar().
static int getch_lower_(int block)
{
struct termios tc = {};
int status;
char rdbuf;
// retrieve initial settings.
if (tcgetattr(STDIN_FILENO, &tc) < 0)
perror("tcgetattr()");
// non-canonical mode; no echo.
tc.c_lflag &= ~(ICANON | ECHO);
tc.c_cc[VMIN] = block ? 1 : 0; // bytes until read unblocks.
tc.c_cc[VTIME] = 0; // timeout.
if (tcsetattr(STDIN_FILENO, TCSANOW, &tc) < 0)
perror("tcsetattr()");
// read char.
if ((status = read(STDIN_FILENO, &rdbuf, 1)) < 0)
perror("read()");
// restore initial settings.
tc.c_lflag |= (ICANON | ECHO);
if (tcsetattr(STDIN_FILENO, TCSADRAIN, &tc) < 0)
perror("tcsetattr()");
return (status > 0) ? rdbuf : EOF;
}
int getch(void)
{
return getch_lower_(1);
}
// return EOF if no input available.
int getch_noblock(void)
{
return getch_lower_(0);
}

Related

Understanding read + write in c

char buf[1];
if (argc == 1) {
while (read(STDIN_FILENO, buf, 1) > 0) {
write(1, buf, sizeof(buf));
}
}
I have a few things I'd like to clarify about this snippet. We run it, ./exec_file Let's say we just press Enter. We move to the next line and read 1 byte '\n' then write it to stdout bringing us down one more line... simple enough. Now lets say we type h, then Enter. The program spits out h on the next line with an invisible '\n'.
Looking at the code after we type h it reads it into the buffer then writes it to stdout but somehow the program waits to spit it out on the next line till after I've pressed Enter..how?
Lastly, when we first hit the while loop wouldn't read initially return 0 since we haven't typed anything in initially??
stdin behaves a bit different than most other streams.
First, input is line buffered. That means that input isn't available until you press enter. this explains while the h won't appear until you press enter.
Since it is a stream it doesn't really have an end. Instead of failing when there is no data to read, the call will block until some data is available (or until the program receives a signal). A socket works the same way.
The blocking behaviour can be turned off using fcntl :
int fd = STDIN_FILENO;
int flags = fcntl(fd, F_GETFL, 0);
fcntl(fd, F_SETFL, flags | O_NONBLOCK);
The terminal is by default line buffered, because it is in canonical mode. From Linux manuals tcgetattr(3):
Canonical and noncanonical mode
The setting of the ICANON canon
flag in c_lflag determines whether the terminal is operating in
canonical mode (ICANON set) or noncanonical mode (ICANON unset).
By default, ICANON set.
In canonical mode:
Input is made available line by line. An input line is
available
when one of the line delimiters is typed (NL, EOL, EOL2; or EOF at
the start of line). Except in the case of EOF, the line delimiter
is included in the buffer returned by read(2).
Line editing is enabled (ERASE, KILL; and if the IEXTEN flag is
set:
WERASE, REPRINT, LNEXT). A read(2) returns at most one line of
input; if the read(2) requested fewer bytes than are available in
the current line of input, then only as many bytes as requested are
read, and the remaining characters will be available for a future
read(2).
You can switch off canonical mode on the terminal by calling tcgetattr with proper flags. First of all disable the canonical mode; then set the timeout to 0; set minimum read to 1 for blocking reads or 0 for non-blocking reads. Usually it is customary to also disable local echo, otherwise everything you type would still be automatically visible (and displayed twice in your program):
#include <stdio.h>
#include <unistd.h>
#include <termios.h>
int main() {
struct termios old_settings, new_settings;
int is_terminal;
// check whether the stdin is a terminal to begin with
if (is_terminal = isatty(STDIN_FILENO)) {
// get the old settings
tcgetattr(STDIN_FILENO, &old_settings);
new_settings = old_settings;
// disable canonical mode and echo
new_settings.c_lflag &= (~ICANON & ~ECHO);
// at least one character must be written before read returns
new_settings.c_cc[VMIN] = 1;
// no timeout
new_settings.c_cc[VTIME] = 0;
tcsetattr(STDIN_FILENO, TCSANOW, &new_settings);
}
while (read(STDIN_FILENO, buf, 1) > 0) {
// add this here so that you can verify that it is character by character,
// and not the local echo from the terminal
write(STDOUT_FILENO, ">", 1);
write(STDOUT_FILENO, buf, sizeof(buf));
}
// finally restore the old settings if it was a terminal
if (is_terminal) {
tcsetattr(STDIN_FILENO, TCSANOW, &old_settings);
}
return 0;
}
If you still want the blocking to happen, but want to read character by character, you can use termios to configure how the input will be given to your program. See the code below.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <termios.h>
int main()
{
char buf[1];
struct termios term, term_orig;
if (tcgetattr(0, &term_orig)) {
printf("tcgetattr failed\n");
exit(-1);
}
term = term_orig;
term.c_lflag &= ~ICANON;
term.c_lflag |= ECHO;
term.c_cc[VMIN] = 1;
term.c_cc[VTIME] = 0;
if (tcsetattr(0, TCSANOW, &term)) {
printf("tcsetattr failed\n");
exit(-1);
}
while (read(0, buf, 1) > 0) {
write(1, buf, sizeof(buf));
}
return 0;
}

Getting input from user. Shell

I am writing my own simple shell and currently I'm thinking of getting input ( command ) from user.
I wrote a following prototype:
while(1) {
printf("gsh> ");
fflush(stdout);
total_len = 0;
do {
len = read(0, buffer, MAX_LENGTH_OF_COMMAND-total_len -1);
total_len+= len;
} while( buffer[total_len-1] != '\n');
buffer[total_len]='\0';
parse(buffer);
}
And this soultion seems me to be best, but I am not sure. So, I am asking for correct and recommend/advice me something.
Thanks in advance.
You may rather use getchar() so you can be able to catch keys like up and down arrow (usually useful for shell history) that generate more than one character when you press it. You may also want to make your terminal as raw to get non blocking inputs.
#include <termios.h>
#include <unistd.h>
int main()
{
struct termios oldt;
struct termios newt;
tcgetattr(0, &oldt);
memcpy(&newt, &oldt, sizeof(newt));
cfmakeraw(&newt);
tcsetattr(0, TCSANOW, &newt);
/* your read function ...*/
/* before exiting restore your term */
tcsetattr(0, TCSANOW, &oldt);
}
A good way to create a custom prompt is using read. There is multiple ways so there is always a cleaner / better solution. But here is mine:
while ((fd = read(0, buff, BUFF_SIZE) > 0) {
if (fd == BUFF_SIZE)
// Command to big, handle this as you want to
buff[fd - 1] = '\0';
// Do what you want with your buff
}
Of course this solution has a max buffer size. You would need to wrap the read inside anoter function and use malloc to allocate the good size.

Nonblocking Get Character

Platform: Linux 3.2.0 x86 (Debian 7)
Compiler: GCC 4.7.2 (Debian 4.7.2-5)
I am writing a function that reads a single character from stdin if a character is already present in stdin. If stdin is empty the function is suppose to do nothing and return -1. I googled nonblocking input and was pointed to poll() or select(). First I tried to use select() but I could not get it to work so I tried poll() and reached the same conclusion. I am not sure what these functions do exactly but from what I understand of poll()'s documentation if I call it like so:
struct pollfd pollfds;
pollfds = STDIN_FILENO;
pollfds.events = POLLIN;
poll(pollfds, 1, 0);
if(pollfds.revents & POLLIN) will be true if "Data other than high-priority data may be read without blocking.". But poll() always times out in my test situation. How I test the function could be the problem but the functionality I want is exactly what I am testing for. Here is the function currently and the test situation as well.
#include <poll.h>
#include <stdio.h>
#include <unistd.h>
int ngetc(char *c)
{
struct pollfd pollfds;
pollfds.fd = STDIN_FILENO;
pollfds.events = POLLIN;
poll(&pollfds, 1, 0);
if(pollfds.revents & POLLIN)
{
//Bonus points to the persons that can tell me if
//read() will change the value of '*c' if an error
//occurs during the read
read(STDIN_FILENO, c, 1);
return 0;
}
else return -1;
}
//Test Situation:
//Try to read a character left in stdin by an fgets() call
int main()
{
int ret = 0;
char c = 0;
char str[256];
//Make sure to enter more than 2 characters so that the excess
//is left in stdin by fgets()
fgets(str, 2, stdin);
ret = ngetc(&c);
printf("ret = %i\nc = %c\n", ret, c);
return 0;
}
You're doing IO incorrectly, the POSIX manual and all other related documentation explicitly says never to mix IO done on FILE *s and file descriptors. You have very blatantly broken this rule. This rule is in place because FILE *s use buffering an this means that after a call to fgets there will be nothing left for read to get because fgets already read all pending data into a buffer that is kept in the FILE * structure.
So since there's no way to check if an ISO C IO method will block, we have to use file descriptors only.
Since we know that STDIN_FILENO is just the number 0, we can use
fcntl (0, F_SETFL, O_NONBLOCK);
this will turn all reads on file descriptor 0 to non-blocking mode, if you want to use a different file descriptor so that you can leave 0 alone then just use dup to duplicate it.
This way, you can stay away from poll completely and implement ngetc as
ssize_t
ngetc (char *c)
{
return read (0, c, 1);
}
or better yet, a macro
#define ngetc(c) (read (0, (c), 1))
Thus you get a simple implementation for what you're looking for.
Edit: If you are still worried about the terminal buffering the input, you can always change the terminal's settings, see How to disable line buffering of input in xterm from program? for more information on how to do this.
Edit: The reason that one could not use fgetc instead of read is for the same reason that using fgets won't work. When one of the FILE * IO functions is run, it reads all the data from the associated file descriptor. But once that happens, poll will never return because it's waiting on a file descriptor that's always empty, and the same thing will happen with read. Thus, I suggest that you follow the advice of the documentation and never mix streams (IO using fgets, fgetc, etc.) and file descriptors (IO using read, write, etc.)
There are two problems in your code.
According to manual of poll, assigning 0 to timeout will return immediately
If the value of timeout is 0, poll() shall return immediately. If the value of timeout is -1, poll() shall block until a requested event occurs or until the call is interrupted.
fgets does not do what you expect, it is from stdio library and will buffer reads. Suppose you entered 3 letters and press enter, after fgets, the third letter won't be available to poll.
So comment out the fgets line and assign -1 to timeout in poll, and run it again to see if that's what you want.
I did not get the expected behavior with the answer above, and I actually had to take into account this answer as well
which set the TTY in non canonical mode.
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <termios.h>
int main(int argc, char *argv[])
{
struct termios t;
tcgetattr(0, &t);
t.c_lflag &= ~ICANON;
tcsetattr(0, TCSANOW, &t);
fcntl(0, F_SETFL, fcntl(0, F_GETFL) | O_NONBLOCK);
printf("Starting loop (press i or q)...\n");
for (int i = 0; ; i++) {
char c = 0;
read (0, &c, 1);
switch (c) {
case 'i':
printf("\niteration: %d\n", i);
break;
case 'q':
printf("\n");
exit(0);
}
}
return 0;
}

fseek does not work in linux [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Using fseek with a file pointer that points to stdin
i have a program that use fseek to clear my input buffer, it works well in Windows, buf fails in Linux. Please help me .
#include <stdio.h>
#define NO_USE_FSEEK 0
int main(int argc, char *argv[])
{
char ch = 'a';
int i = 1;
long int fpos = -1;
while(1)
{
printf("loop : %d\n", i);
fseek(stdin, 0L, SEEK_END); /*works in Windows with MinGW, fails in Linux*/
fpos = ftell(stdin);
if (-1 == fpos)
{
perror("ftell failure:"); /*perror tells it is Illegal Seek*/
printf("\n");
}
else
{
printf("positon indicator:%ld\n", fpos);
}
scanf("%c", &ch);
printf("%d : %c\n", (int)ch, ch);
i++;
}
return 0;
}
Thanks in advance!
This is not the accepted way to "clear your input buffer" on either Windows or Linux.
On windows, using the MSVCRT version of the standard C functions, there is an extension allowing fflush(stdin) for this purpose. Note that on other systems this is undefined behavior.
Linux has a function called fpurge with the same purpose.
However, I have to ask, why do you want to clear your input buffer? If it's the usual complaint people have with scanf not reading to the end of the line, it would be better to write code to actually read and discard the rest of the line (loop with getc until reading a '\n', for example, as in pmg's answer). Clearing the input buffer will tend to skip a large amount of data when used on a redirected file or pipe rather than the normal console/tty input.
i guess fseek will not work with stdin. Because the size of stdin is not known.
Test the return value from fseek() (in fact, test the return value from all <stdio.h> input functions).
if (fseek(stdin, 0, SEEK_END) < 0) { perror("fseek"); exit(EXIT_FAILURE); }
Use the idiom
while ((ch = getchar()) != '\n' && ch != EOF) /* void */;
/* if (ch == EOF)
** call feof(stdin) or ferror(stdin) if needed; */
to ignore all characters in the input buffer up to the next ENTER (or end of file or input error).

How to get around no backspace when ICANON in non-canonical

I am using termios as suggested in a previous question I asked but now am asking if there is a way get backspace to work whilst using termios in non-canonical mode. I am using termios to have not have an echo If I use &=ECHO and &=ICANON this is the result I want, the keyboard input is sent to putchar() as soon as the key is press and displayed but the '\b' key is display as hex, if I do the opposite I can't see the text till enter is pressed but '\b' works.
I have looked up the manual and some other forums that and they said " not possible just don't make any mistakes", this would make sense seeing as how when I don't enter my password correctly in in a terminal on Ubuntu I can't backspace and change it. But I was making sure I haven't missed anything in the manual.
Code is to get input from stdin and not display empty lines.
#include <unistd.h>
#include <termios.h>
#include <errno.h>
#include <stdio.h>
#define ECHOFLAGS (ECHO)
int setecho(int fd, int onflag);
int first_line(int *ptrc);
int main(void){
struct termios old;
tcgetattr(STDIN_FILENO,&old);
setecho(STDIN_FILENO,0);
int c;
while((c = getchar())!= 4) //no end of file in non-canionical match to control D
first_line(&c);
tcsetattr(STDIN_FILENO,&old);
return 0;
}
int setecho(int fd, int onflag){
int error;
struct termios term;
if(tcgetattr(fd, &term) == -1)
return -1;
if(onflag){ printf("onflag\n");
term.c_lflag &= ECHOFLAGS ; // I know the onflag is always set to 0 just
term.c_lflag &=ICANON; // testing at this point
}
else{ printf("else\n");
term.c_lflag &= ECHO;
term.c_lflag &=ICANON;
}
while (((error = tcsetattr(fd, TCSAFLUSH, &term)) ==-1 && (errno == EINTR)))
return error;
}
int first_line(int *ptrc){
if (*ptrc != '\n' && *ptrc != '\r'){
putchar(*ptrc);
while (*ptrc != '\n'){
*ptrc = getchar();
putchar(*ptrc);
}
}
else return 0;
return 0;
}
Thanks Lachlan
P.S on a side point in my research I noticed someone saying Termios isn't "Standard C" is this because it is system dependant? (only for comments)
How would you expect this to work? If the input characters are sent to your program immediately, then by the time the backspace character is recieved it's simply too late for the terminal to handle backspace - your program has already seen the previous character, so it can't be taken back.
For example, say the user presses A. Your program will receieve 'A' from getchar() and process it. Now the user presses backspace - now what should the terminal do?
So this implies that the only place you can handle backspace in non-canonical mode is in your program itself. When you receive the '\b' character from getchar(), you can handle it specially (just like you have special handling for '\n') - for example, remove the most recently entered character from a buffer.
It's implementation-dependent. On my machine, pressing backspace led to the byte 127 being read by read(). This code worked on my machine.
#include <unistd.h>
#include <stdio.h>
#include <termios.h>
#include <string.h>
#include <stdlib.h>
#define MAXBUFSIZE (10000U)
#define DEL (127)
int main(void) {
char buf[MAXBUFSIZE];
char c;
size_t top;
struct termios curterm;
tcgetattr(STDIN_FILENO, &curterm);
curterm.c_lflag &= ~(ICANON| ECHO);
curterm.c_cc[VTIME] = 0;
curterm.c_cc[VMIN] = 1;
tcsetattr(STDIN_FILENO, TCSANOW, &curterm);
top = 0;
while (read(STDIN_FILENO, &c, sizeof c) == 1) {
switch (c) {
case DEL:
if (top) {
--top;
const char delbuf[] = "\b \b";
write(STDOUT_FILENO, delbuf, strlen(delbuf));
}
break;
case '\n':
write(STDOUT_FILENO, &c, sizeof c);
write(STDOUT_FILENO, buf, top);
top = 0;
break;
default:
buf[top++] = c;
write(STDOUT_FILENO, &c, sizeof c);
break;
}
}
return 0;
}
When the user presses backspace the application receives some control code. The application has to interpret the control code and take a character out of its buffer (application buffer since you are not using the kernel buffer). If the application is doing any kind of echoing (eg: echoing stars in place of the password characters) then it will have to send some other control codes to move the cursor left and blank out the last star.
Both the codes received for delete or backspace and the codes you send to move the cursor depend on the type of terminal the user has, so before you can do any of this jiggery-pokery you have to detect the terminal type as well. Most programmers don't want to spend time reading the manual for hundreds of different types of terminal, so they generally use a library that hides all of this from them. One such library is curses.

Resources