piped stdin and keyboard same time in C - c

I have read previous questions regarding this problem too. fflush(stdin) does not work in this scenario for me.
I want my program to read from a piped stdin and continue from keyboard input in the middle.
int main()
{
int userin = 3;
read_input();
userin = print_menu();
userin = parse_input(userin);
return 0;
}
I want to read data from a file which is passed to the program as a pipied stding like
program < testing_text
int read_input(){
char line[200];
char word[MAX_STRING+1];
int line_number = 0;
while(fgets(line, sizeof(line), stdin) != NULL ){
//do something
printf("%s",line);
line_number++;
}
}
Now read_input must finish reading from the piped input. 'print_menu' must continue reading from the keyboard.
int print_menu()
{
int userinput;
char c;
char num[4];
while((c=getchar()) != '\n' && c != EOF && c != '\r');
printf("\n1. Choice 1 \n");
printf("2. Choice 2\n");
printf("3. Exit\n");
printf("Enter your choice (1-3): ");
/* scanf("%d", &userinput); */
/* fgets(num,80,stdin); */
scanf("%s", num);
userinput = atoi(num);
return userinput;
}
int parse_input(int userinput)
{
char num[4];
while( userinput > 3 || userinput < 1 ){
printf("Sorry, that is not a valid option\n");
printf("Enter your choice (1-3): ");
scanf("%s", num);
userinput = atoi(num);
/* scanf("%d", &userinput); */
/* while( (c = getchar()) == '\n'); */
}
return userinput;
}
My output is a infinite loop of
Enter your choice (1-3): Sorry, that is not a valid option
When I remove read_input method and piped stdin program works fine.
I cannot figure out a get around for this, does someone has a idea..

Pipes come (controlled by the shell) normally on file-descriptor 0, which is the standard input. You can use dup2() to "move" that to a different file descriptor, and use freopen() to continue reading from that. You can also open the /dev/tty device (or whatever the tty program knows about) and make that your standard input.
The dialog program does this for the --gauge (progress bar) option. The reason why it does this is because dialog is a curses application (which by default uses the standard input and output). The actual pipe file-descriptor is as well optional, so quoting from its manual page gives some hints:
--input-fd fd
Read keyboard input from the given file descriptor. Most dialog
scripts read from the standard input, but the gauge widget reads
a pipe (which is always standard input). Some configurations do
not work properly when dialog tries to reopen the terminal. Use
this option (with appropriate juggling of file-descriptors) if
your script must work in that type of environment.
The logic which implements this is in the util.c, from the init_dialog function. Here is the main part of that, to give an idea how the functions are used:
dialog_state.pipe_input = stdin;
if (fileno(input) != fileno(stdin)) {
if ((fd1 = dup(fileno(input))) >= 0
&& (fd2 = dup(fileno(stdin))) >= 0) {
(void) dup2(fileno(input), fileno(stdin));
dialog_state.pipe_input = fdopen(fd2, "r");
if (fileno(stdin) != 0) /* some functions may read fd #0 */
(void) dup2(fileno(stdin), 0);
} else {
dlg_exiterr("cannot open tty-input");
}
close(fd1);
} else if (!isatty(fileno(stdin))) {
if ((fd1 = open_terminal(&device, O_RDONLY)) >= 0) {
if ((fd2 = dup(fileno(stdin))) >= 0) {
dialog_state.pipe_input = fdopen(fd2, "r");
if (freopen(device, "r", stdin) == 0)
dlg_exiterr("cannot open tty-input");
if (fileno(stdin) != 0) /* some functions may read fd #0 */
(void) dup2(fileno(stdin), 0);
}
close(fd1);
}
free(device);
}

