How to synchronize input with output on a terminal? - c++

I'm writing a linux shell for a custom scripting language, and I want to print a "... " before each follow-up line a user enters for a single statement, with the first line having a ">>> " printed, then waiting for input. Following is an example:
>>> void f() {
... "this is a test"
... }
>>>
I'm reading the line with fgets, and after I read it completely, I print the "... ", and repeat using another call to fgets. This works fine for moderately fast interactive input. But if I paste code containing newlines into the terminal, what I get is the following
>>> void f() {
"this is a test"
}
... ... >>>
The "... "'es are printed too late, even though I emit a fflush call after I print them to stdout. Does anyone know whether there is anything special to do to make this work?

If you turn off echo (see stty(1) -echo) for the terminal, then you are in complete control over when the input is printed to the screen.
My assumption is the paste is causing all lines to be written to the terminal at once, and your program will never get an opportunity to send output to the terminal when necessary. So, if you turn off echo and print the user's input as they enter it, you can perform your special handling when you see the newline chars.
You can see that the irb program does something very similar by running strace on it:
ioctl(0, SNDCTL_TMR_TIMEBASE or TCGETS, {B38400 opost isig icanon echo ...}) = 0
...
ioctl(0, SNDCTL_TMR_STOP or TCSETSW, {B38400 opost isig -icanon -echo ...}) = 0

There's not really a simple way to do it using stdio -- you'll need to use something like ncurses to control the terminal. The issue is that when you copy and paste multiple lines like that, they all get pulled into stdin's read buffer in a single call to read(2), so stdout has no chance to intervene and print the intervening prompts.

Related

How to get the full cmdline that is used to call a program, if the cmdline has multiple pipes

Im writing a framework, to track how people use my utilities, like example utility 'result'
So I want to put in piece of code into result.cxx main() that will log stuff like,
1. what arguments were given to result = argc, argv
2. what other programs were used to process stuff and pipe to my utility 'result'
eg:
Im trying to run a program 'result' which is provided input from pipes like
abc -e earg -t targ | process_stuff_out_data | result -s sarg
now in the result.cxx I use to capture piped input
std::string piped_data;
std::getline(std::cin, piped_data);
this works with cases like
echo "1 2 3 " | result -i in_number
// here the piped input is just "1 2 3" so i am able to log it from result
but wont work for cases where the output from the previous program is a stream of binary data
abc -e earg -t targ | out_bin_data | result -s sarg
In this case i just want to
LOG_PIPED_STUFF: abc -e earg -t targ | process_stuff_out_data
std::getline won't return untill it reads a newline, see here: http://www.cplusplus.com/reference/string/string/getline
Use another separator token or just use another function to read from standard input while the data becomes availible.
You can use for example feof(stdin) to check if stdin has bytes availible and then fread() them.
If you are on linux you can use select(2) to wait for input on file descriptor 0.
Possible reasons you are not getting data
the data is binary and doesn't have newlines. You need to use binary I/O calls.
the data is buffered. You need to either flush the stdout of the middle process process_stuff_out_data if you can rewrite it, or just wait until abc and process_stuff_out_data exit.
process_stuff_out_data is writing to stderr, not stdout. Or it is writing to the console (ugh), i.e. /dev/console.

C++, Print to screen even with > file.out in command line

I am manipulating characters from a file and sending them to another file using command line (which I am pretty new to).
a.out -d 5 < garbage01.txt > garbage02.txt
The characters are going to garbage02.txt through cout.put(char). If the command line arguments don't validate I just want to print to screen a simple message to state that, but everything goes to garbage02.txt. Changing the layout of the command is not an option.
I hope this is a pretty straight-forward issue, that I am just having difficulty finding a solution to.
It is common to write error messages to stderr and normal output to stdout. To print an error message to stderr do
std::cerr << "Something went wrong\n";
(You can also do this with fprintf, but that is usually not needed.)
Output written to stderr will not be redirected by
> someFile
but only by
2> someFile
so the user can choose where they want to see the "normal" and the "error" output separately.
std::cerr also has the nice property that it does not buffer the output (unlike std::cout). That means that the user will see the error message before the program continues after the output line.
If you do not want this non-buffer functionality, use std::clog.
You can use /dev/tty file for that, it is a special file representing terminal for current process.
#include <fstream>
std::ofstream screen("/dev/tty");
screen<<"Your message"<<std::endl;
Either use std::cerr to print to screen
std::cerr << "Some message" << std::endl;
or change your terminal command
a.out -d 5 < garbage01.txt 2> garbage02.txt # Redirect stderr stream only

system("pause") clarification

