Extracting .rar/.zip using c++ - c++

For no particular reason, I'm currently working on a program that extracts .zip/.rar files using system().
I currently have WinRar installed because winrar.exe is able to handle both .zip & .rar files.
int main()
{
vector<wstring> files;
if (ListFiles(L"folder", L"*", files))
{
string program = "\"C:\\Program Files\\WinRAR\\winrar.exe\"";
string args = "x -y";
string type = "*.*";
TCHAR dir[MAX_PATH];
GetCurrentDirectory(MAX_PATH, dir);
wstring current_directory(wstring(L"\"") + dir + wstring(L"\\"));
for (const auto& f : files)
{
if (wcscmp(PathFindExtension(f.c_str()), L".rar") == 0 ||
wcscmp(PathFindExtension(f.c_str()), L".zip") == 0)
{
string file = ws2s(f.c_str());
string output = "\"c:\\Users\\my name\\Desktop\\output\"";
string command = program + " " + args + " " + ws2s(current_directory) + file + "\"" + " " + type + " " + output;
cout << command << endl;
if (system(command.c_str()) != 0)
return GetLastError();
}
}
}
return 0;
}
Because I'm using the command line, and don't want spaces to be a problem I wrap what I can in quotation marks:
-- "C:/users/username/program files (x86)/" --
-- "folder/zipped folder.zip" vs folder/"zipped folder.zip" --
After building the complete command contained in command, I printed it out to the screen so I could Edit->Mark:
"C:\Program Files\WinRAR\winrar.exe" x -y "C:\Users\my name\Documents\Visual Studio 2013\Projects\extractor\folder\unzip.zip" *.* "c:\Users\my name\Desktop\output"
However, 'C:\Program' is not recognized as an internal or external command,
operable program or batch file. is what I'm met with after the system(command) call.
If I Copy & Paste the exact same command into Start->Command Prompt, it works like a dream.
How to extract ZIP files with WinRAR command line?
http://comptb.cects.com/using-the-winrar-command-line-tools-in-windows/
https://www.feralhosting.com/faq/view?question=36
Is there a way different way to invoke the system() call?
If there's not, how else can command line arguments be used?
I'd prefer to [avoid entirely] not use Boost:: or 3rd party libraries.
Thanks!

This is probably because of the quirky behavior of Command Prompt when it comes to quotation of arguments. Whenever you call system("\"arg1\" \"arg2\""), it is equivalent to calling:
cmd.exe /c "arg1" "arg2"
Because of the strange behavior as described in the linked post, this will not be interpreted correctly by Command Prompt. An extra set of quotes is needed:
cmd.exe /c ""arg1" "arg2""
For invoking executables, CreateProcess provides an alternative that gives you more control over the process. You'll still have to quote the arguments but the rules are a bit simpler as the Command Prompt is no longer in your way.

Related

How can I use C++ to write to an opened terminal?

