user1804208
user1804208

Reputation: 165

C++ using execvp with string array

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.

Upvotes: 3

Views: 3025

Answers (3)

Rupesh Yadav.
Rupesh Yadav.

Reputation: 904

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

Upvotes: 2

GreenScape
GreenScape

Reputation: 7737

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];
}

Upvotes: 0

Collin Dauphinee
Collin Dauphinee

Reputation: 13993

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;

Upvotes: 1

Related Questions