1 /* 2 * Copyright 2007-2012, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7 #include <ArgumentVector.h> 8 9 #include <stdlib.h> 10 #include <string.h> 11 12 #include <string> 13 #include <vector> 14 15 16 struct ArgumentVector::Parser { 17 ParseError Parse(const char* commandLine, const char*& _errorLocation) 18 { 19 // init temporary arg/argv storage 20 fCurrentArg.clear(); 21 fCurrentArgStarted = false; 22 fArgVector.clear(); 23 fTotalStringSize = 0; 24 25 for (; *commandLine; commandLine++) { 26 char c = *commandLine; 27 28 // whitespace delimits args and is otherwise ignored 29 if (isspace(c)) { 30 _PushCurrentArg(); 31 continue; 32 } 33 34 const char* errorBase = commandLine; 35 36 switch (c) { 37 case '\'': 38 // quoted string -- no quoting 39 while (*++commandLine != '\'') { 40 c = *commandLine; 41 if (c == '\0') { 42 _errorLocation = errorBase; 43 return UNTERMINATED_QUOTED_STRING; 44 } 45 _PushCharacter(c); 46 } 47 break; 48 49 case '"': 50 // quoted string -- some quoting 51 while (*++commandLine != '"') { 52 c = *commandLine; 53 if (c == '\0') { 54 _errorLocation = errorBase; 55 return UNTERMINATED_QUOTED_STRING; 56 } 57 58 if (c == '\\') { 59 c = *++commandLine; 60 if (c == '\0') { 61 _errorLocation = errorBase; 62 return UNTERMINATED_QUOTED_STRING; 63 } 64 65 // only '\' and '"' can be quoted, otherwise the 66 // the '\' is treated as a normal char 67 if (c != '\\' && c != '"') 68 _PushCharacter('\\'); 69 } 70 71 _PushCharacter(c); 72 } 73 break; 74 75 case '\\': 76 // quoted char 77 c = *++commandLine; 78 if (c == '\0') { 79 _errorLocation = errorBase; 80 return TRAILING_BACKSPACE; 81 } 82 _PushCharacter(c); 83 break; 84 85 default: 86 // normal char 87 _PushCharacter(c); 88 break; 89 } 90 } 91 92 // commit last arg 93 _PushCurrentArg(); 94 95 return NO_ERROR; 96 } 97 98 const std::vector<std::string>& ArgVector() const 99 { 100 return fArgVector; 101 } 102 103 size_t TotalStringSize() const 104 { 105 return fTotalStringSize; 106 } 107 108 private: 109 void _PushCurrentArg() 110 { 111 if (fCurrentArgStarted) { 112 fArgVector.push_back(fCurrentArg); 113 fTotalStringSize += fCurrentArg.length() + 1; 114 fCurrentArgStarted = false; 115 } 116 } 117 118 void _PushCharacter(char c) 119 { 120 if (!fCurrentArgStarted) { 121 fCurrentArg = ""; 122 fCurrentArgStarted = true; 123 } 124 125 fCurrentArg += c; 126 } 127 128 private: 129 // temporaries 130 std::string fCurrentArg; 131 bool fCurrentArgStarted; 132 std::vector<std::string> fArgVector; 133 size_t fTotalStringSize; 134 }; 135 136 137 ArgumentVector::ArgumentVector() 138 : 139 fArguments(NULL), 140 fCount(0) 141 { 142 } 143 144 145 ArgumentVector::~ArgumentVector() 146 { 147 free(fArguments); 148 } 149 150 151 char** 152 ArgumentVector::DetachArguments() 153 { 154 char** arguments = fArguments; 155 fArguments = NULL; 156 fCount = 0; 157 return arguments; 158 } 159 160 161 ArgumentVector::ParseError 162 ArgumentVector::Parse(const char* commandLine, const char** _errorLocation) 163 { 164 free(DetachArguments()); 165 166 ParseError error; 167 const char* errorLocation = commandLine; 168 169 try { 170 Parser parser; 171 error = parser.Parse(commandLine, errorLocation); 172 173 if (error == NO_ERROR) { 174 // Create a char* array and copy everything into a single 175 // allocation. 176 int count = parser.ArgVector().size(); 177 size_t arraySize = (count + 1) * sizeof(char*); 178 fArguments = (char**)malloc( 179 arraySize + parser.TotalStringSize()); 180 if (fArguments != 0) { 181 char* argument = (char*)(fArguments + count + 1); 182 for (int i = 0; i < count; i++) { 183 fArguments[i] = argument; 184 const std::string& sourceArgument = parser.ArgVector()[i]; 185 size_t argumentSize = sourceArgument.length() + 1; 186 memcpy(argument, sourceArgument.c_str(), argumentSize); 187 argument += argumentSize; 188 } 189 190 fArguments[count] = NULL; 191 fCount = count; 192 } else 193 error = NO_MEMORY; 194 } 195 } catch (...) { 196 error = NO_MEMORY; 197 } 198 199 if (error != NO_ERROR && _errorLocation != NULL) 200 *_errorLocation = errorLocation; 201 202 return error; 203 } 204