I am trying to communicate with a program (xfoil) and read and write to its command line. Xfoil can take many parameters in its built in terminal.
I can open the program using system("path") but how do I input more commands to the opened xfoil terminal? Once I can do that I can do all that I need to do with the terminal commands.
Tiny Process Library is a good library for interacting with processes.
Checkout example 6 and 7 for giving commands to a process. https://gitlab.com/eidheim/tiny-process-library/blob/master/examples.cpp
How can I use C++ to write to an opened terminal
On linux, there are 7+ steps:
1) open a terminal
I find ctrl-alt t most convenient
press and hold ctrl and alt, then press t
There is also a selection LXTerminal on a mouse menu
Note - There is a way for your program to open a 'default' terminal,
and c++ can use the technique, but it is not c++, and I always seem to
need to change the defaults for my use.
2) using the terminal, you manually find the name of the terminal by:
type the command "tty" into the terminal
Typical response on my system:
hostname#username:~$ tty
/dev/pts/1
The terminal name is always of the form "/dev/pts/x" on my Linux system, where x is a number.
3) For my programs (that use a second terminal), my code accepts the x portion (of the terminal response) as command parameter 1, and uses that parameter to create a path file name (PFN) to the terminal.
std::string aTermPFN = "/dev/pts/"; // partial address
aTermPFN += argv[1]; // ouput tty identified by argv[1]
// creating /dev/pts/<argv[1]>
// thus creating PFN of "/dev/pts/1"
4) My code typically provides a confirmation of the number during creation. (recommended)
std::cout << "accessing '" << aTermPFN
<< "' with std::ofstream " << std::endl;
5) and then creates (or attempts to create) the ofstream object
std::ofstream* ansiTerm = new std::ofstream(aTermPFN);
6) and perform a few checks on it ...
if (!ansiTerm->is_open())
{
std::cerr << "Can not access '" << aTermPFN << "'" << std::endl;
assert(0); // abort
}
7) When done with term, be sure to clean up
ansiTerm->close();
delete ansiTerm;
// you can avoid the delete if you use an appropriate smart pointer.
Now all output to that 2nd terminal uses the 'ansiTerm' object, I happen to use a more generic term in that code (not a pointer, but reference) : "termRef".
Examples of use
// mostly, in the app where these sample exist, I output text at computed
// screen locations
// ansi terminals provide goto row col ansi functions
// the style is simply position the cursor,
termRef << m_ansi.gotoRC(3, col) << ss.str() << std::flush;
// then output text-----------------^^^^^^^^ (ss is stringstream)
// ansi terminals can clear
void clearScr() { termRef << m_ansi.clrscr() << std::flush; }
// ansi terminals can draw simple borders (not-fancy)
{ // across top gotoRC (int row, int col )
termRef << m_ansi.gotoRC ( tlcRow, tlcCol+1);
for ( int i=0; i<borderWidth; ++i) termRef << m_acs.HLINE;
termFlush();
}

How to input multiple commands or loop a command in Command Prompt using C++?

I am trying to display the results of my commands live. I am already able to retrieve the results, but I want a live stream. Whenever I put a while loop into my code to repeat the command, a window opens, the command executes, then the window closes, then another window opens and the process repeats until the program crashes. I want the command prompt to run in the background while receiving commands because I am running a QT gui in front of it.
Here is my code currently:
cmd = _popen("snmpget -v 2c -c public 192.168.127.101 .1.3.6.1.4.1.8691.8.4.6.1.1.3.1.1.4.1", "r"); //input command in format (snmp_function -v version -c community_string IP_address OID)
if (cmd == NULL)
{
perror("_popen");
exit(EXIT_FAILURE);
}
while (fgets(result, sizeof(result), cmd)) //gets full responce string to snmpget and passes string to result variable
{
std::string str = result; //sets string to values in variable result
std::regex rgx("\"([^\"]*)\""); //retrieves only the section of the output string that is between double air quotes
std::smatch match;
std::string buffer;
std::stringstream ss(str);
std::vector<std::string> strings;
//necessary? whitespace
while (ss >> buffer)
strings.push_back(buffer);
for (auto& i : strings)
{
if (std::regex_match(i, match, rgx))
{
std::ssub_match submatch = match[1];
std::string str = submatch.str();
for (size_t j=0; j<str.length(); j++)
{
output[j] = str[j];
}
}
}
}
_pclose(cmd);
The std::string stuff is just to get the part of the return value I want.
I can normally hold the window open by putting in a getchar() at the end of a while loop that contains this, but I don't want to be reliant on the user pressing enter to get an updated result.
I suggest that you use a nested cmd when you execute the first command. Something like:
cmd.exe /c cmd.exe /k "Put the command you want to execute here!"
Im not sure if quoting the command is necessary but I know that it does work with quotes so that would mean you change your first line to:
cmd = _popen("cmd.exe /c cmd.exe /k \"snmpget -v 2c -c public 192.168.127.101 .1.3.6.1.4.1.8691.8.4.6.1.1.3.1.1.4.1\"", "r"); //Note the escaped quotes
The above is untested but I believe it should give you the intended result. Note that this opens an inner cmd each time so with this solution you might want to keep track of the exits. Basically for each command that isn't the first one, you will want to send exit to close that inner->inner cmd.

