2

I'm working on a C project that's supposed to be similar to Unix's "more" command. Like "more", this program is supposed to be able to get its input from either a file(if specified) or stdin if no file is given (so the output of other programs can be piped into it) and display it to stdout. Also like "more", echoing and canonical mode are supposed to be disabled, which I've done with the following code:

  //change terminal attributes
  tcgetattr(0, &info); //Get info
  tcgetattr(0, &orig_inf); //Save original info
  info.c_lflag &= ~ECHO; //Disable echo
  info.c_lflag &= ~ICANON; //Disable canonical mode
  info.c_cc[VMIN] = 1; //Get 1 char at a time
  tcsetattr(0, TCSANOW, &info); //Set attributes

To read user commands from the keyboard, I'm opening "dev/tty" explicitly rather than just reading from stdin:

//Open cmd stream
if((cmd = fopen("/dev/tty", "r")) == NULL){
  perror("Failure opening command stream");
  tcsetattr(0, TCSANOW, &orig_inf);
  exit(EXIT_FAILURE);
}

and reading them with getc(cmd). This works fine when the user provides a file to read from, but if the program is receiving input from stdin, it seems like the terminal attributes are being reset. I can see every command I try to type (which means echoing is on again) and commands don't get sent to the program unless I hit enter (meaning canonical mode has somehow been reactivated again). I've searched the web and all of the man pages for almost all of the system calls I'm using, and can't seem to find a reason for this.

If anyone knows why this is happening and how to fix it, help would be much appreciated.

Thanks!

Fac3Value
  • 61
  • 1
  • 2
  • 1
    Your comment says "change terminal attributes", but that is not what you are doing. You are changing the attributes of file descriptor zero, which, when you have re-directed the standard input to read from a file, is not the terminal! – Thomas Padron-McCarthy Apr 04 '15 at 16:45
  • You should check the return value from `tcgetattr()` and from `tcsetattr()`. You don't say whether the code is running with I/O redirection from a file (`yourprog < file`) or via pipe (`someprog | yourprog`), but in either case, operations on file descriptor 0 are not affecting the terminal. And it is quite likely that both functions set `errno` to `ENOTTY` (not a tty). You need to open `/dev/tty` and control its attributes, not those of standard input. Also, `atexit()` can be quite useful for programs like this. – Jonathan Leffler Apr 04 '15 at 17:18

1 Answers1

3

What seems to be missing (at least not stated in the question) is that you are opening /dev/tty to read commands, and are resetting the original standard input via file-descriptor. But there is no mention of using fdreopen or dup2:

  • since you are referring to file descriptor 0 (FILENO_STDIN), this is not the same file descriptor as fileno(cmd) -- unless you use dup2 to replace that.
  • normally one opens devices using open rather than fopen, simply because open provides more control over the process. Then one would use fdreopen to get a buffered stream from the file descriptor.

Using fopen on /dev/tty, for instance, you may have overlooked the actual settings in that stream, which are not necessarily the same as those which you reconfigured on file descriptor 0. A related question might be: What is the difference between stdin and STDIN_FILENO?.

Community
  • 1
  • 1
Thomas Dickey
  • 51,086
  • 7
  • 70
  • 105