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
32DE_DECLARE_POOL_ARRAY(CharPtrArray, char*);
33
34enum
35{
36	MAX_ARGS = 64
37};
38
39deCommandLine* 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((int)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':	*outPtr++ = '\n';	break;
82				case 't':	*outPtr++ = '\t';	break;
83				default:	*outPtr++ = c;		break;
84			}
85		}
86		else if (strChr != 0 && c == strChr)
87		{
88			/* String end. */
89			strChr = 0;
90		}
91		else if (strChr == 0 && (c == '"' || c == '\''))
92		{
93			/* String start. */
94			strChr = c;
95		}
96		else if (c == ' ' && strChr == 0)
97		{
98			/* Arg end. */
99			*outPtr++		 = 0;
100			argNdx			+= 1;
101			if (!CharPtrArray_pushBack(args, outPtr))
102			{
103				deFree(buf);
104				deMemPool_destroy(tmpPool);
105				return DE_NULL;
106			}
107		}
108		else
109			*outPtr++ = c;
110	}
111
112	DE_ASSERT(commandLine[pos] == 0);
113
114	/* Terminate last arg. */
115	*outPtr = 0;
116
117	/* Create deCommandLine. */
118	{
119		deCommandLine* cmdLine = (deCommandLine*)deCalloc(sizeof(deCommandLine));
120
121		if (!cmdLine || !(cmdLine->args = (char**)deCalloc(sizeof(char*)*CharPtrArray_getNumElements(args))))
122		{
123			deFree(cmdLine);
124			deFree(buf);
125			deMemPool_destroy(tmpPool);
126			return DE_NULL;
127		}
128
129		cmdLine->numArgs	= CharPtrArray_getNumElements(args);
130		cmdLine->argBuf		= buf;
131
132		for (argNdx = 0; argNdx < cmdLine->numArgs; argNdx++)
133			cmdLine->args[argNdx] = CharPtrArray_get(args, argNdx);
134
135		deMemPool_destroy(tmpPool);
136		return cmdLine;
137	}
138}
139
140void deCommandLine_destroy (deCommandLine* cmdLine)
141{
142	deFree(cmdLine->argBuf);
143	deFree(cmdLine);
144}
145
146static void testParse (const char* cmdLine, const char* const* refArgs, int numArgs)
147{
148	deCommandLine*	parsedCmdLine	= deCommandLine_parse(cmdLine);
149	int				argNdx;
150
151	DE_TEST_ASSERT(parsedCmdLine);
152	DE_TEST_ASSERT(parsedCmdLine->numArgs == numArgs);
153
154	for (argNdx = 0; argNdx < numArgs; argNdx++)
155		DE_TEST_ASSERT(deStringEqual(parsedCmdLine->args[argNdx], refArgs[argNdx]));
156
157	deCommandLine_destroy(parsedCmdLine);
158}
159
160void deCommandLine_selfTest (void)
161{
162	{
163		const char* cmdLine	= "hello";
164		const char* ref[]	= { "hello" };
165		testParse(cmdLine, ref, DE_LENGTH_OF_ARRAY(ref));
166	}
167	{
168		const char* cmdLine	= "hello world";
169		const char* ref[]	= { "hello", "world" };
170		testParse(cmdLine, ref, DE_LENGTH_OF_ARRAY(ref));
171	}
172	{
173		const char* cmdLine	= "hello/world";
174		const char* ref[]	= { "hello/world" };
175		testParse(cmdLine, ref, DE_LENGTH_OF_ARRAY(ref));
176	}
177	{
178		const char* cmdLine	= "hello/world --help";
179		const char* ref[]	= { "hello/world", "--help" };
180		testParse(cmdLine, ref, DE_LENGTH_OF_ARRAY(ref));
181	}
182	{
183		const char* cmdLine	= "hello/world --help foo";
184		const char* ref[]	= { "hello/world", "--help", "foo" };
185		testParse(cmdLine, ref, DE_LENGTH_OF_ARRAY(ref));
186	}
187	{
188		const char* cmdLine	= "hello\\world --help foo";
189		const char* ref[]	= { "hello\\world", "--help", "foo" };
190		testParse(cmdLine, ref, DE_LENGTH_OF_ARRAY(ref));
191	}
192	{
193		const char* cmdLine	= "\"hello/worl d\" --help --foo=\"bar\" \"ba z\\\"\"";
194		const char* ref[]	= { "hello/worl d", "--help", "--foo=bar", "ba z\"" };
195		testParse(cmdLine, ref, DE_LENGTH_OF_ARRAY(ref));
196	}
197	{
198		const char* cmdLine	= "'hello/worl d' --help --foo='bar' 'ba z\\\''";
199		const char* ref[]	= { "hello/worl d", "--help", "--foo=bar", "ba z'" };
200		testParse(cmdLine, ref, DE_LENGTH_OF_ARRAY(ref));
201	}
202	{
203		const char* cmdLine	= "hello \"'world'\"";
204		const char* ref[]	= { "hello", "'world'" };
205		testParse(cmdLine, ref, DE_LENGTH_OF_ARRAY(ref));
206	}
207	{
208		const char* cmdLine	= "hello '\"world\"'";
209		const char* ref[]	= { "hello", "\"world\"" };
210		testParse(cmdLine, ref, DE_LENGTH_OF_ARRAY(ref));
211	}
212	{
213		const char* cmdLine	= "hello \"world\\n\"";
214		const char* ref[]	= { "hello", "world\n" };
215		testParse(cmdLine, ref, DE_LENGTH_OF_ARRAY(ref));
216	}
217}
218