How to handle Microsoft strange command line handling of escape character - c++

A third party system calls a program I am writing that takes in a path as the first argument on the command line. An example command line might be something like:
"C:\Program Files (x86)\ProgramX"
Actually this isn't printed how I want in this editor, an example input might be:
"C:\Program Files (x86)\ProgramX\"
The problem is that Windows seems to correctly identify the first and second backslash as backslash characters and not the escape character. But the third backslash is seen as an escape of " and so you end up argv[1] being:
C:\Program Files (x86)\ProgramX"
Is this a bug? What is a reliable way to handle this issue?
Sample code below.
If you run the program: test.exe "C:\Program Files (x86)\ProgramX"
it prints:
>test.exe "C:\Program Files (x86)\ProgramX\"
No. args: 2
arg[0] = test.exe
arg[1] = C:\Program Files (x86)\ProgramX"
path: C:\Program Files (x86)\ProgramX" does not exist
/*
pass in for example
test.exe "C:\Program Files (x86)\ProgramX\"
prints path does not exist
*/
#include <string>
#include <iostream>
#include <windows.h>
bool DirectoryExists(const char* path)
{
DWORD dwAttrib = GetFileAttributes(path);
return (dwAttrib != INVALID_FILE_ATTRIBUTES &&
(dwAttrib & FILE_ATTRIBUTE_DIRECTORY));
}
int main(int argc, char* argv[]) {
std::cout << "No. args: " << argc << std::endl;
for (int i = 0; i < argc; i++) {
std::cout << "arg[" << i << "] = " << argv[i] << std::endl;
}
if (argc > 1) {
std::string path(argv[1]);
std::cout << "path: " << path << (DirectoryExists(path.c_str()) ? " exists" : " does not exist") << std::endl;
}
}

Related

unable to set command line parameters when debugging in visual studio code using cmake

I have set up a cmake project using visual studio 2019. I want to start the debug session with some command line parameters.
To achieve that, I've configured the CMakeLists.txt with the VS_DEBUGGER_COMMAND_ARGUMENTS:
set( VS_DEBUGGER_COMMAND_ARGUMENTS "this" "is" "a" "test" )
set( VS_DEBUGGER_WORKING_DIRECTORY "." )
The first thing my C++ code do is to print out that parameters:
if (argc > 1) {
// config_file = argv[1];
for (std::size_t i = 0; i < argc; i++) {
std::cout << "argument " << i << ": " << argv[i] << std::endl;
}
}
else {
std::cout << "the value of argc is: " << argc << std::endl;
}
The problem is that when I run Debug, the output I've always see is the value of argc is 1. I've also tried to modify the launch.vs.json file as appears in this related question:
Adding command line arguments to project
and it doesn't work. Any ideas?

Taking a filename as an input while running a program I compiled in cpp

