1 /*-------------------------------------------------------------------------
2 * drawElements Utility Library
3 * ----------------------------
4 *
5 * Copyright 2014 The Android Open Source Project
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 *
19 *//*!
20 * \file
21 * \brief Command line parser.
22 *//*--------------------------------------------------------------------*/
23
24 #include "deCommandLine.h"
25 #include "deMemPool.h"
26 #include "dePoolArray.h"
27 #include "deMemory.h"
28 #include "deString.h"
29
30 #include <string.h>
31
32 DE_DECLARE_POOL_ARRAY(CharPtrArray, char *);
33
34 enum
35 {
36 MAX_ARGS = 64
37 };
38
deCommandLine_parse(const char * commandLine)39 deCommandLine *deCommandLine_parse(const char *commandLine)
40 {
41 deMemPool *tmpPool = deMemPool_createRoot(DE_NULL, 0);
42 CharPtrArray *args = tmpPool ? CharPtrArray_create(tmpPool) : DE_NULL;
43 char *buf = DE_NULL;
44 char *outPtr;
45 int pos;
46 int argNdx;
47 char strChr;
48
49 if (!args)
50 {
51 if (tmpPool)
52 deMemPool_destroy(tmpPool);
53 return DE_NULL;
54 }
55
56 DE_ASSERT(commandLine);
57
58 /* Create buffer for args (no expansion can happen). */
59 buf = (char *)deCalloc(strlen(commandLine) + 1);
60 pos = 0;
61 argNdx = 0;
62 outPtr = buf;
63 strChr = 0;
64
65 if (!buf || !CharPtrArray_pushBack(args, buf))
66 {
67 deMemPool_destroy(tmpPool);
68 return DE_NULL;
69 }
70
71 while (commandLine[pos] != 0)
72 {
73 char c = commandLine[pos++];
74
75 if (strChr != 0 && c == '\\')
76 {
77 /* Escape. */
78 c = commandLine[pos++];
79 switch (c)
80 {
81 case 'n':
82 *outPtr++ = '\n';
83 break;
84 case 't':
85 *outPtr++ = '\t';
86 break;
87 default:
88 *outPtr++ = c;
89 break;
90 }
91 }
92 else if (strChr != 0 && c == strChr)
93 {
94 /* String end. */
95 strChr = 0;
96 }
97 else if (strChr == 0 && (c == '"' || c == '\''))
98 {
99 /* String start. */
100 strChr = c;
101 }
102 else if (c == ' ' && strChr == 0)
103 {
104 /* Arg end. */
105 *outPtr++ = 0;
106 argNdx += 1;
107 if (!CharPtrArray_pushBack(args, outPtr))
108 {
109 deFree(buf);
110 deMemPool_destroy(tmpPool);
111 return DE_NULL;
112 }
113 }
114 else
115 *outPtr++ = c;
116 }
117
118 DE_ASSERT(commandLine[pos] == 0);
119
120 /* Terminate last arg. */
121 *outPtr = 0;
122
123 /* Create deCommandLine. */
124 {
125 deCommandLine *cmdLine = (deCommandLine *)deCalloc(sizeof(deCommandLine));
126
127 if (!cmdLine ||
128 !(cmdLine->args = (char **)deCalloc(sizeof(char *) * (size_t)CharPtrArray_getNumElements(args))))
129 {
130 deFree(cmdLine);
131 deFree(buf);
132 deMemPool_destroy(tmpPool);
133 return DE_NULL;
134 }
135
136 cmdLine->numArgs = CharPtrArray_getNumElements(args);
137 cmdLine->argBuf = buf;
138
139 for (argNdx = 0; argNdx < cmdLine->numArgs; argNdx++)
140 cmdLine->args[argNdx] = CharPtrArray_get(args, argNdx);
141
142 deMemPool_destroy(tmpPool);
143 return cmdLine;
144 }
145 }
146
deCommandLine_destroy(deCommandLine * cmdLine)147 void deCommandLine_destroy(deCommandLine *cmdLine)
148 {
149 deFree(cmdLine->argBuf);
150 deFree(cmdLine);
151 }
152
testParse(const char * cmdLine,const char * const * refArgs,int numArgs)153 static void testParse(const char *cmdLine, const char *const *refArgs, int numArgs)
154 {
155 deCommandLine *parsedCmdLine = deCommandLine_parse(cmdLine);
156 int argNdx;
157
158 DE_TEST_ASSERT(parsedCmdLine);
159 DE_TEST_ASSERT(parsedCmdLine->numArgs == numArgs);
160
161 for (argNdx = 0; argNdx < numArgs; argNdx++)
162 DE_TEST_ASSERT(deStringEqual(parsedCmdLine->args[argNdx], refArgs[argNdx]));
163
164 deCommandLine_destroy(parsedCmdLine);
165 }
166
deCommandLine_selfTest(void)167 void deCommandLine_selfTest(void)
168 {
169 {
170 const char *cmdLine = "hello";
171 const char *ref[] = {"hello"};
172 testParse(cmdLine, ref, DE_LENGTH_OF_ARRAY(ref));
173 }
174 {
175 const char *cmdLine = "hello world";
176 const char *ref[] = {"hello", "world"};
177 testParse(cmdLine, ref, DE_LENGTH_OF_ARRAY(ref));
178 }
179 {
180 const char *cmdLine = "hello/world";
181 const char *ref[] = {"hello/world"};
182 testParse(cmdLine, ref, DE_LENGTH_OF_ARRAY(ref));
183 }
184 {
185 const char *cmdLine = "hello/world --help";
186 const char *ref[] = {"hello/world", "--help"};
187 testParse(cmdLine, ref, DE_LENGTH_OF_ARRAY(ref));
188 }
189 {
190 const char *cmdLine = "hello/world --help foo";
191 const char *ref[] = {"hello/world", "--help", "foo"};
192 testParse(cmdLine, ref, DE_LENGTH_OF_ARRAY(ref));
193 }
194 {
195 const char *cmdLine = "hello\\world --help foo";
196 const char *ref[] = {"hello\\world", "--help", "foo"};
197 testParse(cmdLine, ref, DE_LENGTH_OF_ARRAY(ref));
198 }
199 {
200 const char *cmdLine = "\"hello/worl d\" --help --foo=\"bar\" \"ba z\\\"\"";
201 const char *ref[] = {"hello/worl d", "--help", "--foo=bar", "ba z\""};
202 testParse(cmdLine, ref, DE_LENGTH_OF_ARRAY(ref));
203 }
204 {
205 const char *cmdLine = "'hello/worl d' --help --foo='bar' 'ba z\\\''";
206 const char *ref[] = {"hello/worl d", "--help", "--foo=bar", "ba z'"};
207 testParse(cmdLine, ref, DE_LENGTH_OF_ARRAY(ref));
208 }
209 {
210 const char *cmdLine = "hello \"'world'\"";
211 const char *ref[] = {"hello", "'world'"};
212 testParse(cmdLine, ref, DE_LENGTH_OF_ARRAY(ref));
213 }
214 {
215 const char *cmdLine = "hello '\"world\"'";
216 const char *ref[] = {"hello", "\"world\""};
217 testParse(cmdLine, ref, DE_LENGTH_OF_ARRAY(ref));
218 }
219 {
220 const char *cmdLine = "hello \"world\\n\"";
221 const char *ref[] = {"hello", "world\n"};
222 testParse(cmdLine, ref, DE_LENGTH_OF_ARRAY(ref));
223 }
224 }
225