haiku/src/kits/shared/ArgumentVector.cpp

204 lines
3.9 KiB
C++

/*
* Copyright 2007-2012, Ingo Weinhold, ingo_weinhold@gmx.de.
* Distributed under the terms of the MIT License.
*/
#include <ArgumentVector.h>
#include <stdlib.h>
#include <string.h>
#include <string>
#include <vector>
struct ArgumentVector::Parser {
ParseError Parse(const char* commandLine, const char*& _errorLocation)
{
// init temporary arg/argv storage
fCurrentArg.clear();
fCurrentArgStarted = false;
fArgVector.clear();
fTotalStringSize = 0;
for (; *commandLine; commandLine++) {
char c = *commandLine;
// whitespace delimits args and is otherwise ignored
if (isspace(c)) {
_PushCurrentArg();
continue;
}
const char* errorBase = commandLine;
switch (c) {
case '\'':
// quoted string -- no quoting
while (*++commandLine != '\'') {
c = *commandLine;
if (c == '\0') {
_errorLocation = errorBase;
return UNTERMINATED_QUOTED_STRING;
}
_PushCharacter(c);
}
break;
case '"':
// quoted string -- some quoting
while (*++commandLine != '"') {
c = *commandLine;
if (c == '\0') {
_errorLocation = errorBase;
return UNTERMINATED_QUOTED_STRING;
}
if (c == '\\') {
c = *++commandLine;
if (c == '\0') {
_errorLocation = errorBase;
return UNTERMINATED_QUOTED_STRING;
}
// only '\' and '"' can be quoted, otherwise the
// the '\' is treated as a normal char
if (c != '\\' && c != '"')
_PushCharacter('\\');
}
_PushCharacter(c);
}
break;
case '\\':
// quoted char
c = *++commandLine;
if (c == '\0') {
_errorLocation = errorBase;
return TRAILING_BACKSPACE;
}
_PushCharacter(c);
break;
default:
// normal char
_PushCharacter(c);
break;
}
}
// commit last arg
_PushCurrentArg();
return NO_ERROR;
}
const std::vector<std::string>& ArgVector() const
{
return fArgVector;
}
size_t TotalStringSize() const
{
return fTotalStringSize;
}
private:
void _PushCurrentArg()
{
if (fCurrentArgStarted) {
fArgVector.push_back(fCurrentArg);
fTotalStringSize += fCurrentArg.length() + 1;
fCurrentArgStarted = false;
}
}
void _PushCharacter(char c)
{
if (!fCurrentArgStarted) {
fCurrentArg = "";
fCurrentArgStarted = true;
}
fCurrentArg += c;
}
private:
// temporaries
std::string fCurrentArg;
bool fCurrentArgStarted;
std::vector<std::string> fArgVector;
size_t fTotalStringSize;
};
ArgumentVector::ArgumentVector()
:
fArguments(NULL),
fCount(0)
{
}
ArgumentVector::~ArgumentVector()
{
free(fArguments);
}
char**
ArgumentVector::DetachArguments()
{
char** arguments = fArguments;
fArguments = NULL;
fCount = 0;
return arguments;
}
ArgumentVector::ParseError
ArgumentVector::Parse(const char* commandLine, const char** _errorLocation)
{
free(DetachArguments());
ParseError error;
const char* errorLocation = commandLine;
try {
Parser parser;
error = parser.Parse(commandLine, errorLocation);
if (error == NO_ERROR) {
// Create a char* array and copy everything into a single
// allocation.
int count = parser.ArgVector().size();
size_t arraySize = (count + 1) * sizeof(char*);
fArguments = (char**)malloc(
arraySize + parser.TotalStringSize());
if (fArguments != 0) {
char* argument = (char*)(fArguments + count + 1);
for (int i = 0; i < count; i++) {
fArguments[i] = argument;
const std::string& sourceArgument = parser.ArgVector()[i];
size_t argumentSize = sourceArgument.length() + 1;
memcpy(argument, sourceArgument.c_str(), argumentSize);
argument += argumentSize;
}
fArguments[count] = NULL;
fCount = count;
} else
error = NO_MEMORY;
}
} catch (...) {
error = NO_MEMORY;
}
if (error != NO_ERROR && _errorLocation != NULL)
*_errorLocation = errorLocation;
return error;
}