In cpp, I need to run a program like this
g++ *.cpp -o out
./out <input.txt> <somenumber>
where input.txt is a text file containing lines of information I need to proccess, and somenumber is an integer value I need to use.
I am searching for hours and couldn't find the answer I was looking for,
I found solutions that work like
./out < input.txt
reads the input.txt line as a string which then in the code I can process,
but the assignment says that the code will be run only and specifically as
./out <input.txt> <somenumber>
can anyone help ?
I have wrote some code, in which I wrote my main as
int main(int argc, char* argv[] ){
but when I run
./out <input.txt>
the terminal gives an error saying
" -bash: syntax error near unexpected token `newline' "
edit: typo
You are correct in wanting to accomplish this using argc and argv. Something like this should work
#include <iostream>
#include <string>
int main(int argc, char* argv[]) {
std::string file_name;
std::string number;
if(argc == 3) {
file_name = argv[1];
number = argv[2];
}
std::cout << "Filename: " << file_name << " number: " << number << "\n";
}
By convention, the use of < > to enclose an input parameter signifies that the parameter is mandatory for the command.
Therefore, the command
./out <input.txt> <some_number>
signifies that the two parameters - input.txt and some_number are mandatory.
The command can be run as:
./out input.txt 101
Here is an example of working code:
#include <iostream>
#include <string>
using namespace std;
int main(int argc, char* argv[]) {
string inputFile;
string someNumber;
if(argc != 3) {
cout << "Sorry! Wrong input \n";
cout << "Usage: ./out <input_file_name> <some_number> \n";
return -1;
}
inputFile = argv[1];
someNumber = argv[2];
cout << "Processing ...\n";
cout << "File = " << inputFile << ", Number = " << someNumber << "\n";
return 0;
}
Output:
$ ./out input.txt 101
Processing ...
File = input.txt, Number = 101

Command Arguments field in Visual Studio 2015

I try to pass some arguments to the program using Visual Studio Community 2015.
I add some input in the Configuration Properties - Debugging - Command Arguments.
#include <iostream>
int main(int args, char* argv[])
{
std::cout << "args:" << args << std::endl;
for (size_t i = 0; i < args; i++)
{
std::cout << "argv[" << i << "]=" << argv[i] << std::endl;
}
}
The output is:
args:1
argv[0]=c:\users\john\documents\visual studio 2015\Projects\ConsoleApplication2\Debug\ConsoleApplication2.exe
Why the input in the "Command Arguments" field is ignored?
Are you sure the build configuration you set the command line for is the one you are running? VS allows different command line arguments to be set for each build (debug/release and platforms by default).

Checking file existence, size and similarity in C++

I am new to C++ and I am trying to do a few things with my code. I have been researching on how to do them but haven't been able to get my head around it and have been fairly unsuccessful.
bool Copy(char filenamein[], char filenameout[]);
int main(int argc, char **argv)
{
if (argc !=3) {
cerr << "Usage: " << argv[0] << " <input filename> <output filename>" << endl;
int keypress; cin >> keypress;
return -1;
}
if (Copy(argv[1], argv[2]))
cout << "Copy completed" << endl;
else
cout << "Copy failed!" << endl;
system("pause");
return 0;
}
bool Copy(char filenamein[], char filenameout[])
{
ifstream fin(filenamein);
if(fin.is_open())
{
ofstream fout(filenameout);
char c;
while(fin.get(c))
{
fout.put(c);
}
fout.close();
fin.close();
return true;
}
return false;
}
This code already creates 2 text files, input.txt and output.txt. Both files also contains the same items/characters.
What I'm trying to do if checking if the input.txt file already exists before trying to copy it.
I am also wanting to check both files to make sure they are the same as well as checking the file sizes are equal.
How do I go about on doing this?
For general filesystem operations there's Boost Filesystem.
http://www.boost.org/doc/libs/1_57_0/libs/filesystem/doc/index.htm
To compare files you can calculate hashes and compare the hashes. For two files it would be just as efficient to compare them character by character but for more than two files comparing hashes wins.
For this there's Crypto++.
http://www.cryptopp.com/
Example of using the two libraries to solve the 3 problems in the question.
// C++ standard library
#include <iostream>
// Boost
#include <boost/filesystem.hpp>
// Crypto++
#include <cryptopp/sha.h>
#include <cryptopp/hex.h>
#include <cryptopp/files.h>
using std::string;
const string file_hash(const boost::filesystem::path &file);
int main( int argc, char** argv) {
if (argc != 3)
{
std::cout << "Usage: " << argv[0] << "filepath1 filepath2\n";
return 1;
}
const string filename1(argv[1]);
const string filename2(argv[2]);
std::cout << "filename 1: " << filename1 << std::endl;
std::cout << "filename 2: " << filename2 << std::endl;
// file existence
const bool file_exists1 = boost::filesystem::exists(filename1);
const bool file_exists2 = boost::filesystem::exists(filename2);
std::cout << "file 1 exists: " << std::boolalpha << file_exists1 << std::endl;
std::cout << "file 2 exists: " << std::boolalpha << file_exists2 << std::endl;
if (!file_exists1 || !file_exists2)
return EXIT_SUCCESS;
// file size
const boost::filesystem::path file_path1(filename1);
const boost::filesystem::path file_path2(filename2);
const uintmax_t file_size1 = boost::filesystem::file_size(file_path1);
const uintmax_t file_size2 = boost::filesystem::file_size(file_path2);
std::cout << "file 1 size: " << std::boolalpha << file_size1 << std::endl;
std::cout << "file 2 size: " << std::boolalpha << file_size2 << std::endl;
// comparing files
const string hash1 = file_hash(file_path1);
const string hash2 = file_hash(file_path2);
std::cout << "hash1: " << hash1 << std::endl;
std::cout << "hash2: " << hash2 << std::endl;
const bool same_file = hash1 == hash2;
std::cout << "same file: " << same_file << std::endl;
}
const string file_hash(const boost::filesystem::path& file)
{
string result;
CryptoPP::SHA1 hash;
CryptoPP::FileSource(file.string().c_str(),true,
new CryptoPP::HashFilter(hash, new CryptoPP::HexEncoder(
new CryptoPP::StringSink(result), true)));
return result;
}
Compilation on my laptop (the directories will of course be specific to wherever you have the headers and libraries but these are how homebrew installs them on OS X):
clang++ -I/usr/local/include -L/usr/local/lib -lcryptopp -lboost_system -lboost_filesystem demo.cpp -o demo
Example usage:
$ ./demo demo.cpp demo.cpp
filename 1: demo.cpp
filename 2: demo.cpp
file 1 exists: true
file 2 exists: true
file 1 size: 2084
file 2 size: 2084
hash1: 57E2E81D359C01DA02CB31621C9565DF0BCA056E
hash2: 57E2E81D359C01DA02CB31621C9565DF0BCA056E
same file: true
$ ./demo demo.cpp Makefile
filename 1: demo.cpp
filename 2: Makefile
file 1 exists: true
file 2 exists: true
file 1 size: 2084
file 2 size: 115
hash1: 57E2E81D359C01DA02CB31621C9565DF0BCA056E
hash2: 02676BFDF25FEA9E3A4D099B16032F23C469E70C
same file: false
Boost Filesystem will throw exceptions if you try to do stuff like get the size of a file that doesn't exist. You should be prepared to catch those exceptions so you don't need to explicitly test for file existence since you should have a catch block anyway. (If all you want to know is if a file exists but you don't want to do stuff with the file then it makes sense to test for existence explicitly.)
This is how I would go about doing these things in practice. If what you're asking is how these things would be done without libraries then you can check if a file exists by using the C or C++ standard library to try and open a file and check if you succeeded. For checking file size, you can open a file, you can seek to the end and compare the position to the beginning of the file.
However, it's preferable to rely on operating system support to interact with filesystems in general.
https://www.securecoding.cert.org/confluence/display/seccode/FIO19-C.+Do+not+use+fseek%28%29+and+ftell%28%29+to+compute+the+size+of+a+regular+file
fstat() for example is specific to Unix and Unix-like systems and returns a struct containing file size data but on Microsoft systems you use GetFileSizeEx() to get a file size. Because of this, if you want a portable solution then you have to use libraries that interact with the various operating systems for you and present a consistent API across operating systems.
Comparing files using only standard library support can be done by either implementing hashing functions or comparing files character by character.
Look at fstat, it will tell you the file size (or return an error if it does not exist).
You could also force the last update date of the copied file to be the same as the source file, so that if the source file changes but keeps the same size you will notice it (look at futimes to do so).

Trying to execute .exe from std::cout

I have a C++ program that reads a config file and gets the directories.
What I want to do now is to execute an .exe program using the directory settings from the config file.
Here is a piece of my code:
int main(){
ConfigFile cfg("htbaseconfig.properties");
bool exists = cfg.keyExists("backuplocation");
exists = cfg.keyExists("logdir");
exists = cfg.keyExists("execdir");
exists = cfg.keyExists("fulldir");
exists = cfg.keyExists("incdir");
exists = cfg.keyExists("appdir");
std::string bkploc = cfg.getValueOfKey<std::string>("backuplocation");
std::cout << "Backup Location: " << bkploc << "\n";
std::string bkplogdir = cfg.getValueOfKey<std::string>("logdir");
std::cout << "Log Location: " << bkplogdir << "\n";
std::string bkpexec = cfg.getValueOfKey<std::string>("execdir");
std::cout << "Exec Directory: " << bkpexec << "\n";
std::string bkpfulldir = cfg.getValueOfKey<std::string>("fulldir");
std::cout << "Full Directory: " << bkpfulldir << "\n";
std::string bkpappdir = cfg.getValueOfKey<std::string>("appdir");
std::cout << "Real app Directory: " << bkpappdir << "\n\n\n";
for( ; ; ) {
Sleep(6000);
ShellExecute(NULL, L"open", , L"C:\\teste.htm", NULL,SW_SHOWNORMAL);
}
std::cin.get();
return (0);}
Inside the ShellExecute, I wanted to execute the following line parsing the config options:
$execdir/program.exe $logdir/log.txt $bkpappdir $bkploc
How do I do this? I want to execute my program with the variables I get on std::cout.
You must pass to ShellExecute, instead of the second NULL, a string (c string, a char[]) that contains all parameters, like if you are passing them to the command line.
So Will be something like
ShellExecute(NULL, L"open", , L"C:\\teste.htm", "option=param option2=param2",SW_SHOWNORMAL);
Depends on how you parse them (or how they are parsed) from the other exe file