C++ Parse command line parameters and bool - c++

So, I'm trying to add something that allows me to use argv to allow three command line inputs.
such that:
./program input.dat (string input)
so that (I assume) argv[0] = input.dat and argv[1] = string input, argue[2] = file output
...I'm not sure if I'm explaining it right, but this is my best effort.
what I want to do is have a command line input that allows me to have like, if it says "encrypt" it makes a bool true, and if I type "decrypt" it sets that bool to false.

bool encrypt;
std::string action(argv[2]);
if (action == "encrypt") {
encrypt = true;
} else if (action == "decrypt") {
encrypt = false;
} else {
// Report invalid argument
}

To do what you're describing:
int main(int argc, char** argv) {
if (argc < 3) {
// print usage here and return, since that's what you need.
}
const char* filename = argv[1];
if (!strcmp(argv[2], "encrypt")) {
// encrypt!
}
else if (!strcmp(argv[2], "decrypt")) {
// decrypt!
}
else {
// error!
}
}
Or alternatively with strings:
std::string filename = argv[1];
std::string mode = argv[2];
if (mode == "encrypt") {
// etc.
}
You can have lots of args, There's nothing to stop you from calling:
./program hi everybody this is a little excessive but just an example
At which point, you'd get called with argc == 12 and, e.g., argv[5] pointing to `"a"``.

Related

Creating a history function for a Unix Shell