Passing Arguments Through system() in C++

How would I pass a command line variable though a system() command in c++.
I have tried using:
string i;
i = system("./findName.sh");
i += argv[1];
cout << i;
But when i run this it gives me my condition for wrong number of arguments i have written in my shell script.
This is the output I received when running my program with "./findName brandonw". Which is my executable file ran with the argument i want my shell script to run with.
The arguments you put are:
brandonw
usage: findName.sh [only_one_argument]
Just concatenate it to the command string.
string command = "./findName.sh";
command = command + " " + argv[1];
system(command.c_str());
Just rearrange your code a bit:
string i("./findName.sh ");
i += argv[1];
system(i.c_str());
cout << i;
Also note, that system doesn't return a std::string, but an implementation defined int value.
If you need to deal with ./findName.sh's output, you rather need pipe().

How do I execute a string as a shell script in C++?

I'm writing a program that needs to be able to execute a shell script provided by the user. I've gotten it to execute a single shell command, but the scripts provided will be more complicated than that.
Googling got me as far as the following code snippet:
FILE *pipe;
char str[100];
// The python line here is just an example, this is *not* about executing
// this particular line.
pipe = popen("python -c \"print 5 * 6\" 2>&1", "r");
fgets(str, 100, pipe);
cout << "Output: " << str << endl;
pclose(pipe)
So that this point str has 30 in it. So far so good. But what if the command has carriage returns in it, as a shell script file would, something like the following:
pipe = popen("python -c \"print 5 * 6\"\nbc <<< 5 + 6 2>&1", "r");
With this my goal is that str eventually have 30\n11.
To put another way, assume I have a file with the following contents:
python -c "print 5 * 6"
bc <<< 5 + 6
The argument I'm sending to popen above is the string representation of that file. I want to, from within C++, send that string (or something similar) to bash and have it execute exactly as if I were in the shell and sourced it with . file.sh, but setting the str variable to what I would see in the shell if it were executed there, in this case, 30\n11.
Yes, I could write this to a file and work it that way, but that seems like it should be unnecessary.
I wouldn't think this was a new problem, so either I'm thinking about it in a completely wrong way or there's a library that I simply don't know about that already does this.
use bash -c.
#include <stdio.h>
int main()
{
FILE *pipe = popen("bash -c \"echo asdf\necho 1234\" ", "r");
char ch;
while ((ch = fgetc(pipe)) != EOF)
putchar(ch);
}
Output:
asdf
1234
(I've test on cygwin)

Create a file at a given path using C++ in Linux

I want to create a file at a given path that is relative to the current directory. The following code behaves erratically. I some times see the file created and some times do not. That may be because of the change in the current directory. Here's the code.
//for appending timestamp
timeval ts;
gettimeofday(&ts,NULL);
std::string timestamp = boost::lexical_cast<std::string>(ts.tv_sec);
//./folder/inner_folder is an existing directory
std::string filename = "./folder/inner_folder/abc_"+timestamp+ ".csv";
std::ofstream output_file(filename);
output_file << "abc,efg";
output_file.close();
Now, the problem is the file is created only in some cases. That is when I have as a command line argument an input file from the current directory, it works fine.
./program input_file
If I have something like this, it does not work
./program ./folder1/input_file
I tried giving the full path as an argument for ofstream, I still don't see the files created.
What is the correct way to do this? Thanks
ofstream will not create missing directories in the file path, you must ensure the directories exist and if not create them using OS specific api or boost's file system library.
Always check the result of IO operations, and query system error code to determine reason for failures:
if (output_ file.is_open())
{
if (!(output_file << "abc,efg"))
{
// report error.
}
}
else
{
const int last_error = errno;
std::cerr << "failed to open "
<< filename
<< ": "
<< strerror(last_error)
<< std::endl;
}