Custom interactive shell - c++

I've run into the following problem: My console utility should be running as a process (hope it's the right term) so every command goes to it directly. Like gnuplot, interactive shells (irb, etc.).
This shows what I'm talking about:
Mikulas-Dites-Mac-2:Web rullaf$ command
Mikulas-Dites-Mac-2:Web rullaf$ irb
>> command
NameError: undefined local variable or method `command' for main:Object
from (irb):1
>> exit
Mikulas-Dites-Mac-2:Web rullaf$
first command is executed as shell command, but after I enter irb, it's not. You get the point.
irb puts console into some special mode, or it simply parses the given input itself in some loop?
Is here any proper way to create such a behavior in c++? Thanks

You have to parse the input yourself. Depending on the complexity of the input, this might be accomplished by some simple string matching for trivial cases. A very simple example:
#include <iostream>
#include <string>
int main()
{
std::string input;
for(;;)
{
std::cout << ">>";
std::cin >> input;
if(input=="exit")
return 0;
else if(input=="test")
std::cout << "Test!\n";
else
std::cout << "Unknown command.\n";
}
}
Obviously, this little program will print a prompt (>>) and understand the commands exit and test and will print Unknown command. on all other commands.
For everything else, you probably want to learn some more about pattern matching or parsing; Google is your friend (take a look at bison for example and a good tutorial).

To parse your command line, you can use Boost.Program_options.

Related

Config File using Environment Variables

I have a .cfg file and I'd like to use an environment variable to configure one of the fields.
directory=${HOME}/folder1/
However, when I parse this config, it's reading ${HOME} as a string, which is obviously not what I want.
I wrote my own parser in C++, in case I need to do something special. Right now it is a very basic read and parse.
void Config_Parser::parse_config_by_delimiter(string config, string delimiter) {
ifstream infile(config);
while (infile >> line) {
key = line.substr(0, line.find(delimiter));
value = line.substr(line.find(delimiter)+1);
if (this->config_settings.find(key) != this->config_settings.end()) {
cout << "Cannot use config... same key is set multiple times" << endl;
}
this->config_settings.insert({key, value});
}
}
The code seems to work fine for all other config settings (anything not using an environment variable), so I don't think its a problem with the code. But, I am a C++ noobie, so it's here anyways.
When I parse and print out the value:
Actual output: ${HOME}/folder1/
Expected/desired output: /home/my_dir/folder1/
Untested
You can use wordexp to do posix shell-like expansion of strings.
The function wordexp() performs a shell-like expansion of the string
s and returns the result in the structure pointed to by p.
You will need to #include <wordexp.h>
You also probably want to specify the flag WRDE_NOCMD to prevent subshell command execution.
http://man7.org/linux/man-pages/man3/wordexp.3.html
Is the following configuration syntax acceptable to you?
directory = getenv("HOME") + "/folder1/";
If so, then a configuration file parser library I wrote called Config4* can do what you want. You can find it on http://www.config4star.org.
I recommend you scroll down the web page to "Download the Manuals" and retrieve Config4* Getting Started Guide and Config4* C++ API Guide. Chapters 2 (overview of syntax) and 3 (overview of API) of the "Getting Started" guide should be more than sufficient to get you up and running.

how to pass on some output from a c++ program to the shell so that it can be used in the shell

Is there any good way i can make some data created by my c++ program available to the shell after exiting the program?
I have a c++ program, inside which i have a string containing a path:
std::string path = "/home/some/path"
I want this path to be available after the c++ program exits main and i am returned to the shell, so that i can use that path (e.g. cd to that path).
I have considered/tried the following approaches:
I tried making an environment variable in c++ program using setenv(). However the environment variable only exists while in the c++ program, and it is apparently not possible to make those changes visible in the shell after exiting the program.
(considered) writing the path to a temporary file, so that a bash script could later access the details of the path from it. However i have read many suggestions to not do that due to security vulnerabilities.
I tried calling the bash script from within the c++ program, using system(). This does not work if i try to cd to that directory (exiting the program will keep me in the same directory as before).
I figure that if i am desperate, i could have my program cout the path, and use the solutions as described here:
$ ./program | tee output.txt
Then the path is stored inside the file. This works technically, but has the undesirable effect of creating a file and printing the path to the screen, and is basically creating a temporary file.
another option to, again, cout in my program, and use command substitution. running in the shell
$ var=$(./program)
storing the path in var. This didnt work because my program does many things including requiring user input before calling
std::cout<< path << std::endl;.
Particularly, i have observed this approach to not display a curses window, which is required for the program.
the only solution that has worked is piping the output to tee.
Environment variables are only an input, they cannot be used to return any information from a program.
You are already using std::cin and std::cout for user input, and std::cerr should be reserved for error messages. However, you can have the shell open more filedescriptors, and have your program write to those. However, doing this with pure C++ is not possible. But if you don't mind using POSIX C functions:
#include <cstdio>
int main() {
FILE *f = fdopen(3, "w");
fprintf(f, "some path\n");
}
And then use it like so:
./program 3> output.txt
This of course creates an undesirable file. I don't think there is any way to store the output from an extra filedescriptor directly to a variable in bash. However, you could create a temporary file inside /dev/shm, so it will never be written to disk, or create a FIFO object and redirect the output from the program to the FIFO, and then read it back. For some examples of how to do this, see this question.
You could write the output that you want the user to see to stderr instead of stdout. Only output what you need your shell script to see to stdout:
#include <iostream>
int main() {
std::clog << "Enter data: "; // clog prints to stderr like cerr
std::string line;
std::getline(std::cin, line);
std::cout << line << '\n';
}
Then this will work:
var=$(./program)

std system is run after going to next line c++

here is piece of my code:
void Espresso::run()
{
std::system("/home/espresso-ab-1.0/src/espresso espresso.in > espresso.out");
std::string line;
std::ifstream myfile ("espresso.out");
if (myfile.is_open())
{
while ( getline (myfile,line) )
{
std::cout << line << '\n';
}
myfile.close();
}
}
I am wondering if above code first run the system command and fill completely "espresso.out" file and then go to the the next line of reading it.
if not, how I can make sure file is fully printed before going to read it.
NOTE: I am restricted to use C++03.
Thanks for your prompt answer. I want to Update my question by:
- Is it a thread safe method as well?
std::system is not an async function. So for example, if you'd run:
std::system("sleep 5");
std::cout << "Foo" << std::endl;
"Foo" will be displayed after 5 seconds.
Of course if you're on linux you could run it like this std::system("sleep 5 &"). Then the sleep command will run as a background process and the code following the system call will execute immediately.
Although I encourage you not to use this function. Calling system functions by their name is dangerous. Imagine what would happen, if someone replaced the sleep binary in your system with their own program. Conclusion: your program will hang until the system command is completed. So your file will be ready.
Yes, the command will be fully completed before the std::system call returns.

Writing a Shell Script that runs my program with inputs

Ok so I created my own shell, and I've tested it plenty on my own, but I need shell scripts that will run it and test it.
I've create a script which consist of this:
#!/bin/bash
echo "ls && echo dog" | ./a.out
However, all it does is print the command prompt "$" infinitely, and I have to force quit the program. therefore I am pretty sure my program does not like my script lol. My program works by using getline to capture the user input until they push <enter> and the boost library to tokenize the string and look for connector e.g "||" "&&" ";" and and so on, then run the commands. All of this is done in a while loop that loops until the user types exit and I close my program. Being as I am new to writing scripts I am sure I probably am not writing my script in the best of manners. I created a simple program to ask for your age and then output it and this script method works for that, but being as my shell isn't as simple I am not surprised this scrip doesn't seem to work.
string user_input;
bool good = true;
typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
while(good){
//command prompt
cout << "$ ";
//read in user input
getline(cin, user_input);
//tokenize user input
tokenizer tok(user_input);
//parse and execute commands inputed by user in string
//only exit while loop if user command is <exit> good = false
}
my shell works if I execute the program normally and I enter inputs into the program what I need is a shell script that I can use to run and test the program for me. Ex. if I type ./script.sh in the standard linux shell it will run my script which will then execute my a.out and then test my own shell with a variety of commands Examples being ls echo ...
You should exit the shell when you reach EOF (End Of File). Getline while return -1 in that case.
I can't think of any other advice as you didn't provide any code, but this might resolve the infinite loop issue.

Adding a status bar to c++ console applications

I am making a linux application using C++ and it will print info out to the console. Parts of the program will take a while to compute and I would like to add a status bar in the console similar to the one used in wget (I put my own depiction below).
%complete[===========> ] eta
What would be the best way to accomplish this goal? Are there any useful libraries that make it easy to add this functionality?
If your program is like wget, that is, it's basically a batch program without the need for a full-screen UI (for which I would recommend ncurses), you can use the trick to print a carriage return (but not line feed) after your line; the next thing you write will overwrite the same line.
Here's a demonstration.
#include <iostream>
#include <unistd.h>
int main(void)
{
for (int i = 0; i < 10; i++) {
std::cout << "Status: " << i << "\r" << std::flush;
sleep(1);
}
std::cout << "Completed.\n";
}
The ncurses library should be useful to you. Or you can write the progress line char by char, using backspaces, calling fflush or std::flush, etc.
A simpler way would just to output dots...
You can use debconf-apt-progress to run a command while displaying a progress line like apt does. Your command needs to report progress over a pipe fd back to debconf-apt-progress. I haven't figured out how to extract this functionality out of debconf-apt-progress yet.