3

I'm writing a program to execute Unix commands with any amount of arguments. I have no problem receiving input and making tokens as I use regular strings. However, execvp will only accept an array of pointers and I'm not sure how to go about converting the array of strings to this. Here's what I have:

#include <cstring>
#include <iostream>
#include <sstream>
#include <string>
#include <unistd.h>
int main()
{
while (true)
{
    int argc = 0;
    std::istringstream iss;
    std::string command;
    std::cout << "$> ";
    getline(std::cin, command);
    iss.str(command);
    for (unsigned i = 0; i <= command.length(); i++)
    {
        if (command[i] == ' ' || command[i] == '\0')
        {
            argc++;
        }
    }
    std::string arr[argc+1];
    for (int i = 0; i < argc; i++)
    {
        iss >> arr[i];
    }
    if (arr[0].compare("quit"))
    {
        break;
    }
    else
    {
        char*argv[argc+1];
        for (int i = 0; i < argc; i++)
        {
            argv[i] = arr[i].c_str(); //This line is wrong
        }
        argv[argc] = NULL;
        execvp(argv[0], argv);
    }
}
return 0;
}

I've tried various methods and can't figure out how to convert a string to a char array in the proper manner. Methods like strcpy won't work because the length of each argument will vary. Any help would be greatly appreciated.

3
  • What about making your array const to match the fact that c_str() returns a const ? Can you show us the error message by the way ? Commented Feb 25, 2015 at 6:42
  • I've tried adding const to the array but I'm getting "invalid conversion from ‘const char**’ to ‘char* const*’". But placing const anywhere else only leads to more errors. The inital message without the const says "invalid conversion from const char* to char*" which makes sense but the rest is throwing me. Commented Feb 25, 2015 at 6:59
  • Ok, you mean later in the execvp() ? Commented Feb 25, 2015 at 8:11

3 Answers 3

2

You have very small mistakes.

Replace:

  1. argv[i] = arr[i].c_str(); with argv[i] = const_cast<char*>(arr[i].c_str());
  2. if(arr[0].compare("quit")) with if(!arr[0].compare("quit"))

And you are good to go, and this would work in any compiler.
Run here

But I have some advice to make it using fork so it won't run only one command at a time.

Example here

Sign up to request clarification or add additional context in comments.

2 Comments

Yes ! But casting a way const, be it with (char*) or with argv[i] = const_cast<char*>(arr[i].c_str()); wouldn't protect you anymore against unauthorized overwriting.
Thank you, that worked perfectly! Totally forgot that const_cast was a thing.
1

Your problem is that your array is holding char*, while std::string::c_str() returns const char*. It can't be stored in your array, because it would lose const-ness.

If you're using a C++11 compliant compiler, you can use &arr[i][0] to obtain a non-const pointer to the internal string buffer.

If you aren't using a C++11 compliant compiler, the internal buffer may not be null-terminated, so your only option is to use new or malloc to allocate correctly sized buffers, then strcpy the strings into them.

char* buffer = new char[arr[i].length() + 1];
strcpy(buffer, arr[i].c_str());
argv[i] = buffer;

Comments

0

You can ensure null terminator at the end of each string in the array this way:

for (int i = 0; i < argc; i++)
{
    iss >> arr[i];
    arr[i].push_back('\0');
}

Then you can simply capture pointer to the first character of each string. Clean and safe, without any const_cast:

for (int i = 0; i < argc; i++)
{
    argv[i] = &arr[i][0];
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.