When i use system("pause"), then a line "Press any key to continue..." shows up on the screen.
This is iritating and makes reading the output quite cumbersome.
Is there some way to stop this from coming?
Do you mean that you want to press any key to continue but not to display the "Press any key to continue" on the screen? Try this getchar(); this will capture one character typing from keyboard and continue.
Rather than using platform dependent system("pause") you can use the platform independent std::cin.get() and if the buffer is messing with it, you can use:
std::cin.ignore(std::numeric_limits<std::streamsize>::max(),'\n')
before hand to clear the buffer.
Assuming you're on Windows, replace the system("pause") with system("pause > NULL").
First of all, you should never use system("pause") because it is dangerous. Your code will be calling an external system procedure for no reason; and a cracker can find a way to substitute the "pause" command to other, making your program call the other program with your user permissions.
That said, you can avoid the message sending it to null device.
On Windows:
pause > nul
And if you want to be bold to make this awful system call portable, you can use:
on linux:
echo Press any key to continue ...; read x
Now you can apply the OR and AND (logic connectives) to both and make a system call that works on both systems:
void pause(void)
{
system("echo Press any key to continue . . . && ( read x 2> nul; rm nul || pause > nul )");
return;
}
Linux will create a temporary file called "nul" because it does not recognize this keyword. The null device on linux is /dev/null, not just nul. After that, the command will remove this temporary file with rm nul. So if you happen to have a file named nul on the same directory, be warned this command is not good for you (for yet another reason).
This command mimics the original. If you want to avoid the message, just remove the echo Pres... part of it.
Bonus:
Clear the terminal screen portably using system? (No, do not do this for the same reasons. Its dangerous.) But for tests purposes, you can use:
system("cls||clear");
Avoid pause. C is a language, one of the most powerful languages that there is. I'm sure there is a way to make a pause using only C (getchar() or scanf() for instance).
That line is part of the system("pause"). You can try a different method, such as getline(std::cin, variable) or cin.get().
Use
system("pause>nul")
It works perfectly for windows!

Is it possible to cout to terminal while redirecting cout to outfile?

I'm running a program and redirecting cout to an outfile, like so:
./program < infile.in > outfile.o
I want to be able to read in an option ('-h' or '--help') from the command line and output a help message to the terminal. Is there a way I can do this but still have the regular cout from the rest of the program go to the outfile?
Would cout be the right object to use for such a thing?
You should use cerr to output your help message to STDERR, which is not included in your redirection to outfile.o.
Given ./program < infile.in > outfile.o:
cout << "This writes to STDOUT, and gets redirected to outfile.";
cerr << "This doesn't get redirected, and displays on screen.";
If, later on, you want to redirect both STDOUT and STDERR, you can do
./program < infile.in &> outfile.o
If you want to redirect only STDERR, but allow STDOUT to display, use
./program < infile.in 2> outfile.o
Bash redirection is more complex than most people realize, and often everything except the simplest form (">") gets overlooked.
If you're on linux you can use the pseudo device /dev/tty to output to a controlling terminal (if any). This will work even if stderr is redirected as well as stdout. Other operating systems may provide similar mechanisms.
E.g.
#include <iostream>
#include <ostream>
#include <fstream>
int main()
{
std::ofstream term("/dev/tty", std::ios_base::out);
term << "This goes to terminal\n";
std::cout << "This goes to stdout\n";
return 0;
}
Will work like this:
$ ./a.out
This goes to stdout
This goes to terminal
$ ./a.out >/dev/null
This goes to terminal
Note the way that the with the two streams being buffered independently the relative ordering if they are outputting to the same device is not necessarily preserved. This can be adjusted by flushing the streams at appropriate times.
~$ cmd | tee log_file to dup stdout to file and terminal
~$ cmd 2>log_file to print stdout onto terminal and stderr into a file
You may like to output the help message to stderr. Stderr is generally used for non-normal output and you may consider a usage paragraph to be such output.
One of the things I've done - not saying this is always appropriate - is write modules that have something like this signature.
void write_out(ostream &o);
And then I can create fstream objects and pass them in, or pass in cout and cerr, whatever I need to at that time. This can be helpful in writing logging code where sometimes you want to see on-terminal what happens, and at other times you just want a logfile.
HTH.
You should use cerr instead of cout. Using shell redirection > only redirects stdout (cout), not stderr (cerr).

Implementing a log watcher

I'm wondering how you can implement a program similar to tail -f in C/C++, a program that watches for and processes new lines added to a log file?
You can use fseek() to clear the eof condition on the stream. Essentially, read to the end of the file, sleep for a while, fseek() (without changing your position) to clear eof, the read to end of file again. wash, rinse, repeat. man fseek(3) for details.
Here's what it looks like in perl. perl's seek() is essentially a wrapper for fseek(3), so the logic is the same:
wembley 0 /home/jj33/swap >#> cat p
my $f = shift;
open(I, "<$f") || die "Couldn't open $f: $!\n";
while (1) {
seek(I, 0, 1);
while (defined(my $l = <I>)) {
print "Got: $l";
}
print "Hit EOF, sleeping\n";
sleep(10);
}
wembley 0 /home/jj33/swap >#> cat tfile
This is
some
text
in
a file
wembley 0 /home/jj33/swap >#> perl p tfile
Got: This is
Got: some
Got: text
Got: in
Got: a file
Hit EOF, sleeping
Then, in another session:
wembley 0 /home/jj33/swap > echo "another line of text" >> tfile
And back to the original program output:
Hit EOF, sleeping
Got: another line of text
Hit EOF, sleeping
I think what you're looking for is the select() call in c/c++. I found a copy of the man page here: http://www.opengroup.org/onlinepubs/007908775/xsh/select.html. Select takes file descriptors as arguments and tells you when one of them has changed and is ready for reading.
The tail program is open source, so you could reference that. I wondered the same thing and looked at the code a while back, thinking it would be pretty simple, but I was surprised at how complex it was. There are lots of gotchas that have to be taken into account.
See here
You could either call out to tail and retrieve the stream back into your app, or as it's open source, maybe try to pull it into your own code.
Also, it is possible in C++ iostream to open a file for viewing only and just read to the end, while buffering the last 10-20 lines, then output that.