xref: /haiku/src/kits/shared/ArgumentVector.cpp (revision ed24eb5ff12640d052171c6a7feba37fab8a75d1)
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