1 // Emacs style mode select   -*- C++ -*-
2 //-----------------------------------------------------------------------------
3 //
4 // $Id: m_argv.cpp 4469 2014-01-03 23:38:29Z dr_sean $
5 //
6 // Copyright (C) 1993-1996 by id Software, Inc.
7 // Copyright (C) 2006-2014 by The Odamex Team.
8 //
9 // This program is free software; you can redistribute it and/or
10 // modify it under the terms of the GNU General Public License
11 // as published by the Free Software Foundation; either version 2
12 // of the License, or (at your option) any later version.
13 //
14 // This program is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 // GNU General Public License for more details.
18 //
19 // DESCRIPTION:
20 //  Command-line arguments
21 //
22 //-----------------------------------------------------------------------------
23 
24 
25 #include <algorithm>
26 
27 #include "cmdlib.h"
28 #include "m_fileio.h"
29 #include "m_argv.h"
30 #include "i_system.h"
31 
IMPLEMENT_CLASS(DArgs,DObject)32 IMPLEMENT_CLASS (DArgs, DObject)
33 
34 DArgs::DArgs ()
35 {
36 }
37 
DArgs(unsigned int argc,char ** argv)38 DArgs::DArgs (unsigned int argc, char **argv)
39 {
40 	if(argv)
41 		CopyArgs(argc, argv);
42 }
43 
DArgs(const DArgs & other)44 DArgs::DArgs (const DArgs &other) : args(other.args)
45 {
46 
47 }
48 
DArgs(const char * cmdline)49 DArgs::DArgs (const char *cmdline)
50 {
51 	SetArgs(cmdline);
52 }
53 
~DArgs()54 DArgs::~DArgs ()
55 {
56 	FlushArgs ();
57 }
58 
operator =(const DArgs & other)59 DArgs &DArgs::operator= (const DArgs &other)
60 {
61 	args = other.args;
62 	return *this;
63 }
64 
operator [](size_t n)65 const char *DArgs::operator[] (size_t n)
66 {
67 	return GetArg(n);
68 }
69 
SetArgs(unsigned argc,char ** argv)70 void DArgs::SetArgs (unsigned argc, char **argv)
71 {
72 	CopyArgs(argc, argv);
73 }
74 
CopyArgs(unsigned argc,char ** argv)75 void DArgs::CopyArgs (unsigned argc, char **argv)
76 {
77 	args.clear();
78 
79 	if(!argv || !argc)
80 		return;
81 
82 	args.resize(argc);
83 
84 	for (unsigned i = 0; i < argc; i++)
85 		if(argv[i])
86 			args[i] = argv[i];
87 }
88 
FlushArgs()89 void DArgs::FlushArgs ()
90 {
91 	args.clear();
92 }
93 
94 //
95 // CheckParm
96 // Checks for the given parameter
97 // in the program's command line arguments.
98 // Returns the argument number (1 to argc-1) or 0 if not present
99 //
CheckParm(const char * check) const100 size_t DArgs::CheckParm (const char *check) const
101 {
102 	if(!check)
103 		return 0;
104 
105 	for (size_t i = 1, n = args.size(); i < n; i++)
106 		if (!stricmp (check, args[i].c_str()))
107 			return i;
108 
109 	return 0;
110 }
111 
CheckValue(const char * check) const112 const char *DArgs::CheckValue (const char *check) const
113 {
114 	if(!check)
115 		return 0;
116 
117 	size_t i = CheckParm (check);
118 
119 	if (i > 0 && i < args.size() - 1)
120 		return args[i + 1].c_str();
121 	else
122 		return NULL;
123 }
124 
GetArg(size_t arg) const125 const char *DArgs::GetArg (size_t arg) const
126 {
127 	if (arg < args.size())
128 		return args[arg].c_str();
129 	else
130 		return NULL;
131 }
132 
GetArgList(size_t start) const133 const std::vector<std::string> DArgs::GetArgList (size_t start) const
134 {
135 	std::vector<std::string> out;
136 
137 	if(start < args.size())
138 	{
139 		out.resize(args.size() - start);
140 		std::copy(args.begin() + start, args.end(), out.begin());
141 	}
142 
143 	return out;
144 }
145 
NumArgs() const146 size_t DArgs::NumArgs () const
147 {
148 	return args.size();
149 }
150 
AppendArg(const char * arg)151 void DArgs::AppendArg (const char *arg)
152 {
153 	if(arg)
154 		args.push_back(arg);
155 }
156 
157 //
158 // IsParam
159 //
160 // Helper function to return if the given argument number i is a parameter
161 //
IsParam(const std::vector<std::string> & args,size_t i)162 static bool IsParam(const std::vector<std::string>& args, size_t i)
163 {
164 	return i < args.size() && (args[i][0] == '-' || args[i][0] == '+');
165 }
166 
167 //
168 // FindNextParamArg
169 //
170 // Returns the next argument number for a command line parameter starting
171 // from argument number i.
172 //
FindNextParamArg(const char * param,const std::vector<std::string> & args,size_t i)173 static size_t FindNextParamArg(const char* param, const std::vector<std::string>& args, size_t i)
174 {
175 	while (i < args.size())
176 	{
177 		if (!IsParam(args, i))
178 			return i;
179 
180 		// matches param, return first argument for this param
181 		if (stricmp(param, args[i].c_str()) == 0)
182 		{
183 			i++;
184 			continue;
185 		}
186 
187 		// skip over any params that don't match and their arguments
188 		for (i++; i < args.size() && !IsParam(args, i); i++)
189 			;
190 	}
191 
192 	return args.size();
193 }
194 
195 //
196 // DArgs::GatherFiles
197 //
198 // Collects all of the arguments entered after param and returns them
199 // if their file extension matches ext or if they have no extension and
200 // acceptNoExt is true. This can handle param being specified multiple times
201 // on the command lien.
202 //
GatherFiles(const char * param,const char * ext,bool acceptNoExt) const203 DArgs DArgs::GatherFiles(const char* param, const char* ext, bool acceptNoExt) const
204 {
205 	DArgs out;
206 
207 	if (param[0] != '-' && param[0] != '+')
208 		return out;
209 
210 	for (size_t i = 1; i < args.size(); i++)
211 	{
212 		i = FindNextParamArg(param, args, i);
213 		if (i < args.size())
214 		{
215 			std::string argext;
216 			M_ExtractFileExtension(args[i], argext);
217 
218 			if (ext[0] == '.' && stricmp(ext + 1, argext.c_str()) == 0)
219 				out.AppendArg(args[i].c_str());
220 			else if (ext[0] == 0 && argext.empty())
221 				out.AppendArg(args[i].c_str());
222 			else if (acceptNoExt && argext.empty())
223 				out.AppendArg(args[i].c_str());
224 		}
225 	}
226 
227 	return out;
228 }
229 
SetArgs(const char * cmdline)230 void DArgs::SetArgs(const char *cmdline)
231 {
232 	char *outputline, *q;
233 	char **outputargv;
234 	int outputargc;
235 
236 	args.clear();
237 
238 	if(!cmdline)
239 		return;
240 
241 	while (*cmdline && isspace(*cmdline))
242 		cmdline++;
243 
244 	if (!*cmdline)
245 		return;
246 
247 	outputline = (char *)Malloc((strlen(cmdline) + 1) * sizeof(char));
248 	outputargv = (char **)Malloc(((strlen(cmdline) + 1) / 2) * sizeof(char *));
249 
250 	const char *p = cmdline;
251 	q = outputline;
252 	outputargv[0] = NULL;
253 	outputargc = 1;
254 
255 	while (*p)
256 	{
257 		int quote;
258 
259 		while (*p && isspace(*p))
260 			p++;
261 
262 		if (!*p)
263 			break;
264 
265 		outputargv[outputargc] = q;
266 		outputargc++;
267 		quote = 0;
268 
269 		while (*p)
270 		{
271 			if (!quote && isspace(*p))
272 				break;
273 
274 			if (*p == '\\' || *p == '\"')
275 			{
276 				int slashes = 0, quotes = 0;
277 
278 				while (*p == '\\')
279 				{
280 					slashes++;
281 					p++;
282 				}
283 
284 				while (*p == '"')
285 				{
286 					quotes++;
287 					p++;
288 				}
289 
290 				if (!quotes)
291 					while (slashes--)
292 						*q++ = '\\';
293 				else
294 				{
295 					while (slashes >= 2)
296 					{
297 						slashes -= 2;
298 						*q++ = '\\';
299 					}
300 
301 					if (slashes)
302 					{
303 						quotes--;
304 						*q = '\"';
305 					}
306 
307 					if (quotes > 0)
308 					{
309 						if (!quote)
310 						{
311 							quotes--;
312 							quote = 1;
313 						}
314 
315 						for (int i = 3; i <= quotes + 1; i += 3)
316 							*q++ = '\"';
317 
318 						quote = (quotes % 3 == 0);
319 					}
320 				}
321 			}
322 			else
323 				*q++ = *p++;
324 		}
325 
326 		*q++ = '\0';
327 	}
328 
329 	CopyArgs(outputargc, outputargv);
330 
331 	M_Free(outputargv);
332 	M_Free(outputline);
333 
334 	return;
335 }
336 
337 //
338 // bond - PROC M_FindResponseFile
339 //
340 
341 static long ParseCommandLine (const char *args, int *argc, char **argv);
342 
M_FindResponseFile(void)343 void M_FindResponseFile (void)
344 {
345 	for (size_t i = 1; i < Args.NumArgs(); i++)
346 	{
347 		if (Args.GetArg(i)[0] == '@')
348 		{
349 			char	**argv;
350 			char	*file;
351 			int		argc;
352 			int		argcinresp;
353 			FILE	*handle;
354 			int 	size;
355 			long	argsize;
356 			size_t 	index;
357 
358 			// READ THE RESPONSE FILE INTO MEMORY
359 			handle = fopen (Args.GetArg(i) + 1,"rb");
360 			if (!handle)
361 			{ // [RH] Make this a warning, not an error.
362 				Printf (PRINT_HIGH,"No such response file (%s)!", Args.GetArg(i) + 1);
363 				continue;
364 			}
365 
366 			Printf (PRINT_HIGH,"Found response file %s!\n", Args.GetArg(i) + 1);
367 			fseek (handle, 0, SEEK_END);
368 			size = ftell (handle);
369 			fseek (handle, 0, SEEK_SET);
370 			file = new char[size+1];
371 			fread (file, size, 1, handle);
372 			file[size] = 0;
373 			fclose (handle);
374 
375 			argsize = ParseCommandLine (file, &argcinresp, NULL);
376 			argc = argcinresp + Args.NumArgs() - 1;
377 
378 			if (argc != 0)
379 			{
380 				argv = (char **)Malloc (argc*sizeof(char *) + argsize);
381 				argv[i] = (char *)argv + argc*sizeof(char *);
382 				ParseCommandLine (file, NULL, argv+i);
383 
384 				for (index = 0; index < i; ++index)
385 					argv[index] = (char*)Args.GetArg (index);
386 
387 				for (index = i + 1, i += argcinresp; index < Args.NumArgs (); ++index)
388 					argv[i++] = (char*)Args.GetArg (index);
389 
390 				DArgs newargs (i, argv);
391 				Args = newargs;
392 
393 				M_Free(argv);
394 			}
395 
396 			delete[] file;
397 
398 			// DISPLAY ARGS
399 			Printf (PRINT_HIGH,"%d command-line args:\n", Args.NumArgs ());
400 			for (size_t k = 1; k < Args.NumArgs (); k++)
401 				Printf (PRINT_HIGH,"%s\n", Args.GetArg (k));
402 
403 			break;
404 		}
405 	}
406 }
407 
408 // ParseCommandLine
409 //
410 // bond - This is just like the version in c_dispatch.cpp, except it does not
411 // do cvar expansion.
412 
ParseCommandLine(const char * args,int * argc,char ** argv)413 static long ParseCommandLine (const char *args, int *argc, char **argv)
414 {
415 	int count;
416 	char *buffplace;
417 
418 	count = 0;
419 	buffplace = NULL;
420 	if (argv != NULL)
421 	{
422 		buffplace = argv[0];
423 	}
424 
425 	for (;;)
426 	{
427 		while (*args <= ' ' && *args)
428 		{ // skip white space
429 			args++;
430 		}
431 		if (*args == 0)
432 		{
433 			break;
434 		}
435 		else if (*args == '\"')
436 		{ // read quoted string
437 			char stuff;
438 			if (argv != NULL)
439 			{
440 				argv[count] = buffplace;
441 			}
442 			count++;
443 			args++;
444 			do
445 			{
446 				stuff = *args++;
447 				if (stuff == '\\' && *args == '\"')
448 				{
449 					stuff = '\"', args++;
450 				}
451 				else if (stuff == '\"')
452 				{
453 					stuff = 0;
454 				}
455 				else if (stuff == 0)
456 				{
457 					args--;
458 				}
459 				if (argv != NULL)
460 				{
461 					*buffplace = stuff;
462 				}
463 				buffplace++;
464 			} while (stuff);
465 		}
466 		else
467 		{ // read unquoted string
468 			const char *start = args++, *end;
469 
470 			while (*args && *args > ' ' && *args != '\"')
471 				args++;
472 			end = args;
473 			if (argv != NULL)
474 			{
475 				argv[count] = buffplace;
476 				while (start < end)
477 					*buffplace++ = *start++;
478 				*buffplace++ = 0;
479 			}
480 			else
481 			{
482 				buffplace += end - start + 1;
483 			}
484 			count++;
485 		}
486 	}
487 	if (argc != NULL)
488 	{
489 		*argc = count;
490 	}
491 	return (long)(buffplace - (char *)0);
492 }
493 
494 VERSION_CONTROL (m_argv_cpp, "$Id: m_argv.cpp 4469 2014-01-03 23:38:29Z dr_sean $")
495 
496