This is a homework assignment first off.
We have to create a "common application-programming model in UNIX/Linux known as the filter".
I'm stuck on reading the input passed through as arguments (it's all I ever seem to have trouble on).
For example, the cmd is open and the following line is entered:
program -isomebinaryfile.bin
I need to determine what the first letter is after the hyphen (-) and so on and so forth.
Is scanf what I would be using? My main is set up to be able to accept arguments:
int main (int argc, char *argv[])
{
FILE *inf = NULL;
char *arg = argv[0];
}
Can someone give me a little help?
Unless your assignment is only to handle processing of arguments, you may want to look up getopt - it's a standard library parser for arguments.
As for the meat of your question, there are a lot of options, and you could use sscanf as part of it, but you don't have to.
To parse the one argument you mentioned, you need to do the following: check if the argument begins with -i, grab the data out of the argument.
The easiest way to check if the argument begins with -i is:
if (argv[1][0] == '-' && argv[1][1] == 'i')
Alternatively, if you have a lot of argument options, all beginning with '-', you may want something like:
char * i = NULL;
char * o = NULL;
char * s = NULL;
for (int i = 1; i < argc; ++i) {
if (argv[i][0] == '-') {
switch(argv[i][1]) {
case 'i':
i = argv[i][2];
break;
case 's':
s = argv[i][2];
break;
case 'o':
o = argv[i][2];
break;
default:
cerr << "Unknown option: " << argv[i][1];
}
} else {
cerr << "Error: all options must begin with '-'";
}
Note, I'm using argv[1], not 0. argv[0] is always the name of the executable.
The fastest way to extract the rest of the argument is simple pointer arithmetic:
char * filename = argv[1] + 2; // (Or you could equivalently say = &argv[1][2]
This is most efficient - it reuses the strings that are already in argv. If you're planning on changing the strings around, you'd do better with strcpy:
char * filename = (char *)malloc(strlen(argv[1]) - 2);
strcpy(filename, argv1 + 2);
// and eventually you'd have to free(filename)...
Play around and experiment with all the string functions. You'll find them essential to all of your later programs.
You need to use getopt. The manual page has an example.
argv is an array of strings. You can loop over them like
for (int i=1; i<argc; i++) { // skip argv[0] as that's this program's name
const char* arg = argv[i];
}
Once you have the string for a particular argument, you can use the string manipulation functions from <string.h>
if (arg[0] == '-' && strlen(arg) > 0) {
arg++; // advance past the leading '-'
if (strcmp(arg, "command_one") == 0) {
// handle command_one
}
else if (strcmp(arg, "command_one") == 0) {
....
else {
printf("Error: unexpected command %s\n", arg);
}
Get arguments from argv ("argument vector"). In your example, argc and argv will be as follows:
argc == 2
argv[0] == "cmd"
argv[1] == "-isomebinaryfile.bin"
Finding the first letter after the hyphen in argv[1] is a simple loop.
Related
I have below code where I am passing address of array of charectors to function "GetStr". GetStr function prototype is fixed.
Here in below code I am passing command line argument where string to function "GetStr" and update the value in the address of variable "str".
PRoblem:
At Print 1, I am getting the value what I passed from command line arguments. A Print 2 in main function, I am unable to print or get the value back. Could you please let me know how to print the value at "Print 2" statement
int GetStr(void *RetVal, int argc, char **argv)
{
if(argc != 0){
if(argv != NULL && argv[1] != '\0'){
RetVal = argv[1];
}
else{
RetVal = '\0';
}
cout<< " RetVal: " << (char *)RetVal <<endl; ->>> PRINT 1
}
else{
return -1;
}
return 0;
}
Driver:
int main(int argc, char **argv)
{
char str[8];
GetStr(str,argc,argv);
cout<<"HH: "<<((char *)(str))<<endl; ->>> PRINT 2
}
You have to use strcpy() to copy a string. You're just assigning the address of argv[1] to the local variable RetVal, which has no effect on the array in the caller.
You should also check argc to determine if argv[1] has been supplied.
int GetStr(void *RetVal, int argc, char **argv)
{
if(argc != 0){
if(argv != NULL && argc >= 2){
strcpy((char*)RetVal, argv[1]); // copy first argument
}
else{
*(char*)RetVal = '\0'; // Set to empty string
}
cout<< " RetVal: " << (char *)RetVal <<endl; ->>> PRINT 1
}
else{
return -1;
}
return 0;
}
Note that it's necessary to cast RetVal before indirecting through it in the assignment, because you can't dereference a void pointer.
The parameter RetVal is a local variable of the function GetStr. Changes of it do not influence on the original argument passed to the function. Moreover as the original argument is an array then arrays do not have the assignment operator. You have to copy characters pointed to by argv[1] to the passed array.
So in any case this statement
RetVal = argv[1];
and this
RetVal = '\0';
have no effect relative to the passed array. And it is unclear what you are trying to do in the second statement above. Either you want to set the pointer to NULL. Or you want to set the first pointed byte to the terminating zero character that is invalid because the pointer is not dereferenced and moreover has the type void *.
Also it seems that instead of this expression in the if statement
argv[1] != '\0'
you mean either
argv[1][0] != '\0'
or
argv[1] != nullptr
In any case these two if statements
if(argc != 0){
if(argv != NULL && argv[1] != '\0'){
can be substituted for one if statement as
if ( not ( argc < 2 ) )
The function can look at least the following way
int GetStr( void *RetVal, int argc, char **argv )
{
int error = argc < 2 ? -1 : 0;
if ( not error )
{
strcpy( RetVal, argv[1] );
}
return error;
}
The function should not output anything except for the debug purpose. It is the caller of the function will decide whether to output a message if any.
Pay attention to that the function is unsafe because the passed array can be less than it is required to copy the string pointed to by argv[1].
I'm learning to code and i want to do the following:
User runs program through CMD Ex:
cd C:\Program.exe P1 P2
The program will then complete its purpose. However I want to validate so if the user does the following certain responses occur:
P1 = Not a digit, or a +,-,e,E are the first or last character in argv[1] which returns "error".
P2 = The first character is not X and the second character is less than 2 or greater than 16. Which will return the response "error"
Cheers
You can easily check your arguments as they are simply strings. Storing the result as a std::string allows you to perform many operations to test your input. I've heavily commented the below code to help you understand the checks.
Everytime an error is occured, an error message is printed using fprintf to stderr. You should always print your errors to this stream as opposed to printing to stdout. This is because standard error is still seen by users without interfering with things that store the output from programs.
If the program does not return during checks, then your program can function knowing that your input is good.
#include <string>
int main (int argc, char *argv[]) {
//Validate
//If incorrect number of arguments, print error
if (argc != 3) { //3 as program name is argument, ie argv[1]
fprintf(stderr, "Not enough arguments");
return 1;
} else { //Check arguments
//Store arguments as strings to allow easy operations
std::string p1 = argv[1];
std::string p2 = argv[2];
//Check P1
if (! (p1.find_first_not_of( "0123456789" ) == std::string::npos) //if p1 is not made of 0-9
&& (p1.front() != '+' ) && (p1.front() != '-' ) //and the front char of p1 is none of the allowed characters
&& (p1.front() != 'e' ) && (p1.front() != 'E' ) ) //...then we have an error
{
fprintf(stderr, "Not a digit, or a +,-,e,E are the first or last character");
return 1;
}
//Check P2
std::string numeral = p2.substr(1, p2.length()); //store everything but first char into new string
int n = strtol(numeral.c_str(), NULL, 10); //use strtol to convert this to a number
if (p2.front() != 'X' //if front is not X, we have error
|| !numeral.find_first_not_of( "0123456789" ) == std::string::npos //or if substring is not a number, we have an error
|| (n < 2 || n >16) ) //or if n is either less than 2 or greater than 16, we have an error
{
fprintf(stderr, "The first character is not X and the second character is less than 2 or greater than 16");
return 1;
}
}
//program body here....
return 0;
}
int
main(int argc,char **argv){
for (argc--, argv++; argc > 0; argc -= argCount, argv += argCount) {
argCount = 1;
switch (argv[0][1]) {
case 'q':
testnum = atoi(argv[1]);
argCount++;
break;
default:
testnum = 1;
break;
}
}
//...............
my question is what does the argv[0][1] mean and the condition in for() confused me i mean for (argc--, argv++; argc > 0; argc -= argCount, argv += argCount)
//thanks guys....**argv[0][1] should be argv[0][1],thats my mistake not the code writers.
That code doesn't look correct. **argv[0][1] tries to dereference a char.
argv[0][1] would make sense, and means "take the second char of the first char* in argv." IMHO, the code is trying to detect a -q command-line flag (and subsequently setting testnum to the int version of the next argument, blindly assuming it is present), but it's skipping checking for the -, and blindly assuming it's there, and no other arguments would ever have q as a second character.
This code needs to be refactored. Here's one way:
int main(int argc, char **argv) {
int testnum = 1;
for (int argi = 1; argi < argc; ++argi) {
if(argv[argi][0] == '-') {
switch (argv[argi][1]) {
case 'q':
if(argi + 1 == argc || argv[argi + 1][0] == '-') {
/* Handle missing argument error. */
return 1;
}
testnum = atoi(argv[++argi]);
break;
default:
/* Handle unrecognized flag error. */
return 1;
}
}
else
{
/* Handle non-flag parameter. */
}
/* Continue with program. */
return 0;
}
argv[0] represents the name of the program as it was invoked on the command line. If you typed ./myprogram --help, then argv[0] would be "./myprogram".
argv[0][1] will be the second character of that string, '/' in the example above.
Let’s see that for (argc--, argv++; argc > 0; argc -= argCount, argv += argCount):
It initializes the loop by doing argc-- then argv++ (argv now points to the second user parameter string) and argc declares an argument less.
The loop is for all arguments argc>0, and at every iteration, the number of treated arguments argCount is taken off the number of all arguments argc. That makes sense.
However switch (**argv[0][1]) doesn’t make any sense, argv[0][1] is a char, as seen before, not a pointer, so it cannot be dereferenced.
This code looks very crazy. I guess you intended to do the following:
int main(int argc,char **argv){
char** p = argv + 1; // skipping program name
while (*p != 0) { // over all parameters
testnum = 1;
if (*p[1] == 'q') { // skipping - of "-q", not a good idea
p ++;
if (*p != 0) { // may be null, read testnum from parameter,
// ?? no check whether this is an integer at all
testnum = atoi(*p);
}
}
}
(not tested, may not compile nor work)
i need to create a function that will accept a directory path. But in order for the compiler to read backslash in i need to create a function that will make a one backslash into 2 backslash.. so far this are my codes:
string stripPath(string path)
{
char newpath[99999];
//char *pathlong;
char temp;
strcpy_s(newpath, path.c_str());
//pathlong = newpath;
int arrlength = sizeof(newpath);
for (int i = 0; i <= arrlength ;i++)
{
if(newpath[i] == '\\')
{
newpath[i] += '\\';
i++;
}
}
path = newpath;
return path;
}
this code receives an input from a user which is a directory path with single backslash.
the problem is it gives a dirty text output;
int arrlength = sizeof(newpath); causes the size of your entire array (in chars) to be assigned to arrlength. This means you are iterating over 99999 characters in the array, even if the path is shorter (which it probably is).
Your loop condition also allows goes one past the bounds of the array (since the last (99999th) element is actually at index 99998, not 99999 -- arrays are zero-based):
for (int i = 0; newpath[i]] != '\0'; i++)
Also, there is no reason to copy the string into a character array first, when you can loop over the string object directly.
In any case, there is no need to escape backslashes from user input. The backslash is a single character like any other; it is only special when embedded in string literals in your code.
In this line:
if(newpath[i] = '\\')
replace = with ==.
In this line:
newpath[i] += '\\';
This is supposed to add a \ into the string (I think that's what you want), but it actually does some funky char math on the current character. So instead of inserting a character, you are corrupting the data.
Try this instead:
#include <iostream>
#include <string>
#include <sstream>
int main(int argc, char ** argv) {
std::string a("hello\\ world");
std::stringstream ss;
for (int i = 0; i < a.length(); ++i) {
if (a[i] == '\\') {
ss << "\\\\";
}
else {
ss << a[i];
}
}
std::cout << ss.str() << std::endl;
return 0;
}
lots wrong. did not test this but it will get you closer
http://www.cplusplus.com/reference/string/string/
string stripPath(string path)
{
string newpath;
for (int i = 0; i <= path.length() ;i++)
{
if(path.at(i) == '\\')
{
newpath.append(path.at(i));
newpath.append(path.at(i));
}
else
newpath.append(path.at(i));
}
return newpath;
}
But in order for the compiler to read
backslash in i need to create a
function that will make a one
backslash into 2 backslash
The compiler only reads string when you compile, and in that case you will need two as the first back slash will be an escape character. So if you were to have a static path string in code you would have to do something like this:
std::string path = "C:\\SomeFolder\\SomeTextFile.txt";
The compiler will never actually call your function only compile it. So writing a function like this so the compiler can read a string is not going to solve your problem.
The condition if (newpath[i] = '\\') should be if (newpath[i] == '\\').
The statement newpath[i] += '\\'; will not give the intended result of concatenation. It will instead add the integral value of '\\' to newpath[i].
Moreover why are you using a char newpath[99999]; array inside the function. newpath could be std::string newpath.
int main()
{
std::string path = "c:\\test\\test2\\test3\\test4";
std::cout << "orignal path: " << path << std::endl;
size_t found = 0, next = 0;
while( (found = path.find('\\', next)) != std::string::npos )
{
path.insert(found, "\\");
next = found+4;
}
std::cout << "path with double slash: " << path << std::endl;
return 0;
}
I am trying to create a simple shell in Unix. I read a lot and found that everybody uses the strtok function a lot. But I want to do it without any special functions. So I wrote the code but I can't seem to get it to work. What am I doing wrong here?
void process(char**);
int arg_count;
char **splitcommand(char* input)
{
char temp[81][81] ,*cmdptr[40];
int k,done=0,no=0,arg_count=0;
for(int i=0 ; input[i] != '\0' ; i++)
{
k=0;
while(1)
{
if(input[i] == ' ')
{
arg_count++;
break;
}
if(input[i] == '\0')
{
arg_count++;
done = 1;
break;
}
temp[arg_count][k++] = input[i++];
}
temp[arg_count][k++] = '\0';
if(done == 1)
{
break;
}
}
for(int i=0 ; i<arg_count ; i++)
{
cmdptr[i] = temp[i];
cout<<endl;
}
cout<<endl;
}
void process(char* cmd[])
{
int pid = fork();
if (pid < 0)
{
cout << "Fork Failed" << endl;
exit(-1);
}
else if (pid == 0)
{
cout<<endl<<"in pid";
execvp(cmd[0], cmd);
}
else
{
wait(NULL);
cout << "Job's Done" << endl;
}
}
int main()
{
cout<<"Welcome to shell !!!!!!!!!!!"<<endl;
char input[81];
cin.getline(input,81);
splitcommand(input);
}
Several things:
you don't return anything from the splitcommand function
everything you do in the splitcommand function is done in local variables, so it (esp. the strings you make) will not survive its end
the code that attaches the null terminator is wrong (you put it to the following string, not the current one)
using fixed-size buffer is a great choice; people love it
note that in real UNIX shells, not every space designates an argument, and not every argument is designated by spaces
I'd suggest you use strings and some (real) parser framework, provided it is not too special for you.
This is almost certainly homework. There is no reason to avoid library functions unless you were told to. In fact, most likely you were told to implement strtok.
strtok isn't really a special function as it's standard function in standard include string.h, so there is no good reason not to use it.
If you decide to make your shell more complicated then you may reason about using a tool for lexical analysis.
For example:
http://en.wikipedia.org/wiki/Flex_lexical_analyser
The problem is with the
arg_count++;
inside the if(input[i] == ' ') and if(input[i] == '\0')
when you are parsing the command line and you find a space or you reach the end of the command line you are increment arg_count before you put a \0 at the end of the command you were reading.
So change it to:
if(input[i] == ' ')
{
// arg_count++; REMOVE THIS.
break;
}
if(input[i] == '\0')
{
// arg_count++; REMOVE THIS.
done = 1;
break;
}
temp[arg_count][k++] = input[i++];
}
temp[arg_count][k++] = '\0'; // add null-char at the end.
arg_count++; // increment should happen here.
More bugs:
You are not returning anything from
splitcommand
You cannot just return cmdptr
because they point to local char
arrays(temp) which will not persist
after the function returns. So you'll
have to make sure that the array
temp persists even after function
call by allocating it dynamically or
making it global.
Arguments to execvp look good to
me. Others please take a look.