void turtle (int gtot)
{
int msg;
fcntl(gtot,F_SETFL,O_NONBLOCK);
read(gtot,&msg,4);
gotoxy(12, 21); printf("The value of buffer for turtle is %d",msg);
//react to god's message
xcoor += msg;
msg = 0;
sleep(sleep_time);
}
void god (int gtot )
{
char choice, sign;
int distance;
scanf("%c",&choice);
//choice = getchar();
gotoxy(1,18);
printf("Enter the distance which should be moved");
fflush(stdout);
gotoxy(50, 14);
scanf ("%d", &distance);
int newd = distance;
//printf ("The distance that is to be moved by %d", distance);
if (choice == 'h')
{
write(gtoh,&distance,4);
}
else if (choice == 't')
{
write(gtot,&newd,4);
gotoxy(12,23);
printf("I am writing %d as the number", distance);
fflush(stdout);
}
//printf("You chose %s", &choice);
sleep(sleep_time);
}
main(){
int gtot[2];
pipe (gtot);
pid_turtle = fork ();
if (pid_turtle == 0)
{
close (gtot[1]);
turtle (gtot[0]);
}
pid_god = fork ();
if (pid_god == 0)
{
close (gtot[0]);
god (gtot[1]);
}
}
When I write from the pipe from God function to the Turtle function. I expect it to return nothing when the user gives no input and the numbers when the user gives any. But the printf statement is printing outputs like
The value of buffer for turtle is 0106368
The value of buffer for turtle is 05291328
Which seems to me like the memory address of the number. What is the error that I am making in the program.
Several observations about your program:
In function turtle:
You do not initialize the variable msg.
You set the gtot file descriptor for O_NONBLOCK.
BUT you do not check the return value of read.
This is a significant problem. read is returning immediately and you are printing the uninitialized value of msg.
The way you fork and close is also contributing. You have closed gtot[1] prior to fork-ing the "god" process. If you choose to use this one-parent of two-child-processes approach, don't close the file handles until you are done fork-ing.
Also, it appears that you intended for at least the turtle function, and probably the god function, to loop. As written, your turtle function will immediately exit: it has no loop and it performs read with the O_NONBLOCK flag set.
But wait, there is more. When you do call fcntl to set O_NONBLOCK, what you are doing is also resetting every flag other than O_NONBLOCK. Here is a function taken from the libc documentation which handles the other flags while setting or resetting the non-blocking flag:
/* Set the O_NONBLOCK flag of desc if value is nonzero,
or clear the flag if value is 0.
Return 0 on success, or -1 on error with errno set. */
int
set_nonblock_flag (int desc, int value)
{
int oldflags = fcntl (desc, F_GETFL, 0);
/* If reading the flags failed, return error indication now. */
if (oldflags == -1)
return -1;
/* Set just the flag we want to set. */
if (value != 0)
oldflags |= O_NONBLOCK;
else
oldflags &= ~O_NONBLOCK;
/* Store modified flag word in the descriptor. */
return fcntl (desc, F_SETFL, oldflags);
}
There are some other things which could also be contributing to your problem:
It would be best to replace every occurence of "4" with sizeof(int) wherever you are calling read and write. It is possible that integers are 8 bytes on your machine (64 bit integer?), although pretty unlikely. In the future, it is more likely that int would be 8 bytes, and your code is very fragile in this regard.
There is also something "Strange" about your program that I am observing:
Why is the decimal output from printf being prefixed with a leading 0 (zero)? That would be an octal representation, but the printf specifier is not "%o". "%d" should not show leading zeros unless you use a width-specifier with a leading zero, a la "%08d". So I don't know what to make about that.
you dont want to call fork twice.
You also dont want to close the unused ends of the pipe:)
If this is Linux try using strace to see the exact data being written to the pipes.
If this is Solaris or Mac OS X try dtrace or dtruss.
On BSD I believe the command to view system calls is just truss.
These are very useful commands.
Related
What I intend to do is to get the character entered and used it as a pattern. I've tried using getchar() but it won't work. I've hear of using scanf but it skips and stops whenever I press "shift" for the special characters on my keyboard.
int i, j, n;
char c;
c = getchar();
printf("Enter value of n: ");
scanf("%d", &n);
printf("Enter a Character: ");
getchar();
for(i=1; i<=n; i++)
{
for(j=1; j<=i; j++)
{
printf("%c", c);
}
printf("\n");
}
You need to assign the value returned by getchar to the variable c, and you had a redundent call to getchar that's why it skips reading the desired input:
int i, j, n;
char c;
printf("Enter value of n: ");
scanf("%d", &n);
printf("Enter a Character: ");
scanf(" %c", &c);
for(i=1; i<=n; i++)
{
for(j=1; j<=i; j++)
{
printf("%c", c);
}
printf("\n");
}
You can use %c with scanf:
scanf("%d %c", &n, %c);
This eliminates the need for the two getchar calls.
The space is required; it tells scanf to skip whitespace.
The problem you have is that your assumptions on getchar(3) are incorrect. You think getchar() is going to return the next key pressed in the input stream, but you are incorrectly assuming that it will be done without buffering or system processing (the terminal driver gives the program complete lines, or even worse, if you are reading from a file, complete buffer blocks, that have to be buffered so you miss no characters from the input stream)
You are assuming incorrectly that the end of line you need to press for the input to be feeded to the program does not count in the input stream.
What actually happens is:
you feed a complete line (because the kernel driver works that way) so you press your character, and then you see nothing, not after you have pressed the return key.
once you press it, you have more than one character (depending on how many you pressed before hitting the return key) that will stay in the buffer, until they are so consumed by the program. Normally this happens when you have executed more getchar() or scanf() statements.
The idea of this buffering mechanism is to allow a programmer to process character by charcacter large amounts of text, without the overhead of making a system call per character reading (this is a costly operation) so think of getchar() not as a sample function to get new users introduced to the world of programming, but as a hint to experienced programmers to use efficiently without having to think on buffering large amounts of text.
With stdio package, every character counts, so you have to think slowly and minuciously when you feed input to getchar(3).
The next question is: Right, then how can I solve and stop my program until I press some key? The first answer, with the set of tools you have exposed here is, be careful on what you input, instead of asking for any key, ask the user to press the return key, and then, do something like:
printf("Hit <ENTER> to continue"); fflush(stdout); /* so we get the line out, bypassing the buffering mechanism */
int c;
while ((c = getchar()) != EOF && c != '\n') {
/* just ignore the character we have received */
}
/* c == '\n' || c == EOF, so we can continue */
or, if you prefer, you can write a function just to do this (as there can be so many criteria to implement it, nobody included such a function in the standard C library, my apologies for that. ;) )
void wait_for_enter()
{
/* I use stderr, for two reasons:
* o stderr is normally unbuffered, so there's no need to fflush()
* o stdout can be redirected, so the prompt will not be visible in
* case you want to save the output of your program.
*/
fprintf(stderr, "Hit <ENTER> to continue");
int c;
while ((c = getchar()) != EOF && c != '\n') {
/* just ignore the character we have received
* until we get the end of file (ctrl-d at the terminal)
* or a new line */
}
/* c == '\n' || c == EOF, so we can continue */
/* it's assumed that the user pressed the enter key, so the echoed
* enter already did a newline, no need to do it here */
} /* wait_for_enter */
In order to wait for any character and in raw mode, you need first to ensure your input comes from a terminal (you cannot do the following on a normal file), then you have to switch the terminal driver to raw mode, so each character is given immediately to the program and no line editing processing is done, and then set the stdin descriptor to no buffering at all. Only then, you can receive individual characters with getchar(3), one by one, as they are keyed in. I think this is far out of the scope of this question, as the code to do that is far more complex than the above.
EDIT
Following is a complete sample of a program that uses raw input to process characters as they are keyed in.
/* pru.c -- program to show raw input from the terminal.
* Author: Luis Colorado <luiscoloradourcola#gmail.com>
* Date: Fri Sep 20 08:46:06 EEST 2019
* Copyright: (C) 2019 Luis Colorado. All rights reserved.
* License: BSD.
*/
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h> /* see termios(3) for a description on terminal conf */
#define F(_fmt) __FILE__":%d:%s: " _fmt, __LINE__, __func__
/* this function switches the terminal into raw mode and returns a malloc(3)ed
* terminal configuration, so it can be later restored. BEWARE that the returned
* configuration info must be deallocated by free(3) once it's not needed anymore.
* In case of failure of any system call, the function returns NULL, and errno is
* set to the failing cause. */
struct termios *set_raw(int fd)
{
struct termios *ret = malloc(sizeof *ret), cfg;
if (!ret) return NULL;
int res = tcgetattr(fd, &cfg);
if (res < 0) goto error;
*ret = cfg; /* save it for return */
cfmakeraw(&cfg);
/* set it after all buffered characters in the driver have drained out */
res = tcsetattr(fd, TCSADRAIN, &cfg);
if (res < 0) goto error;
return ret;
error:
free(ret);
return NULL;
} /* set_raw */
/* restores the configuration back to the associated file descriptor */
int restore_cfg(int fd, struct termios *cf)
{
/* set it after all buffered characters in the driver have drained out */
return tcsetattr(fd, TCSADRAIN, cf);
} /* restore_cfg */
int main()
{
struct termios *cfg = set_raw(fileno(stdin));
if (!cfg) {
fprintf(stderr, F("stdin: %s\n"),
strerror(errno));
}
setbuf(stdin, NULL); /* stdin unbuffered */
setbuf(stdout, NULL); /* stdout unbuffered */
/* BEWARE that raw mode doesn't process any characters, so no Ctrl-C(interrupt), Ctrl-D(EOF), etc.
* will be available, only if you read from a file, you'll get EOF, but you'll not be able to produce
* that on the terminal, you'll need to swith to another console and kill the process. */
int c;
while ((c = getchar()) != EOF && c != '\033') { /* ESCAPE key('\033') is a safeguard to end input */
/* print the input char as an hex number */
printf("[%02x]", c);
}
if (cfg) { /* if we were able to set the terminal to raw mode */
/* restore config */
restore_cfg(fileno(stdin), cfg);
/* and free it */
free(cfg);
}
exit(EXIT_SUCCESS);
} /* main */
The full source code can be also downloaded from here.
You can use this program to see how input keys get mapped into characters, as you'll note that when you press the enter key, the raw input is [0d] (ascii char 13, CARRY RETURN) while in normal line mode you get '\n' which is [0a] or ASCII LINE FEED, instead (you can check this if you redirect input from the pru.c text file). Also you'll see that you are unable to specify EOF from the terminal driver with Ctrl-D and that Ctrl-C does not come to help. Well, I have included a safeguard, by ending the program in case you press the ESC key, which generates an ASCII ESCAPE character (\033). This is also commented in the source code.
All of this processing is done by the kernel driver, so all unix implementations get the same line end characters or interpret the control characters the same way.
I have got a text based game in c that uses scanf.
There are a few times when the player is supposed to type in things, however, while he isn't, the cursor stays in the game, letting the user type in anything he wants, which ruins future scanfs and the story.
Is there a way to disallow input unless there is a scanf waiting for a response?
I think it would be helpful to step back and think about all the moving parts that exist in the execution environment of your program.
When executed, your program becomes a distinct process running in the multitasking environment of the OS. The terminal is a separate process with an associated GUI window, and which may be running locally or remotely (e.g. someone could theoretically run your game from a remote location by connecting over a network via ssh). The user interacts with the terminal program through their keyboard and screen.
Now, it is actually the terminal process (working closely with the OS kernel) that is responsible for most of the nuances of user input. It is the terminal that prints just-typed characters to its GUI window as soon as it receives them, and it is the terminal that maintains an input buffer of characters that have been typed but that have not yet been read by a foreground process.
Conveniently, terminals allow their behavior to be controlled by a set of configuration settings, and these settings can be changed programmatically during the run-time of the connected program. The C-level API that we can use to read and write these settings is called termios.
There's a great article on terminals I highly recommend: The TTY demystified. For the purposes of this question, the section Configuring the TTY device is most useful. It doesn't demonstrate the termios library directly, but shows how to use the stty utility which uses the termios library internally.
(Note that, although the links I've been giving so far are focused on Linux, they are applicable to all Unix-like systems, which includes Mac OS X.)
Unfortunately there's no way to completely "disallow" input with a single switch, but we can achieve the same effect by toggling a couple of terminal settings and manually discarding buffered input at the right times.
The two terminal settings we need to concern ourselves with are ECHO and ICANON. Both settings are normally on by default.
By turning off ECHO, we can prevent the terminal from printing just-typed characters to the terminal window when it receives them. Hence, while the program is running, any characters the user types will seem to be ignored completely, although they will still be buffered internally by the terminal.
By turning off ICANON, we ensure that the terminal will not wait for an enter keypress to submit a complete line of input before returning input to the program, e.g. when the program makes a read() call. Rather, it will return whatever characters it currently has buffered in its internal input buffer, thereby making it possible for us to discard them immediately and carry on with execution.
The full process will look like this:
1: Disable input, meaning turn off ECHO and ICANON.
2: Run some gameplay with output, not requiring any user input.
3: Enable input, meaning discard any buffered terminal input and then turn on ECHO and ICANON.
4: Read user input.
5: Repeat from step 1. Subsequent gameplay can now make use of the latest user input.
There is a complication in step 3 related to discarding buffered input. We can implement this discarding operation by simply reading input from stdin via read() with a fixed-length buffer until there's no more input to be read. But if there's no input ready to be read at all for the discarding operation, then the first call would block until the user types something. We need to prevent this blocking.
I believe there are two ways this could be done. There's such a thing called a non-blocking read, which can be set up with termios or fcntl() (or by opening a second file descriptor to the same endpoint with the O_NONBLOCK flag, I think) which would cause read() to return immediately with errno set to EAGAIN if it would block. The second way is to poll the file descriptor with poll() or select() to determine if there's data ready to be read; if not, we can avoid the read() call completely.
Here's a working solution that uses select() to avoid blocking:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <termios.h>
struct termios g_terminalSettings; // global to track and change terminal settings
void disableInput(void);
void enableInput(void);
void discardInputBuffer(void);
void discardInputLine(void);
void setTermiosBit(int fd, tcflag_t bit, int onElseOff );
void turnEchoOff(void);
void turnEchoOn(void);
void turnCanonOff(void);
void turnCanonOn(void);
int main(void) {
// prevent input immediately
disableInput();
printf("welcome to the game\n");
// infinite game loop
int line = 1;
int quit = 0;
while (1) {
// print dialogue
for (int i = 0; i < 3; ++i) {
printf("line of dialogue %d\n",line++);
sleep(1);
} // end for
// input loop
enableInput();
int input;
while (1) {
printf("choose a number in 1:3 (-1 to quit)\n");
int ret = scanf("%d",&input);
discardInputLine(); // clear any trailing garbage (can do this immediately for all cases)
if (ret == EOF) {
if (ferror(stdin)) { fprintf(stderr, "[error] scanf() failed: %s", strerror(errno) ); exit(1); }
printf("end of input\n");
quit = 1;
break;
} else if (ret == 0) { // invalid syntax
printf("invalid input\n");
} else if (input == -1) { // quit code
quit = 1;
break;
} else if (!(input >= 1 && input <= 3)) { // invalid value
printf("number is out-of-range\n");
} else { // valid
printf("you entered %d\n",input);
break;
} // end if
} // end while
if (quit) break;
disableInput();
} // end while
printf("goodbye\n");
return 0;
} // end main()
void disableInput(void) {
turnEchoOff(); // so the terminal won't display all the crap the user decides to type during gameplay
turnCanonOff(); // so the terminal will return crap characters immediately, so we can clear them later without waiting for a LF
} // end disableInput()
void enableInput(void) {
discardInputBuffer(); // clear all crap characters before enabling input
turnCanonOn(); // so the user can type and edit a full line of input before submitting it
turnEchoOn(); // so the user can see what he's doing as he's typing
} // end enableInput()
void turnEchoOff(void) { setTermiosBit(0,ECHO,0); }
void turnEchoOn(void) { setTermiosBit(0,ECHO,1); }
void turnCanonOff(void) { setTermiosBit(0,ICANON,0); }
void turnCanonOn(void) { setTermiosBit(0,ICANON,1); }
void setTermiosBit(int fd, tcflag_t bit, int onElseOff ) {
static int first = 1;
if (first) {
first = 0;
tcgetattr(fd,&g_terminalSettings);
} // end if
if (onElseOff)
g_terminalSettings.c_lflag |= bit;
else
g_terminalSettings.c_lflag &= ~bit;
tcsetattr(fd,TCSANOW,&g_terminalSettings);
} // end setTermiosBit()
void discardInputBuffer(void) {
struct timeval tv;
fd_set rfds;
while (1) {
// poll stdin to see if there's anything on it
FD_ZERO(&rfds);
FD_SET(0,&rfds);
tv.tv_sec = 0;
tv.tv_usec = 0;
if (select(1,&rfds,0,0,&tv) == -1) { fprintf(stderr, "[error] select() failed: %s", strerror(errno) ); exit(1); }
if (!FD_ISSET(0,&rfds)) break; // can break if the input buffer is clean
// select() doesn't tell us how many characters are ready to be read; just grab a big chunk of whatever is there
char buf[500];
ssize_t numRead = read(0,buf,500);
if (numRead == -1) { fprintf(stderr, "[error] read() failed: %s", strerror(errno) ); exit(1); }
printf("[debug] cleared %d chars\n",numRead);
} // end while
} // end discardInputBuffer()
void discardInputLine(void) {
// assumes the input line has already been submitted and is sitting in the input buffer
int c;
while ((c = getchar()) != EOF && c != '\n');
} // end discardInputLine()
I should clarify that the discardInputLine() feature I included is completely separate from the discarding of the input buffer, which is implemented in discardInputBuffer() and called by enableInput(). Discarding of the input buffer is an essential step in the solution of temporarily disallowing user input, while discarding the remainder of the input line that is left unread by scanf() is not exactly essential. But I think it does make sense to prevent residual line input from being scanned on subsequent iterations of the input loop. It's also necessary to prevent infinite loops if the user entered invalid input, so for that reason we can probably call it essential.
Here's a demo of me playing around with the input:
welcome to the game
line of dialogue 1
line of dialogue 2
line of dialogue 3
[debug] cleared 12 chars
choose a number in 1:3 (-1 to quit)
0
number is out-of-range
choose a number in 1:3 (-1 to quit)
4
number is out-of-range
choose a number in 1:3 (-1 to quit)
asdf
invalid input
choose a number in 1:3 (-1 to quit)
asdf 1 2 3
invalid input
choose a number in 1:3 (-1 to quit)
0 1
number is out-of-range
choose a number in 1:3 (-1 to quit)
1 4
you entered 1
line of dialogue 4
line of dialogue 5
line of dialogue 6
choose a number in 1:3 (-1 to quit)
2
you entered 2
line of dialogue 7
line of dialogue 8
line of dialogue 9
[debug] cleared 256 chars
[debug] cleared 256 chars
[debug] cleared 256 chars
[debug] cleared 256 chars
[debug] cleared 256 chars
[debug] cleared 256 chars
[debug] cleared 256 chars
[debug] cleared 238 chars
choose a number in 1:3 (-1 to quit)
-1
goodbye
During the first triplet of dialogue I typed 12 random characters which were discarded afterward. Then I demonstrated various types of invalid input and how the program responds to them. During the second triplet of dialogue I didn't type anything, so no characters were discarded. During the final triplet of dialogue I quickly pasted a large block of text into my terminal several times (using a mouse right-click, which is a quick and easy shortcut for pasting into my particular terminal), and you can see it discarded all of it properly, taking several iterations of the select()/read() loop to complete.
On Linux and HP-UX machines, use
to disable display of inputs from keyboard on terminal
stty -echo
to enable display of inputs from keyboard on terminal
stty echo
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);
}
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;
}
I have got a small program that prints the contents of files using the system call - read.
unsigned char buffer[8];
size_t offset=0;
size_t bytes_read;
int i;
int fd = open(argv[1], O_RDONLY);
do{
bytes_read = read(fd, buffer, sizeof(buffer));
printf("0x%06x : ", offset);
for(i=0; i<bytes_read; ++i)
{
printf("%c ", buffer[i]);
}
printf("\n");
offset = offset + bytes_read;
}while(bytes_read == sizeof(buffer));
Now while running I give a file name that doesn't exist.
It prints some kind of data mixed with environment variables and a segmentation fault at the end.
How is this possible? What is the program printing?
Thanks,
John
It's printing rubbish because fd will invariably be set to -1 which is not a good thing to pass to read since it will, in turn do nothing other than return -1 as well. It will leave your buffer untouched meaning that it's holding whatever rubbish you had in there when you started.
You could probably put the entire do loop inside something like:
if (fd == -1) {
printf ("error here");
} else {
// do loop here
}
read is returning -1 because fd is invalid, you store that in bytes_read which is of type size_t which is unsigned, so your loop prints (size_t)-1 chars, which is a very large number, much larger than the size of buffer. So, you're printing a big chunk of your address space and then getting a segfault when you eventually reach the end and access an invalid address.
As others have mentioned (without answering your actual question), you should be checking the results of open for an error. e.g.,
int fd = open(argv[1], O_RDONLY);
if( fd < 0 ){
fprintf(stderr, "error opening %s: %s\n", argv[1], strerror(errno));
exit(1);
}
A caveat: if you do another system call, or call any routine that might do a system call (e.g., printf) before calling strerror, you must save errno and then pass the saved copy to strerror.
Another note about your program:
while(bytes_read == sizeof(buffer))
This is not a good test, because read can return less than the amount you ask for. Your loop should continue until read returns <= 0.
You should probably check that the file descriptor returned by open is valid before using it. As per these docs, you should get a non-negative response for a valid file. Reading from an invalid descriptor is likely the source of your problem.
Upon successful completion, open function shall open the file and return a non-negative integer representing the file descriptor. Otherwise, -1 shall be returned and errno set to indicate the error. So please check fd before entering the loop to perform the read.