My test application writes logs in stderr and uses stdin to receive interactive commands from the user. Needless to say, that any stderr output spoils user input (and command prompt) in terminal. For example, this command line (_ is a cursor position):
Command: reboo_
will become:
Command: reboo04-23 20:26:12.799 52422 2563 D [email protected]:27 started
_
after log() call.
To fix that, I want to have something like old Quake console in terminal, where logs go one line above the current input line. In other words, I want to get that instead:
04-23 20:26:12.799 52422 2563 D [email protected]:27 started
Command: reboo_
I can modify both logging code and code that reads user input. Want that to work for Linux and OS X. log() function could be invoked from different thread. The log() function is the only writer to stderr.
Other suggestion to fix that problem (spoiled input line) are welcome. I'm looking for a solution that could be implemented without additional libraries (like Curses). I tried to google that up, but realized that I need a sort of idiomatic kickoff to understand what exactly I want.
Upate
Thanks to Jonathan Leffler comment I realized that I also should mention that separating stderr and stdout is no that important. Since I control the log() function it's not a problem to make it write to stdout instead of stderr. No sure whether it makes the task easier or not, though.
Update
Crafted something that seems to work good enough:
void set_echoctl(const int fd, const int enable)
{
struct termios tc;
tcgetattr(fd, &tc);
tc.c_lflag &= ~ECHOCTL;
if (enable)
{
tc.c_lflag |= ECHOCTL;
}
tcsetattr(fd, TCSANOW, &tc);
}
void log(const char *const msg)
{
// Go to line start
write(1, "\r", 1);
// Erases from the current cursor position to the end of the current line
write(1, "\033[K", strlen("\033[K"));
fprintf(stderr, "%s\n", msg);
// Move cursor one line up
write(1, "\033[1A", strlen("\033[1A"));
// Disable echo control characters
set_echoctl(1, 0);
// Ask to reprint input buffer
termios tc;
tcgetattr(1, &tc);
ioctl(1, TIOCSTI, &tc.c_cc[VREPRINT]);
// Enable echo control characters back
set_echoctl(1, 1);
}
However, that doesn't support command prompt ("Command: " at the start of the input line). But probably I can have two lines for that - one for the command prompt and another for the input itself, like:
Command:
reboo_

stderrvery carefully, coordinated with what is written tostdoutvery carefully. In fact, you probably need a full screen management package, possibly with sub-windows for different parts of the screen — likecursesgives you. Failing that, you're going to have to intercept all the output tostderrsomehow (separate thread?) and have that handle it. Could you write the errors to a log file?cursesunless you wish to reimplement it for your own purposes. Otherwise, you have to write code which determines (or knows) where the cursor is, moves the write position (cursor) to the line where you want the errors to go, write the error, and then move the cursor back to where it was before you started writing the error. It can be done;cursesprovides mechanisms that do it automatically, so you can do whatcursesdoes too. But it ain't trivial to do it for yourself.