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().
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'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.
I have a sample program that outputs a line of text every second. In the test program below, this program writes some text to stdout then waits 1 second and repeats 20 times.
I have another program which uses popen (_popen on Windows) to open a pipe for reading from the program. I then use fgets to read data. The problem I have is that the fgets blocks until the program terminates. Then I get all the output, all 20 lines, in one go. I want to get the output a line at a time, then ok for fgets to block until next line ready. The reason is I plan to use this on a program that will be constantly running, outputting text, e.g. like the use of tail.
If I run this code example on a program that outputs some text all in one go and exits then it works fine.
Why does fgets block? The test program does print some text immediately, so why doesn't fgets read this first line of text immediately?
Here is the code:
#include <stdio.h>
#include <windows.h>
void execute(const char* cmd) {
char buffer[128] = { 0 };
FILE* pipe = _popen(cmd, "r");
if (!pipe) {
printf("popen() failed!\n");
return;
}
while (!feof(pipe)) {
if (fgets(buffer, 128, pipe) != nullptr)
printf("%s", buffer);
}
int rc = _pclose(pipe);
if (rc != EXIT_SUCCESS) { // return code not 0
printf("pclose exit failure: %d\n", rc);
}
}
int main(int argc, char* argv[]) {
if (argc != 2) {
printf("Usage: pipe_test.exe <program>\n");
exit(1);
}
execute(argv[1]);
}
The program run, helloworld.exe:
#include <stdio.h>
#include <windows.h>
int main() {
for (int i = 0; i < 20; i++) {
printf("Hello World %d\n", i);
Sleep(1000);
}
}
Why does fgets block?
Because it's waiting for the children to output something.
The test program does print some text immediately, so why doesn't fgets read this first line of text immediately?
It actually does not print text immediately. The problem here, as #Barmar notices, is that writing to a pipe is buffered (and not line buffered) by the C standard library implementation. This buffering happens in your child program (helloworld), not in your parent program (pipe_test).
From your parent program, you have no control over what the children spawned through popen() will do, therefore if the child output is buffered like in this case, the only thing you can do (without modifying the child's code) is to wait until the buffer is flushed to the pipe.
In order to get the output sooner, you would have to modify the children's code to manually call fflush() or use setvbuf() to disable buffering:
int main() {
setvbuf(stdout, NULL, _IONBF, 0); // Disable buffering on stdout.
for (int i = 0; i < 20; i++) {
printf("Hello World %d\n", i);
Sleep(1000);
}
}
There's really not much else you can do.
I am trying to detect the Ctrl+D user input, which I know returns EOF. Right now, I know the code waits for input from the stdin stream, but is there a way to let the program continue until the Ctrl+D command is in stdin? The program should continue running past the if statement if Ctrl+D isn't inputted.
char buffer[];
if (fgets(buffer, 10, stdin) == NULL{
//write to file
}
You want to stop your program when the user presses Ctrl+D without actually reading stdin? In this case, you should consider using Ctrl+C instead. But first I will write something about non-blocking I/O, since this is what you are asking for.
There is no way to achieve nonblocking I/O in standard C. However, you could use POSIX-functions like select or fcntl in combination with read. There are other questions about it on StackOverflow which should provide all information you need. This question for example.
If you want to handle Ctrl+C instead, you can use thesignal function:
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
volatile bool shouldRun = true;
void sighandler(int) {
shouldRun = false;
}
int main(int argc, char *argv[]) {
if (signal(SIGINT, &sighandler) == SIG_ERR) {
fprintf(stderr, "Could not set signal handler\n");
return EXIT_FAILURE;
}
printf("Program started\n");
while (shouldRun) {
// Do something...
}
printf("Program is shutting down.\n");
return EXIT_SUCCESS;
}
Note that signal handlers (i.e. sighandler) might interrupt your thread at any moment. This means they are prone to race conditions. You must even avoid acquiring any locks within a signal handler. This means just calling printf within a signal handler can cause a deadlock. Just setting boolean flags as shown in the example is fine, though. There are solutions like signal masks and the self pipe trick to circumvent these limitations, but they should not be necessary here.
Since the machine generates EOF on Ctrl+D, you should be checking fgets() for NULL, as fgets() is obliged to return NULL on end of file.
line = fgets(l, BUFFSIZE, stdin)
if (line == NULL)
continue;
On most operating systems, stdin is buffered one line at a time, and any attempt to read it (without going into low-level nasties) will stop until either a line or EOF is available. If you don't mind this, and just want to check for EOF without reading-in any waiting input if EOF is not present, you could use ungetc:
#include <stdio.h>
int check_for_EOF() {
if (feof(stdin)) return 1;
int c = getc(stdin);
if (c == EOF) return 1;
ungetc(c, stdin);
}
int main() {
printf("Start typing:\n");
while (!check_for_EOF()) {
int bytes_typed = 0;
while (getchar() != '\n') bytes_typed++;
printf("You typed a line of %d bytes\n", bytes_typed);
}
printf("You typed EOF\n");
}
You are only guaranteed one character of push-back from ungetc, although most implementations give you much more. And it works only if you're not going to seek the stream later (which is the case with stdin). Notice also that I'm calling it "bytes typed", not "characters typed": Chinese, Japanese and Korean characters for example cannot fit into the char type of most C implementations, and it would depend how the console encodes them when you type (if you have a CJK input method set up or can copy/paste some, you can try it on the above program and see).
It is too much to post here and you are not specific what you have currently and what you want. So here gives you a general idea of how to do it:
Put that if statement inside a forked process or other thread
Send a posix signal to your (parent) process when the key is captured
Add signal handler in your program
If you just wanna terminate the program when C-d is entered, just send a SIGKILL in step 2 and ignore step 3.
If you do not know any term above, Google is your friend