diff --git a/headers/private/shared/ArgumentVector.h b/headers/private/shared/ArgumentVector.h new file mode 100644 index 0000000000..0b32a8743d --- /dev/null +++ b/headers/private/shared/ArgumentVector.h @@ -0,0 +1,53 @@ +/* + * Copyright 2007-2012, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef _ARGUMENT_VECTOR_H +#define _ARGUMENT_VECTOR_H + + +#include + + +namespace BPrivate { + + +class ArgumentVector { +public: + enum ParseError { + NO_ERROR, + NO_MEMORY, + UNTERMINATED_QUOTED_STRING, + TRAILING_BACKSPACE + }; + +public: + ArgumentVector(); + ~ArgumentVector(); + + int32 ArgumentCount() const { return fCount; } + const char* const* Arguments() const { return fArguments; } + + char** DetachArguments(); + // Caller must free() -- it's all one big allocation at the + // returned pointer. + + ParseError Parse(const char* commandLine, + const char** _errorLocation = NULL); + +private: + struct Parser; + +private: + char** fArguments; + int32 fCount; +}; + + +} // namespace BPrivate + + +using BPrivate::ArgumentVector; + + +#endif // _ARGUMENT_VECTOR_H diff --git a/src/kits/shared/ArgumentVector.cpp b/src/kits/shared/ArgumentVector.cpp new file mode 100644 index 0000000000..c678f5de7c --- /dev/null +++ b/src/kits/shared/ArgumentVector.cpp @@ -0,0 +1,203 @@ +/* + * Copyright 2007-2012, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + + +#include + +#include +#include + +#include +#include + + +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& 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 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; +} diff --git a/src/kits/shared/Jamfile b/src/kits/shared/Jamfile index d56a8b6fe2..8992caa24f 100644 --- a/src/kits/shared/Jamfile +++ b/src/kits/shared/Jamfile @@ -15,6 +15,7 @@ UsePrivateHeaders kernel libroot ; StaticLibrary libshared.a : AboutMenuItem.cpp AboutWindow.cpp + ArgumentVector.cpp CalendarView.cpp ColorQuantizer.cpp CommandPipe.cpp