I had a nearly-identical situation with a program I wrote to read binary data from stdin (piped in from some other program's stdout), but I wanted to add keyboard commands (via raw terminal support). Obviously reading piped data and the keyboard simultaneously led to some weird results.
Here's my code, working and tested:
FILE * fp;
int fd;
fd = dup(fileno(stdin));
fp = fdopen(fd, "r");
(void) freopen("/dev/tty", "r", stdin);
Now, my program reads from fp (a FILE* object) for its binary data while reading stdin (now associated with "/dev/tty") for keystrokes.
Note that I discard the return value from freopen (a FILE* object) since I can refer to it via stdin if needed. My experience is that there is no need to close() or fclose() a standard text stream. I fclose(fp) at the end of reading the binary data.

If you want your program to accept keyboard input then don't redirect its standard input. If you redirect a program's standard input, don't expect to be able to feed it data via the keyboard.
If your program needs to accept input both from a file and from the keyboard, then give it the file name as an argument, and let it open and read that file.
If you have to deal with some kind of unmodifiable library function that requires input to be provided via stdin, and you want that input to come from a file, and you want elsewhere to accept keyboard input, then you can maybe play games with dup2()ing file descriptors to make it work out. Once you get it working, consider firing the idiot responsible for that library function.

Related

How can I redefine stdin to point to the console after using file redirection in Unix?

I am working in Unix and I am supposed to first read in text through redirection and then ask the user to enter in a specific character and count how many times it is in the character array.
const int MAX = 8000;
int input = 1;
int i = 0;
char text[MAX], letter;
while(input != 0)
{
scanf("%c", &text[i]);
if(text[i] == '0')
input = 0;
i++;
}
printf("\n%s",text);
printf("\nEnter a letter to search for in the text: ");
scanf("%c", &letter)
Currently, I am printing the correct file through redirection, however my second scanf is being skipped. I am redirecting the file using the command: ./a.out < filename.txt.
If I try and print the character letter then it will result in nothing. It must be reading the \n from inside the empty lines of the text file still. How do I stop the scanf from reading the same text file and let me enter a letter from my keyboard in the console? As an assignment I HAVE to use the command ./a.out < filename.txt.
The freopen() function is exactly what you're looking for. You just need to use it to (re)open /dev/tty as stdin. /dev/tty is a special file referring to the terminal that started the program.
From the manual page:
The freopen() function opens the file whose name is the string pointed to by pathname and associates the stream pointed to by stream with it. The original stream (if it exists) is closed. The mode argument is used just as in the fopen() function.
[...]
The primary use of the freopen() function is to change the file associated with a standard text stream (stderr, stdin, or stdout).
Here's an example:
// ...
FILE *tty;
tty = freopen("/dev/tty", "r", stdin);
if (tty == NULL) {
perror("Unable to open terminal for reading");
exit(1);
}
printf("Enter a letter to search for in the text: ");
// Now scanf will read from the console where the process started.
scanf("%c", &letter);
By the way, your program has some issues. You could read past the end of your text array, you don't correctly terminate it with a NUL character, and you also don't check for errors. A more correct version would be:
const size_t MAX = 8000;
char text[MAX];
size_t i;
int c;
for (i = 0; i < MAX; i++)
{
c = fgetc(stdin);
if (c == EOF)
break;
text[i] = (char)c;
}
text[i] = '\0';
puts(text);
There are a few ways to do this, probably the easiest is just to open /dev/tty which is a special device referring the terminal attached to the current process. I don't recommend replacing stdin as then you will lose access to your file which is redirected there. Instead just use a different file pointer and use functions like fscanf and fgetc. Eg:
FILE *tty = fopen("/dev/tty", "r");
// fopen will return NULL if there is no attached terminal
if(NULL == tty)
{
fputs("Failed opening /dev/tty", stderr);
}
else
{
printf("\nEnter a letter to search for in the text: ");
// Read a character from the terminal
char search = fgetc(tty);
// Now you can still read from the file on stdin and search for
// your letter without needing an array (which may not be large
// enough for the whole file)
char ch;
int count = 0;
while(EOF != (ch = getchar())
{
if(ch == search)
++count;
}
printf("%d occurrences of %c\n", count, search);
}
For more information on /dev/tty and other similar special files, see: https://unix.stackexchange.com/questions/60641/linux-difference-between-dev-console-dev-tty-and-dev-tty0

getc() for passed in input and file reading in C

I have to develop a program in C that can have two kinds of inputs.
By feeding it a string ( I am assuming like this filename < String1234455678, please correct me if I am wrong).
By reading data from some file(s).
I have to do some checks regarding the characters that are in it and store them in an array. But I want to learn how to use the getc() from stdin first.
My first question is, can I use getc() in both cases?
I wanted to loop through every single character in the feed line/file, and I assume the code would look something like this:
char Array1[];
char charHolder;
//If the file/feed has chars (!NULL), execute
if ((charHolder = getchar())!=NULL){
//Do something
//Do some more
//Finally append to Array1
Array1[] = charHolder;
}
There might be some issues with the code above. I wanted to know if that kind of inserting is valid in C (with no index specified, which it will just push the value at the end of the array). Also, I read from http://beej.us/guide/bgc/output/html/multipage/getc.html that getc(stdin) and getchar() are exactly equivalent. I just want to double check that this is indeed true and either function will work with both my cases where I have to read data (from a file and feeding my program a string).
Also, I was wondering how I can achieve reading characters from multiple files. Say if my program was to be executed as programName file1 file2.
Thank you for your time and help!
Cheers!
Edit 1:
I also wanted to know how to check when the chars end from a file/string feed. Should I use the EOF for both cases?
Example:
while ((charHolder = getchar()) != EOF){
//code
}
Here is a sample:
#include <stdio.h>
void do_read(FILE * file, int abort_on_newline) {
char ch;
while (1) {
ch = getc(file);
if (ch == EOF) {
break;
}
if (abort_on_newline && ch == '\n') {
break;
}
printf("%c", ch);
}
}
int main(int argc, char * argv[])
{
int i = 1;
FILE * fp = NULL;
if (1 == argc) {
// read input string from stdin, abort on new line (in case of interactive input)
do_read (stdin, 1);
}
else {
// cycle through all files in command line arguments and read them
for (i=1; i < argc; i++) {
if ((fp = fopen(argv[i], "r")) == NULL) {
printf("Failed to open file.\n");
}
else {
do_read(fp,0);
fclose(fp);
}
}
}
return 0;
}
Use it like this:
To read from stdin: echo youstring | youprogram, or just start
yourprogram to get input from user
To read from file(s) yourprogram yourfile1 yourfile2 ...
Yes your can use getc in both cases, yes you should check for EOF in both cases, except for interactiv input. In case of binary files you also need to use feof function to check for EOF. See code above to read from multiple files.

How input works in C

I'm quite confused about the function scanf and how the input works in C.
See this code :
#include<stdio.h>
int main()
{
FILE pt = *stdin;
char b[100];
scanf("%s", b);
scanf("%s", b); //breakpoint here
scanf(" "); //breakpoint here
}
When the code runs, I input 12345 into the console. I found that pt _ptr(which I don't actually know what it is) has the value "12345\n\n" and b[] has the value "12345".
Then I continue the program and input 23456. Now pt _ptr is "23456\n\n" and b[] is "23456".
My question :
How does the input work in C? Why does pt _ptr have the value of "12345\n\n" not "12345\n" since I pressed enter only one time(and it seems like the function scanf skips those two \n after successfully read "12345" ).
You are looking under the carpet... You are not supposed to copy FILE structs so the first line
FILE pt = *stdin;
results are actually undefined. Do not look inside, unless you are willing to read and understand the source of you standard C library!
The rest of the code is pretty easy to understand, as b has always the expected value, isn't it?
It is not apparent what it is you are really trying to do besides learn scanf, but you are also invoking stdin and the FILE struct.
Using fscanf instead, (read link for details on `fscanf) it will be easier to use all three.
Here is a simple example of how you can combine fscanf, FILE, and stdin:
int main(void)
{
FILE *fp={0};
int res =0 //use to check return value of fscanf
char buf[80]; //use 80 for demonstration only
fp = fopen("c:\\dev\\play\\playtext.txt", "w");//create file and stream
res = fscanf(stdin, "%s", buf);//stream input from stdin
if(res == EOF)
{
printf("Exiting... input error");//notify and exit upon error
return -1;
}
while (strstr(buf, "quit") == NULL)//allow exit by typing quit
{
if(fputs(buf, fp) == EOF) return -1;
buf[0]=0;//reset buffer
res = fscanf(stdin, "%s", buf);
if(res == EOF)
{
printf("Exiting... input error");//notify and exit upon error
return -1;
}
}
fclose(fp);
return 0;
}

Why does this code compile but not actuall work?

Line 12 to 23 runs. But doesn't actually run when the if statement is added. it does compile and runs. It asks the first printf statement then terminates when I choose a character. Why is this happening and how do I fix it.
#include <stdio.h>
#include <stdlib.h>
int main()
{
char ch, file_name[25];
FILE *fp;
printf("Enter [A] and select file or [X] to exit:"); // Prompt user to select file or exit
scanf("%c",&ch);
scanf("%c",&ch);
if (ch=='A')
{
printf("Enter the file name\n"); // if user chooses 'A' this code should run
gets(file_name);
fp = fopen(file_name,"r"); // reading file
if( fp == NULL )
{
perror("File not found.\n");
exit(EXIT_FAILURE);
}
printf("Contents of %s are:\n", file_name);
while( ( ch = fgetc(fp) ) != EOF )
printf("%c",ch);
}
else if (ch=='X')
{
printf("Exiting program...");
exit(0);
}
}
Because you have two calls to scanf..
In the first one, you are reading your input 'A' or 'X' successfully.
In the next call, you are reading the newline character(\n) which was pressed earlier into the same variable ch. So it doesn't satisfy any if clause and simply comes out of program..
Instead make second call to temporary variable..
char temp;
scanf("%c", &temp);
Also fgets is preferred over gets
I guess you are not reading properly from the file , Try:
char buff[255]; //define buffer to read lines
while ( !feof(fp ) ){
memset(buff, '\0', sizeof( buff) );
fgets(buff, 255, (FILE*)fp);
printf("%s", buff );
}
fclose(fp); //don't forget to close
There are a large class of programs that compile yet don't run properly. That's why a distinction is made between syntax errors and runtime/logic errors.
scanf("%c",&ch);
scanf("%c",&ch);
I'm assuming that's to get rid of the newline character but it's a bad idea to read it into ch, since that should keep the first character.
If that is the case, simply read it into some junk variable so that ch is preserved.
char ch, junk, file_name[25];
:
scanf("%c",&ch);
scanf("%c",&junk);
Sadly though, there may be numerous other problems with this approach. If you want a decent line input function, you can find one here. That's much better than using gets(), which is inherently unsafe.
It has buffer overflow detection and prevention, automatic flushing of input lines where too long, prompt output and so on. Once you've used it to get an input line, all you need to do is compare that with what you want, something like:
if (strcmp (buff, "A") == 0)
doSectionA();
else
if (strcmp (buff, "X") == 0)
doSectionX();

File reading in c with fscanf

Im taking a class in c programming and I have this project where they give us a half-made project, and we need to finish it and fix some of the functions.
This project is about sort of a social network.
In this project you can send messages to other users (on the same computer for now) by writing the target user and then you enter the message. Afterwards the message is saved in a file called "messages.txt" in the same folder in this Format:
"[At]25/08/2013 [From]user1 [To]user2 [Message]hello whats up?"
"[At]Date [From]user [To]user2 [Message]any user input"
now after writing this, i go to the second user and try to read from the file with this function:
void showUnreadMessages(char* userName) // the function gets the name of the current
user that wishes to read his/hers messages
{
char msg[MAX_MESSAGE];
char toU[MAX_USER_NAME];
char fromU[MAX_USER_NAME];
char at[15];
int count = 0, flag = 0, count1 = 0;
FILE *file = fopen(MESSAGE_FILENAME, "rt"); //open the messages file
FILE *temp;
clearScreen(); //system("CLS")
if (file == NULL) //if the file didn't exict open one
{
printf("No messages\n");
flag = 1;
_flushall();
file = fopen(MESSAGE_FILENAME, "wt");
_flushall();
}
while (!feof(file) && flag == 0) //if the file did exict
{
if (count1 == 0)
{
temp = file;
}
_flushall();
fscanf(file, "[At]%s [From]%s [To]%s [Message]%s\n", at, fromU, toU, msg); //scan one line at a time
_flushall();
if (strcmp(userName, toU) == 0) //if the userNames match than its a message for the current user
{
count++;
}
count1++;
}
fclose(file);
if (count > 0 && flag == 0) //if there are messages to user
{
printf("You have %d new Messages\n", count);
_flushall();
while (!feof(temp))
{
_flushall();
fscanf(temp, "[At]%s [From]%s [To]%s [Message]%s\n", at, fromU, toU, msg); //scan one line at a time to print it for the user
_flushall();
if (strcmp(userName, toU) == 0)
{
printf("New message at %s from: %s\nStart of message: %s\n-----------------------------------------\n", at, fromU, msg);
}
}
fclose(temp);
}
else if (count == 0 && flag == 0)
{
printf("You have no Messages\n");
}
if (!file)
{
remove(MESSAGE_FILENAME);
}
PAUSE; // system("PAUSE")
}
Now when i try to read with this function, it only shows that the message is the first word in the message section on the first line...
For example For "[At]25/08/2013 [From]user1 [To]user2 [Message]hello whats up?"
the message will be "hello"
and it will be printed twice.. i dont know what to do, for some reason when i open the file and do fscanf for one time it also shows that the pointer file starts "up?[At]... (what appears on the second line)"
Please help me if you understand what i did wrong (which i know is a lot)
Thanks in advance
This part of fscanf :
"..etc. [Message]%s\n"
will only read ONE word of "Hello what's up" because %s parses for contiguous characters.
nr_fields = fscanf(file, "[At]%s [From]%s [To]%s [Message]%80c\n"
would read up to 80 characters regardless of spaces etc. in the text message. Also, the destination for %80c must be 80 characters or more!
Also, always test for number of fields found by fscanf.
Finally, fscanf works when used as directed, but it does have some subtle aspects.
One issue is that temp is pointing to a handle that is no longer valid after you call fclose(file) after the first loop. You could use fgets() to read a line and strtok() and strncpy() to split the read string.
I think it would be a good idea to encapsulate the reading in an extra function to reduce code duplication.

Resources