I'm running a console using fgets() in a microcontroller. If I leave the console idle for too much time while fgets() is prompting for commands the watchdog timer would get triggered.
I wonder if it is possible to set a time limit on fgets() so that if the user doesn't provide commands after certain amount of time, fgets() expires?
Note
This answer might be completely useless if you are writing microcontroller code using something like HI-TECH C or Keil C51. In those cases, you will have to use some platform dependent solution.
There are a lot of things wrong with this example, but it shows how to interrupt fgets:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <setjmp.h>
sigjmp_buf context;
volatile int alarm_occurred = 0;
void alarm_handler(int signum) {
alarm_occurred = 1;
siglongjmp(context, -1);
}
int main() {
char buffer[80];
signal(SIGALRM, alarm_handler);
while (1) {
char *result;
if (sigsetjmp(context, 1) == 0) {
// The call to sigsetjump will cause flow to go here
alarm(3);
printf("Enter a string: ");
result = fgets(buffer, sizeof(buffer), stdin);
}
else {
// The call to siglongjump will cause flow to go here
printf("\n");
continue;
}
}
return 0;
}
Output
Enter a string:
Enter a string:
...
Enter a string:
I've never used sigsetjmp or siglongjmp before, and I know better than to do anything other than set a flag in a signal handler, but hopefully the people who do know how to use these properly can edit the answer to fix the problems.
Related
Good day,
I'm writing my own shell in C for my school which has to resemble bash as closely as possible.
I have to handle signals such as Ctrl-\ and Ctrl-C as bash does; for this reason I'm allowed to use signal function. It works fine, but the thing is whenever a Ctrl-C signal is caught (starting from the second catch), a ^C is printed.
On the net, I've found a workaround suggesting printing "\b \b\b \b\nminishell$ " whenever a Ctrl-C is caught, which will devour the two symbols. The thing is, since at the very first time ^C is not printed, the print devours two symbols of my prompting, making it just minishell instead of minishell$ , with the cursor incorrectly displayed.
Now I've come up with another workaround for this workaround which is to declare a static boolean to not print the baskspaces at the very first call. This doesn't help in case of Ctrl-\ though; Ctrl-\ proceeds to move my cursor to right when I attempt to write the two whitespaces that must replace the ^\.
I don't like these workarounds and would like to know whether there is a way to instruct the terminal not to output this stuff? I'm allowed to use tgetent, tgetflag, tgetnum, tgetstr, tgoto, tputs, tcsetattr, tcgetattr, have read their man pages but nothing seems to be helpful.
When you type a key on a terminal, two things happen
the character is echoed (displayed) on this terminal
the character is sent (over the line) to the attached program
Both these actions can be controlled via termios/tcsetattr(): a different character(s) can be sent or echoed, some can be suppressed, etc. (some/most of these actions take place in the terminal-driver , but this is not relevant here)
Demonstration: using tcsetattr() to control the echoing of the terminal:
#include <stdio.h>
#include <stdlib.h>
#define _SVID_SOURCE 1
#include <termios.h>
#include <unistd.h>
#include <signal.h>
struct termios termios_save;
void reset_the_terminal(void)
{
tcsetattr(0, 0, &termios_save );
}
sig_atomic_t the_flag = 0;
void handle_the_stuff(int num)
{
char buff[4];
buff[0] = '[';
buff[2] = '0' + num%10;
num /= 10;
buff[1] = '0' + num%10;
buff[3] = ']';
write(0, buff, sizeof buff);
the_flag = 1;
}
int main (void)
{
int rc;
int ch;
struct termios termios_new;
rc = tcgetattr(0, &termios_save );
if (rc) {perror("tcgetattr"); exit(1); }
rc = atexit(reset_the_terminal);
if (rc) {perror("atexit"); exit(1); }
termios_new = termios_save;
termios_new.c_lflag &= ~ECHOCTL;
rc = tcsetattr(0, 0, &termios_new );
if (rc) {perror("tcsetattr"); exit(1); }
signal(SIGINT, handle_the_stuff);
printf("(pseudoshell)Start typing:\n" );
while(1) {
ch = getc(stdin);
if (the_flag) {
printf("Saw the signal, last character was %02x\n", (unsigned) ch);
break;
}
}
exit (0);
}
The way to set the console such a SW may intercept all typed chars is to set the terminal in RAW MODE. The problems this way may present are that all keys that aren't in the ASCII 0-255 space, such as è, ì, à will be received from the console as a bytes sequence and all the function and control keys included cursors and backspace will not accomplish any action, some code such as CR, LF and some ANSI sequence may accomplish actions when are read from the input channel and rewritten on the output channel.
To set the terminal in raw mode you have to use the function cfmakeraw followed by the function tcsetattr.
The code below implements a simple but not very good implemented terminal, anyway I think this code is a good point to start. In any case, the code flow and the error control must be at least better arranged.
The code writes all sequence of ASCII char that enter into the console when a key is typed. All chars that have value smaller then 32 or greater then 126 will be written as [HEX-CODE]
I.E. hitting Esc on the console will be written [1B], the code of Ctrl+C will be written as [03], F1 will be [1B]OP, F11 will be [1B][23~, Enter will be [0D].
If you will hit Ctrl+X [18] will be written and the program stops, but this behaviour is under SW control as you can see in the code.
Here the code:
#include <stdio.h> // Standard input/output definitions
#include <string.h> // String function definitions
#include <unistd.h> // UNIX standard function definitions
#include <fcntl.h> // File control definitions
#include <errno.h> // Error number definitions
#include <termios.h> // POSIX terminal control definitions (struct termios)
#include <sys/ioctl.h> // Used for TCGETS2, which is required for custom baud rates
#include <sys/select.h> // might be used to manage select
int setAttr(int ch, int resetToOld);
#define IN 0
#define OUT 1
typedef struct TermCap
{
int fd;
struct termios oldTermios;
struct termios newTermios;
// fd_set fds; // might be used to manage select
} TermCap;
TermCap m_termCap[2];
int main()
{
int i,ex=0;
char msg;
char buff[20];
m_termCap[IN].fd=STDIN_FILENO;
m_termCap[OUT].fd=STDOUT_FILENO;
// Gets STDIN config and set raw config
setAttr(IN,0);
// Gets STDOUT config and set raw config
setAttr(OUT,0);
// Console loop ... the console terminates when ^X is intercepted.
do {
do {
i=read(m_termCap[IN].fd,&msg,1);
if (i>0){
if (msg<32 || msg>126) {
sprintf(buff,"[%02X]",(unsigned char)msg);
write(m_termCap[OUT].fd,buff,4);
if (msg==24)
ex=1;
}else{
write(m_termCap[OUT].fd,&msg,i);
}
}
usleep(10000); // a minimal delay of 10 millisec
} while(i>0 && !ex);
} while(!ex);
// Reset console to initial state.
setAttr(IN,1);
setAttr(OUT,1);
printf("\r\n\nThe end!");
return 0;
}
int setAttr(int ch, int resetToOld)
{
int retVal=0;
int i;
if (!resetToOld) {
// Read old term config
i=tcgetattr(m_termCap[ch].fd, &m_termCap[ch].oldTermios);
if (i==-1) {
return 1;
}
}
m_termCap[ch].newTermios = m_termCap[ch].oldTermios;
if (!resetToOld) {
// Terminal in raw mode
cfmakeraw(&m_termCap[ch].newTermios);
}
i=tcsetattr(m_termCap[ch].fd, TCSANOW, &m_termCap[ch].newTermios);
if (i==-1) {
retVal = 2;
}
return retVal;
}
Wouldn't this work?
void signalHandler(int signo){
if(signo==SIGINT){
printf("\b\b \b\b");
fflush(NULL);
printf("\nHello World\n");
}
}
In my shell it seems to work fine. The first printf and fflush is what you have to implement in your handler. The printf after that is just a way for me to show you that you can, then, do whatever you want after the ^C not appearing.
Why does this make it not appear? In the first printf I erase the characters by using backspaces and spaces. As stdout is buffered by default and I didn't want to use a newline character, I flushed the buffer manually.
Good day,
I'm writing my own shell in C for my school which has to resemble bash as closely as possible.
I have to handle signals such as Ctrl-\ and Ctrl-C as bash does; for this reason I'm allowed to use signal function. It works fine, but the thing is whenever a Ctrl-C signal is caught (starting from the second catch), a ^C is printed.
On the net, I've found a workaround suggesting printing "\b \b\b \b\nminishell$ " whenever a Ctrl-C is caught, which will devour the two symbols. The thing is, since at the very first time ^C is not printed, the print devours two symbols of my prompting, making it just minishell instead of minishell$ , with the cursor incorrectly displayed.
Now I've come up with another workaround for this workaround which is to declare a static boolean to not print the baskspaces at the very first call. This doesn't help in case of Ctrl-\ though; Ctrl-\ proceeds to move my cursor to right when I attempt to write the two whitespaces that must replace the ^\.
I don't like these workarounds and would like to know whether there is a way to instruct the terminal not to output this stuff? I'm allowed to use tgetent, tgetflag, tgetnum, tgetstr, tgoto, tputs, tcsetattr, tcgetattr, have read their man pages but nothing seems to be helpful.
When you type a key on a terminal, two things happen
the character is echoed (displayed) on this terminal
the character is sent (over the line) to the attached program
Both these actions can be controlled via termios/tcsetattr(): a different character(s) can be sent or echoed, some can be suppressed, etc. (some/most of these actions take place in the terminal-driver , but this is not relevant here)
Demonstration: using tcsetattr() to control the echoing of the terminal:
#include <stdio.h>
#include <stdlib.h>
#define _SVID_SOURCE 1
#include <termios.h>
#include <unistd.h>
#include <signal.h>
struct termios termios_save;
void reset_the_terminal(void)
{
tcsetattr(0, 0, &termios_save );
}
sig_atomic_t the_flag = 0;
void handle_the_stuff(int num)
{
char buff[4];
buff[0] = '[';
buff[2] = '0' + num%10;
num /= 10;
buff[1] = '0' + num%10;
buff[3] = ']';
write(0, buff, sizeof buff);
the_flag = 1;
}
int main (void)
{
int rc;
int ch;
struct termios termios_new;
rc = tcgetattr(0, &termios_save );
if (rc) {perror("tcgetattr"); exit(1); }
rc = atexit(reset_the_terminal);
if (rc) {perror("atexit"); exit(1); }
termios_new = termios_save;
termios_new.c_lflag &= ~ECHOCTL;
rc = tcsetattr(0, 0, &termios_new );
if (rc) {perror("tcsetattr"); exit(1); }
signal(SIGINT, handle_the_stuff);
printf("(pseudoshell)Start typing:\n" );
while(1) {
ch = getc(stdin);
if (the_flag) {
printf("Saw the signal, last character was %02x\n", (unsigned) ch);
break;
}
}
exit (0);
}
The way to set the console such a SW may intercept all typed chars is to set the terminal in RAW MODE. The problems this way may present are that all keys that aren't in the ASCII 0-255 space, such as è, ì, à will be received from the console as a bytes sequence and all the function and control keys included cursors and backspace will not accomplish any action, some code such as CR, LF and some ANSI sequence may accomplish actions when are read from the input channel and rewritten on the output channel.
To set the terminal in raw mode you have to use the function cfmakeraw followed by the function tcsetattr.
The code below implements a simple but not very good implemented terminal, anyway I think this code is a good point to start. In any case, the code flow and the error control must be at least better arranged.
The code writes all sequence of ASCII char that enter into the console when a key is typed. All chars that have value smaller then 32 or greater then 126 will be written as [HEX-CODE]
I.E. hitting Esc on the console will be written [1B], the code of Ctrl+C will be written as [03], F1 will be [1B]OP, F11 will be [1B][23~, Enter will be [0D].
If you will hit Ctrl+X [18] will be written and the program stops, but this behaviour is under SW control as you can see in the code.
Here the code:
#include <stdio.h> // Standard input/output definitions
#include <string.h> // String function definitions
#include <unistd.h> // UNIX standard function definitions
#include <fcntl.h> // File control definitions
#include <errno.h> // Error number definitions
#include <termios.h> // POSIX terminal control definitions (struct termios)
#include <sys/ioctl.h> // Used for TCGETS2, which is required for custom baud rates
#include <sys/select.h> // might be used to manage select
int setAttr(int ch, int resetToOld);
#define IN 0
#define OUT 1
typedef struct TermCap
{
int fd;
struct termios oldTermios;
struct termios newTermios;
// fd_set fds; // might be used to manage select
} TermCap;
TermCap m_termCap[2];
int main()
{
int i,ex=0;
char msg;
char buff[20];
m_termCap[IN].fd=STDIN_FILENO;
m_termCap[OUT].fd=STDOUT_FILENO;
// Gets STDIN config and set raw config
setAttr(IN,0);
// Gets STDOUT config and set raw config
setAttr(OUT,0);
// Console loop ... the console terminates when ^X is intercepted.
do {
do {
i=read(m_termCap[IN].fd,&msg,1);
if (i>0){
if (msg<32 || msg>126) {
sprintf(buff,"[%02X]",(unsigned char)msg);
write(m_termCap[OUT].fd,buff,4);
if (msg==24)
ex=1;
}else{
write(m_termCap[OUT].fd,&msg,i);
}
}
usleep(10000); // a minimal delay of 10 millisec
} while(i>0 && !ex);
} while(!ex);
// Reset console to initial state.
setAttr(IN,1);
setAttr(OUT,1);
printf("\r\n\nThe end!");
return 0;
}
int setAttr(int ch, int resetToOld)
{
int retVal=0;
int i;
if (!resetToOld) {
// Read old term config
i=tcgetattr(m_termCap[ch].fd, &m_termCap[ch].oldTermios);
if (i==-1) {
return 1;
}
}
m_termCap[ch].newTermios = m_termCap[ch].oldTermios;
if (!resetToOld) {
// Terminal in raw mode
cfmakeraw(&m_termCap[ch].newTermios);
}
i=tcsetattr(m_termCap[ch].fd, TCSANOW, &m_termCap[ch].newTermios);
if (i==-1) {
retVal = 2;
}
return retVal;
}
Wouldn't this work?
void signalHandler(int signo){
if(signo==SIGINT){
printf("\b\b \b\b");
fflush(NULL);
printf("\nHello World\n");
}
}
In my shell it seems to work fine. The first printf and fflush is what you have to implement in your handler. The printf after that is just a way for me to show you that you can, then, do whatever you want after the ^C not appearing.
Why does this make it not appear? In the first printf I erase the characters by using backspaces and spaces. As stdout is buffered by default and I didn't want to use a newline character, I flushed the buffer manually.
I have problems writing to file, this is the code:
#include <stdio.h>
int main(int argc,char* argv[])
{
if(argc!=2)
{
printf("\x1B[31mError::%s takes exactly one argument!\n\x1B[0m",argv[0]);
return 1;
}
char string[100];
FILE* file=fopen(argv[1],"w");
if(file==NULL)
{
printf("\x1B[31mFile is invalid!\x1B[0m\n");
return 1;
}
while(!feof(stdin))
{
scanf("%s",string);
fprintf(file,"%s\n",string);
}
fclose(file);
return 0;
}
it should take input with scanf and writing to the file until i enter the end of file character (ctrl+Z), when it's finished running,the file i opened is empty. This code structure is also suggested in the deitel & deitel book, Do you know what's wrong here?
Also, i would like to know how can i take a whole phrase with scanf instead of a single word each time.. if i do scanf("%[^\n]",string)
the program gets confused and as soon as i write something, it will go in a loop writing the same thing over and over, and the file will become like 1,7Gb large.. help!
when user will press ctrl+z your process(a.out) will receive signal no 20 internally and your process will be stopped but data will not be saved because program got terminated abnormally.
you can type man 7 signal on terminal and check the default action of ctrl+z. So now you need to modify ctrl+z by-default action ? how ? use signal() or sigaction().
whenever ctrl+z is pressed, jumps to isr() and there save the current process status using exit() system call. ctrl+z is signal no 20.
I did small modifications in your code.
#include <unistd.h>
#include <stdio.h>
#include <signal.h>
void isr(int n)
{
if(n == 20)//if ctrl+z is pressed then condition will be true
{
printf("in isr :\n");
exit(0);//exit(0) is normal termination of process i.e it will save current process context
}
}
int main(int argc,char* argv[])
{
if(argc!=2)
{
printf("\x1B[31mError::%s takes exactly one argument!\n\x1B[0m",argv[0]);
return 1;
}
char string[100];
FILE* file=fopen(argv[1],"w");
if(file==NULL)
{
printf("\x1B[31mFile is invalid!\x1B[0m\n");
return 1;
}
while(!feof(stdin))
{
signal(20,isr);//when user presses ctrl+z it will jump to isr()
scanf("%s",string);
fprintf(file,"%s\n",string);
}
fclose(file);
return 0;
}
once go through man pages of signal() and exit().
I am basically a beginner C++ programmer...and it's my first attempt to code in C.
I am trying to program a snake game (using system ("cls")).
In this program I need to get a character as an input (basically to let the user change the direction of movement of snake)... and if the use doesn't input any character within half a second then this character input command needs to be aborted and my remaining code should get executed.
Please give suggestions to sort out this problem.
EDIT: Thanks for the suggestions, but
My main motive of asking this question was to find a method to abort the getchar command even if the user has not entered anything....Any suggestions on this? And by the way my platform is windows
The best way, in my opinion, is using libncurses.
http://tldp.org/HOWTO/NCURSES-Programming-HOWTO/
You have all the tools to make a snake easily.
If you think it's too easy (it is a relatively high level library), look at the termcaps library.
EDIT: So, a non-blocking read with termcaps is :
#include <termios.h>
#include <unistd.h>
#include <term.h>
uintmax_t getchar()
{
uintmax_t key = 0;
read(0, &key, sizeof(key));
return key;
}
int main(int ac, char **av, char **env)
{
char *name_term;
struct termios term;
if ((name_term = getenv("TERM")) == NULL) // looking for name of term
return (-1);
if (tgetent(NULL, &name_term) == ERR) // get possibilities of term
return (-1);
term.c_lflag &= ~(ICANON | ECHO);
term.c_cc[VMIN] = 0; term.c_cc[VTIME] = 0; // non-blocking read
if (tcgetattr(0, term) == -1) // applying modifications.
return (-1);
/* Your code here with getchar() */
term.c_lflag &= (ICANON | ECHO);
if (tcgetattr(0, term) == -1) // applying modifications.
return (-1);
return (0);
}
EDIT 2:
You have to compile with
-lncurses
option.
The way to do this on a UNIX-like platform (such as Linux) is to use the select function. You can find its documentation online. I'm not sure if this function is available on Windows; you didn't specify an operating system.
I got the best suited answer to my question in comments, which was posted by #eryksun.
The best way is to use a function kbhit() (part of conio.h).
You can spawn a new thread that can emulate pressing of the Enter Key after 30 seconds.
#include <windows.h>
#include <stdio.h>
#pragma comment(lib, "User32.lib")
void ThreadProc()
{
// Sleep for 30 seconds
Sleep(30*1000);
// Press and release enter key
keybd_event(VK_RETURN, 0x9C, 0, 0);
keybd_event(VK_RETURN, 0x9C, KEYEVENTF_KEYUP, 0);
}
int main()
{
DWORD dwThreadId;
HANDLE hThread = CreateThread(NULL, 0,(LPTHREAD_START_ROUTINE)ThreadProc, NULL, 0,&dwThreadId);
char key = getchar();
// you are out of getchar now. You can check the 'key' for a value of '10' to see if the thread did it.
// Kill thread before you do getchar again
}
Be careful with this technique, specially if you do geatchar() in a loop, otherwise you might end up with lot of threads pressing ENTER key! Be sure to kill the thread before you start getchar() again.
I want to be able to clear the value of what scanf read in. In other words, I want to delete the value that was read in by scanf.
Here is my sample code:
#include <stdio.h>
#include <signal.h>
#include <sys/time.h>
volatile sig_atomic_t gotsignal;
void handler(){
gotsignal = 1;
}
int main(){
struct sigaction sig;
sig.sa_handler = handler;
sig.sa_flags = 0;
sigemptyset(&sig.sa_mask);
alarm(5);
sigaction(SIGALRM, &sig, NULL);
int value;
while(!gotsignal){
printf("Insert a value: \n");
scanf("%d", &value);
}
}
Output:
Insert a value:
5(dont press enter)[JPS#localhost c]$ 5 <-
Is it possible(if yes how) to clear the 5?
I have been reading about terminal settings, fflushs, stdin, but i couldn't figure it out. Any help please?
EDIT: After a lot of trys i think i found something that works. If anyone has this problem this worked for me(not sure if it works on other systems and stuff, kinda new to this):
#include <stdio.h>
#include <signal.h>
#include <sys/time.h>
#include <termios.h>
#include <unistd.h>
volatile sig_atomic_t gotsignal;
void handler()
{
gotsignal = 1;
}
int main(){
struct sigaction sig;
sig.sa_handler = handler;
sig.sa_flags = 0;
sigemptyset(&sig.sa_mask);
alarm(5);
sigaction(SIGALRM, &sig, NULL);
int value;
while(!gotsignal){
printf("Insert a value: \n");
scanf("%d", &value);
}
printf("\n");
tcflush(STDOUT_FILENO,TCIOFLUSH); <-important bit!
return 0;
}
Output:
Insert a value:
5
Insert a value:
5(no enter was pressed)!
[JPS#localhost c]$ <- NO MORE NR 5! :D
See these links for good answers:
How can I flush pending input so that a user's typeahead isn't read at the next prompt? Will fflush(stdin) work?
If fflush won't work, what can I use to flush input?
To quote the most interesting:
There is no standard way to discard unread characters from a stdio
input stream. Some vendors do implement fflush so that fflush(stdin)
discards unread characters, although portable programs cannot depend
on this. (Some versions of the stdio library implement fpurge or
fabort calls which do the same thing, but these aren't standard,
either.) Note, too, that flushing stdio input buffers is not
necessarily sufficient: unread characters can also accumulate in
other, OS-level input buffers. If you're trying to actively discard
input (perhaps in anticipation of issuing an unexpected prompt to
confirm a destructive action, for which an accidentally-typed ``y''
could be disastrous), you'll have to use a system-specific technique
to detect the presence of typed-ahead input; see questions 19.1 and
19.2. Keep in mind that users can become frustrated if you discard input that happened to be typed too quickly.
You might use the readline library. But I'm not sure to understand what you mean by "clear the 5".
The simplest example might be
// file testrl.c
#include <readline/readline.h>
#include <stdlib.h>
int main ()
{
char bufprompt[20];
char* lin = NULL;
int cnt = 0;
for (;;) {
memset(bufprompt, 0, sizeof(bufprompt));
cnt++;
snprintf(bufprompt, sizeof(bufprompt)-1, "%d: ", cnt);
lin = readline(bufprompt);
if (!lin)
break;
printf("you typed %s\n", lin);
free (lin);
}
return 0;
}
Compile the above with gcc -Wall -g testrl.c -o testrl -lreadline
See also this question