4

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..

Community
  • 1
  • 1
Krv Perera
  • 119
  • 2
  • 15
  • 1
    Depending on exactly what you need, you may be able to solve this by combining piped and terminal input with the && operator - see http://stackoverflow.com/questions/5843741/how-can-i-pipe-initial-input-into-process-which-will-then-be-interactive – Chris Stratton Apr 17 '15 at 02:37
  • Thank you I never knew that I can't do both at the same time. Do you any good reference that I could learn more about pipes and stdin which helps me to understand the problem. Why it happens ? – Krv Perera Apr 17 '15 at 06:32

3 Answers3

5

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);
    }
Thomas Dickey
  • 51,086
  • 7
  • 70
  • 105
2

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.

pr1268
  • 1,176
  • 3
  • 9
  • 16
  • P.S. I meant to thank Thomas for his answer--my own response here is merely to add another easy example of doing what Krv (and myself) needed. – pr1268 Feb 19 '16 at 01:42
1

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.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157