Here is what my program currently looks like. I have to add history functionality that gets stored in a file 'mysh.history'. Currently I expect my output to simply append each user command in my shell to the file.
first line of output
first line of output
It only appends the first input into the shell instance. I think my problem lies with my understanding of the fork() process but I'm not sure what is going on. Any suggestions?
#define MYSH_BUFFERSIZE 64
#define MYSH_DELIM " \t\n"
fstream file;
// custom function declarations
int mysh_exit(char **args);
int mysh_add_history(char **args);
int mysh_history(char **);
char byebye[] = "byebye";
char exit_program[] = "exit";
char history[] = "history";
// contains names of all custom shell commands implemented
char *lookup_str[] = {byebye, exit_program, history};
// holds references to all commands in lookup_str[]
// order or commands must match each other
int (*lookup_func[])(char **) = {
&mysh_exit,
&mysh_exit,
&mysh_history
};
/* custom shell commands implementations BEGIN*/
// Without the argument, it prints out the recently typed commands (with their
// arguments), in reverse order, with numbers
// If the argument “-c” is passed, it clears the list of recently typed commands.
void clear_history()
{
file.close();
file.open("mysh.history", ios::trunc);
}
int mysh_add_history(char *line)
{
// if exists then append to the history
if (access("mysh.history", F_OK) == 0)
{
file.open("mysh.history", ios::app);
}
// otherwise create mysh.history and start writing
else
{
file.open("mysh.history", ios::out);
}
file << line << "\n";
return 0;
}
int mysh_history(char **)
{
return 0;
}
int mysh_exit(char **args)
{
return 0;
}
int num_commands()
{
return sizeof(lookup_str) / sizeof(char *);
}
/* custom shell functions END*/
/* main shell processes BEGIN*/
// returns the tokens (arguments) array after tokenizing line from mysh_read_line()
char **mysh_split_args(char *line)
{
int buffer_size = MYSH_BUFFERSIZE;
int current_pos = 0;
char **tokens = (char **)malloc(buffer_size * sizeof(char *));
char *tok;
if (!tokens)
{
printf("mysh: memory allocation error\n");
exit(EXIT_FAILURE);
}
tok = strtok(line, MYSH_DELIM);
while (tok != NULL)
{
tokens[current_pos] = tok;
current_pos++;
if (current_pos >= buffer_size)
{
buffer_size += MYSH_BUFFERSIZE;
tokens = (char **)realloc(tokens, buffer_size * sizeof(char *));
if (!tokens)
{
printf("mysh: memory allocation error\n");
exit(EXIT_FAILURE);
}
}
tok = strtok(NULL, MYSH_DELIM);
}
tokens[current_pos] = NULL;
return tokens;
}
// mysh_read_line allocates MYSH_BUFFER_SIZE of memory to the intial buffer
// it reallocates memory as needed with getLine() function
// returns line to be processed and tokenized by mysh_split_args()
char *mysh_read_line(void)
{
char *line = NULL;
size_t buffersize = 0;
// getLine() also needs to check for EOF after in the case of text files being read.
if (getline(&line, &buffersize, stdin) == -1)
{
if (feof(stdin))
{
exit(EXIT_SUCCESS);
}
else
{
printf("failed to read line\n");
exit(EXIT_FAILURE);
}
}
return line;
}
// args passed comes from mysh_split_args()
int mysh_launch_process(char **args)
{
pid_t pid;
pid_t wpid;
int state;
pid = fork();
// if we enter child process
if (pid == 0)
{
if (execvp(args[0], args) == -1)
{
printf("error in mysh\n");
}
exit(EXIT_FAILURE);
}
// forking failed
else if (pid < 0)
{
printf("error in mysh\n");
}
else
{
// if we enter parent process
do
{
wpid = waitpid(pid, &state, WUNTRACED);
} while (!WIFEXITED(state) && !WIFSIGNALED(state));
}
return 1;
}
// calls mysh_launch_process() and handles programs being called
int mysh_execute(char **args)
{
int i;
if (args[0] == NULL)
{
return 1;
}
for (i = 0; i < num_commands(); i++)
{
if (strcmp(args[0], lookup_str[i]) == 0)
{
if (strcmp(args[0], "history") == 0 && strcmp(args[1], "-c"))
{
clear_history();
}
return (*lookup_func[i])(args);
}
}
return mysh_launch_process(args);
}
void mysh_loop(void)
{
char *line;
char **args;
int state;
do
{
printf("# ");
line = mysh_read_line();
mysh_add_history(line);
args = mysh_split_args(line);
state = mysh_execute(args);
free(line);
free(args);
} while (state);
}
int main(int argc, char **argv)
{
// run main program loop
mysh_loop();
file.close();
return EXIT_SUCCESS;
}
/* main shell processes END*/```

Enabling C code to run as C++ code

I have a C program that finds duplicate files within a directory. The program is executed on the command line and passed 2 arguments. One is the parent directory, and argument two is the file name. It is working code in c, but I have a GUI and other files for "microservices" written in c++.
How would one call this C code from a c++ file?
#include<stdio.h>
#include<dirent.h>
#include<sys/stat.h>
#include<errno.h>
#include<string.h>
#include<stdlib.h>
#include<fcntl.h>
//Compile: gcc dreamduplicatefinder.c -o dreamduplicatefinder.exe
//Run: ./dreamduplicateFinder.exe parent_dir filename...
#define false 0
#define true 1
int duplicateCount = 0;
int FindDuplicates(char* path, char* fileName);
int CompareFiles(char* originalFile, char* currFile);
int main(int argc, char *argv[])
{
//Two additional arguments are expected: Parent dir, file to find duplicates of...
if (argc != 3)
{
printf("Usage: %s 'Base Directory' 'File Name'\n", argv[0]);
return -1;
}
//argv[1] = base dir, argv[2] = file to find duplicates of; e.g argv[1] = /home,
//argv[2] = "file.txt"...
FindDuplicates(argv[1], argv[2]);
printf("\n\nFound %d duplicate(s)\n", duplicateCount);
return 0;
}
int FindDuplicates(char* path, char* fileName)
{
DIR *dir;
struct dirent *dp;
struct dirent *result;
struct stat statp;
char absoluteFilePath[255];
if ((dir = opendir(path)) == NULL)
{
//printf(dir); //error could becuase trying to open shortcut or corrupt folder.
printf("%s\n",path);
perror("Failed to open directory");
return -1;
}
while ((dp = readdir(dir)) != NULL)
{
//readdir returns . and .. which we should ignore...
if (strcmp(dp->d_name, ".") && strcmp(dp->d_name, ".."))
{
//find file full path, relative to base path. e.g, a /home/file.txt...
//copy path to absoluteFilePath...
strcpy(absoluteFilePath, path);
//append / at end...
strcat(absoluteFilePath, "/");
//append filename to path...
strcat(absoluteFilePath, dp->d_name);
//check if the current file is actually file or dir...
stat(absoluteFilePath, &statp);
if (S_ISDIR(statp.st_mode)) //is a directory...
{
//recurse through this dir...
FindDuplicates(absoluteFilePath, fileName);
}
else if (S_ISREG(statp.st_mode)) //is a file...
{
//check for duplicates here...
//compare current file with the file specified by user...
if (strcmp(fileName, absoluteFilePath))
{
if (CompareFiles(fileName, absoluteFilePath))
{
//yes, duplicate; print it...
printf("%s\n", absoluteFilePath);
duplicateCount++;
}
}
} //end else if (S_ISREG(statp.st_mode))...
} //if (strcmp(dp->d_name, ".") && strcmp(dp->d_name,".."))...
} //end while...
closedir(dir);
return 0;
}
int CompareFiles(char* originalFile, char* currFile)
{
//two step comparison: (1) first check size; if not same, return false.
//If equal, (2) compare file content.If equal, return true, false otherwise...
struct stat statOriginal, statCurr;
stat(originalFile, &statOriginal);
stat(currFile, &statCurr);
//Step 1...
if ((int)statOriginal.st_size != (int)statCurr.st_size) //size not same...
return false;
//Step 2...
//size matches, files can be same; confirm it by matching both file contents...
int fdOriginal = open(originalFile, O_RDONLY);
int fdCurr = open(currFile, O_RDONLY);
if (fdOriginal == -1 || fdCurr == -1)
return false; //error occurred, not sure if file is duplicate...
//we will read file in small chunks and compare...
int chunkSize = 1024, bytesRead;
char *bufferOriginal = (char*)malloc(chunkSize * sizeof(char));
char *bufferCurr = (char*)malloc(chunkSize * sizeof(char));
while (true)
{
//read file in chunk...
bytesRead = read(fdOriginal, bufferOriginal, chunkSize);
if (bytesRead <= 0)
break; //end of file...
bytesRead = read(fdCurr, bufferCurr, bytesRead);
//compare buffer...
if (strcmp(bufferOriginal, bufferCurr)) //if content not matching...
return false;
}
return true;
}
My errors include: (from compareFiles function)
2x 'open' identifier not found
2x 'read' identifier not found
The working code for those curious.
Thank you #MarcusMüller & #JesperJuhl
#include "stdafx.h" //there is nothing in this header
#include<stdio.h>
#include<dirent.h>
#include<sys/stat.h>
#include<errno.h>
#include<string.h>
#include<stdlib.h>
#include<fcntl.h>
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
//Compile: gcc <name of this file>.cpp -o <nameOfThisFile>.exe
//Run: <nameOfThisFile> parent_dir filename...
#define false 0
#define true 1
int duplicateCount = 0;
int FindDuplicates(char* path, char* fileName);
int CompareFiles(char* originalFile, char* currFile);
int main(int argc, char *argv[])
{
//Two additional arguments are expected: Parent dir, file to find duplicates of...
if (argc != 3)
{
printf("Usage: %s 'Base Directory' 'File Name'\n", argv[0]);
return -1;
}
//argv[1] = base dir, argv[2] = file to find duplicates of; e.g argv[1] = /home,
//argv[2] = "file.txt"...
FindDuplicates(argv[1], argv[2]);
printf("\n\nFound %d duplicate(s)\n", duplicateCount);
return 0;
}
int FindDuplicates(char* path, char* fileName)
{
DIR *dir;
struct dirent *dp;
struct dirent *result;
struct stat statp;
char absoluteFilePath[255];
if ((dir = opendir(path)) == NULL)
{
//possibly trying to open shortcut or corrupt folder typically.
printf("Failed to open directory %s \n",path);
return -1;
}
while ((dp = readdir(dir)) != NULL)
{
//readdir returns . and .. which we should ignore...
if (strcmp(dp->d_name, ".") && strcmp(dp->d_name, ".."))
{
//find file full path, relative to base path. e.g, a /home/file.txt...
//copy path to absoluteFilePath...
strcpy(absoluteFilePath, path);
//append / at end...
strcat(absoluteFilePath, "/");
//append filename to path...
strcat(absoluteFilePath, dp->d_name);
//check if the current file is actually file or dir...
stat(absoluteFilePath, &statp);
if (S_ISDIR(statp.st_mode)) //is a directory...
{
//recurse through this dir...
FindDuplicates(absoluteFilePath, fileName);
}
else if (S_ISREG(statp.st_mode)) //is a file...
{
//check for duplicates here...
//compare current file with the file specified by user...
if (strcmp(fileName, absoluteFilePath))
{
if (CompareFiles(fileName, absoluteFilePath))
{
//yes, duplicate; print it...
printf("This is a duplicate! %s\n", absoluteFilePath);
duplicateCount++;
}
}
} //end else if (S_ISREG(statp.st_mode))...
} //if (strcmp(dp->d_name, ".") && strcmp(dp->d_name,".."))...
} //end while...
closedir(dir);
return 0;
}
int CompareFiles(char* originalFile, char* currFile)
{
//two step comparison: (1) first check size; if not same, return false.
//If equal, (2) compare file content.If equal, return true, false otherwise...
struct stat statOriginal, statCurr;
stat(originalFile, &statOriginal);
stat(currFile, &statCurr);
//Step 1...
if ((int)statOriginal.st_size != (int)statCurr.st_size) //size not same...
return false;
FILE* fdOriginal;
if (fdOriginal = fopen(originalFile, "r")) {
if (fdOriginal == NULL) { fputs("File error", stderr); return false; }
}
else return false; //error occurred, not sure if duplicate
FILE* fdCurr;
if (fdCurr = fopen(currFile, "r")) {
if (fdCurr == NULL) { fputs("File error", stderr); return false; }
}
else return false;
int chunkSize = 1024, objsRead;
char *bufferOriginal = (char*)malloc(chunkSize * sizeof(char));
if (bufferOriginal == NULL) { fputs("Memory error for buff orig", stderr); exit(2); }
char *bufferCurr = (char*)malloc(chunkSize * sizeof(char));
if (bufferCurr == NULL) { fputs("Memory error for buff curr", stderr); exit(2); }
while (true)
{
//read file in chunk...
//std::size_t fread( void* buffer, std::size_t size, std::size_t count, std::FILE* stream );
objsRead = fread(bufferOriginal, sizeof(char), chunkSize , fdOriginal);
if (objsRead <= 0)
break; //end of file...
objsRead = fread(bufferCurr, sizeof(char), objsRead, fdCurr);
//compare buffer...
if (strcmp(bufferOriginal, bufferCurr)) //if content not matching...
return false;
}
return true;
}
You usually just wouldn't do that. You'd wrap it in a C function, and compile it to an object file.
Then you'd include your C header with extern "C" {…}, and just call that function from C++.
When building your executable, you'd link in the object file containing your C function. Done!
Note: C isn't C++, and albeit your code not being illegal in C++ (as far as I can instantly tell), it does very "ugly" things (like #defineing true and false – ugh, that would already be a bad idea in C, to be honest). So, deal with it like you would deal with code in Fortran, or Java, or any other language that has a calling convention that you can use from C++ (which, usually, is the C calling convention): Just use it as an extern object.
Using ::open and ::read should cause the functions to be found.
You may also want to replace the C headers (like "string.h") with their C++ equivalent versions (like "cstring").
Your defines for true and false should also go. In C++ those are proper bools, not integers. This means the return type of CompareFiles should be changed to bool.
And you should wrap duplicateCount in an anonymous namespace - or return it from the function that updates it (either by returning a small struct with two ints, or by using a std::pair or std::tuple) - global variables are evil.

C++ find in file a specific string which contains name

So my goal is to create a console app using visual C++ lang which opens a file ( in my case it is a MIME file) and finds a certain string. In my case it sounds - Content-Disposition: attachment; filename="file.smth".
And then the app shows the file.smth
So here is what I have done. It has some problems that I am not able to find.When I run a console app It gets stuck at finding a filename.
#include "stdafx.h"
#include <stdlib.h>
#include <string.h>
bool ShowFile(char * FileName, char * Name)
{
FILE* file;
if (fopen_s(&file, FileName, "rt") != 0) { return false; }
while (!feof(file))
{
char AttName[100];
int a = sscanf_s("Content-Disposition: attachment; filename=\"%[^\"]\"", AttName,_countof(AttName));
Name = AttName;
}
fclose(file);
return true;
}
int main(int argc, char* argv[])
{
char FileName[100];
if (argc == 2) strcpy_s(FileName, _countof(FileName), argv[1]);
else {
printf("Source file name: "); gets_s(FileName, _countof(FileName));
}
char Name[100];
ShowFile(FileName, Name);
printf("%s \n", Name);
system("pause");
return 0;
}
Thank you for your attention!
The function ShowFile can be improved.
Suggestion 1
If you would like the function to return the name of the file (file.smth) by the second argument, change it to std::string&.
//bool ShowFile(char * FileName, char * Name)
bool ShowFile(char * FileName, std::string& Name)
As the function stands right now, you are changing Name in the function to point to a local variable. But that has no impact on the calling function. The line
Name = AttName;
is completely useless.
Suggestion 2
You haven't added any code to read the data from the file. The line
int a = sscanf_s("Content-Disposition: attachment; filename=\"%[^\"]\"", AttName,_countof(AttName));
has the format specification as the first argument, not the string from which to read the data. It is missing the source string.
You need to add code to read lines of text and try to extract AttName from those lines.
Suggestion 3
Don't use while (!feof(file)). See Why is “while ( !feof (file) )” always wrong?.
You need something like:
char line[200];
while ( fgets(line, sizeof(line), file) )
{
char AttName[100];
int a = sscanf_s(line, "Content-Disposition: attachment; filename=\"%[^\"]\"", AttName,_countof(AttName));
...
}
Suggestion 4
Always check the returned value of scanf family of functions to make sure that the function was able to assign data to the variables. Don't assume it succeeded.
int a = sscanf_s(line, "Content-Disposition: attachment; filename=\"%[^\"]\"", AttName,_countof(AttName));
// Name = AttName;
if ( a == 1 )
{
Name = AttName;
}
Suggestion 5
Add a flag to indicate that Name was successfully read.
Revised function
bool ShowFile(char * FileName, std::string& Name)
{
bool status = false;
FILE* file;
if (fopen_s(&file, FileName, "rt") != 0) { return false; }
char line[200];
while (fgets(line, sizeof(line), file))
{
char AttName[100];
int a = sscanf_s(line, "Content-Disposition: attachment; filename=\"%[^\"]\"", AttName,_countof(AttName));
if ( a == 1 )
{
Name = AttName;
// Got what we are looking for.
// Set the status and break out of the loop.
// There is no need to look for Name any more.
status = true;
break;
}
}
fclose(file);
return status;
}

Weird string result

My program should open a file, the file path is retrieved from the command line using argv[1].
I then try to open the file using fopen but my program crashes because the filepath I use doesn't contain double backslashes so fopen doesn't work.
I've tried to write my own convert function and using print to check the result looked good at first sight.
The problem is that when I use the returned const char* as argument it gives me a weird result.. my code:
#include <stdio.h>
#include <stdlib.h>
#include <string>
const char* ConvertToPath(std::string path)
{
std::string newpath = "";
for(unsigned int i = 0; i < path.length(); ++i)
{
if(path[i] == '\\')
{
newpath += "\\\\";
}
else
{
newpath += path[i];
}
}
printf("%s \n", newpath.c_str());
return newpath.c_str();
}
bool OpenDBC(const char* path)
{
const char* file = ConvertToPath(path);
printf("%s \n", file);
FILE* dbc = fopen(file, "rbw");
if (!dbc)
return false;
return true;
}
int main(int argc, char* argv[])
{
if (argc < 2)
{
printf("Error, expected DBC file.");
getchar();
return -1;
}
if (!OpenDBC(argv[1]))
{
printf("There was an error opening the DBC file.");
getchar();
return -1;
}
getchar();
return 0;
}
Opening a DBC file with my program gives me the following result:
D:\\Achievement.dbc
a
So it looks like const char* file only contains 1 char of the file path, why?
You do not need the ConvertToPath function at all. Double backslashes are only needed in string literals. Never in variables such as a std::string.
I compiled your code on Linux and can not replicate your result
Running ./filereader "D:\\Achievement.dbc" results in
D:\\Achievement.dbc
D:\\Achievement.dbc
Running ./filereader "D:\\\\Achievement.dbc" results in
D:\\\\Achievement.dbc
D:\\\\Achievement.dbc
The later is what you want because command line arguments need to be escaped. Then you can remove the ConvertToPath.

checking conditions on char * filename

I want to give the user the option to specify one file (here it is ground_truth_filename). If he does not specify the option, I want to make assumptions on the default filename.
However, I am not able to check if the ground_truth_filename is NULL or zero even though I initialized it as 0 in the main program. If the argument is passed by user I assign ground_truth_filename to that argument. But the check ground_truth_filename == 0 gives me an assert error.
Any help will be appreciated.
int processFile(const char *filename,
YAML::Emitter &out_yaml,
char *ground_truth_filename)
{
std::cout << "Here" << std::endl;
if (ground_truth_filename == 0)
sprintf(ground_truth_filename,"%s.yaml",filename);
std::ifstream imgstrm(filename, std::ios::binary | std::ios::in);
if (imgstrm.bad() || !imgstrm.is_open())
{
fprintf(stderr, "Failed to open file: %s\n", filename);
return FILE_ERROR;
}
// get ground truth
std::ifstream ground_truth_stream(ground_truth_filename);
if (!ground_truth_stream.is_open())
{
fprintf(stderr, "Failed to open file: %s\n", ground_truth_filename);
return FILE_ERROR;
}
}
Here is how the function is called. Perhaps I should initialize ground_truth_filename = '\0'?
char *ground_truth_filename = 0;
for (int i = 1; i + 1 < argc; i += 2) {
if (!strcmp(argv[i], "--snapshot-markup")) {
ground_truth_filename = argv[i + 1];
markupFlag = true;
}
}
processFile(filename, out_yaml, ground_truth_filename)
The first argument of sprintf must be a pointer to a buffer large enough to hold the output. A NULL pointer or a pointer to a smaller char buffer (e.g. the string literal "") will cause a crash.
Code that would work is:
char buf[256];
if (ground_truth_filename == NULL) {
int charsneeded = snprintf(buf,sizeof(buf),"%s.yaml",filename);
if (charsneeded >= sizeof(buf)) {
return FILE_ERROR; // filename too long
}
ground_truth_filename = buf;
}
The point is that buf gives the new filename a place in memory to live. Strings need that.
Edit: added the charsneeded thing to protect you from too-long filenames as a safety measure. If you actually expect to need it, dynamically allocate buf instead.
You need to allocate ground_truth_filename before you can copy characters into it.
if (ground_truth_filename == 0)
{
int length = strlen(filename) + strlen(".yaml") + 1;
ground_truth_filename = new char[length];
sprintf(ground_truth_filename,"%s.yaml",filename);
}