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 {
ParseArgumentVector::Parser17 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
ArgVectorArgumentVector::Parser98 const std::vector<std::string>& ArgVector() const
99 {
100 return fArgVector;
101 }
102
TotalStringSizeArgumentVector::Parser103 size_t TotalStringSize() const
104 {
105 return fTotalStringSize;
106 }
107
108 private:
_PushCurrentArgArgumentVector::Parser109 void _PushCurrentArg()
110 {
111 if (fCurrentArgStarted) {
112 fArgVector.push_back(fCurrentArg);
113 fTotalStringSize += fCurrentArg.length() + 1;
114 fCurrentArgStarted = false;
115 }
116 }
117
_PushCharacterArgumentVector::Parser118 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
ArgumentVector()137 ArgumentVector::ArgumentVector()
138 :
139 fArguments(NULL),
140 fCount(0)
141 {
142 }
143
144
~ArgumentVector()145 ArgumentVector::~ArgumentVector()
146 {
147 free(fArguments);
148 }
149
150
151 char**
DetachArguments()152 ArgumentVector::DetachArguments()
153 {
154 char** arguments = fArguments;
155 fArguments = NULL;
156 fCount = 0;
157 return arguments;
158 }
159
160
161 ArgumentVector::ParseError
Parse(const char * commandLine,const char ** _errorLocation)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