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