Panda3D
win32ArgParser.cxx
Go to the documentation of this file.
1 /**
2  * PANDA 3D SOFTWARE
3  * Copyright (c) Carnegie Mellon University. All rights reserved.
4  *
5  * All use of this software is subject to the terms of the revised BSD
6  * license. You should have received a copy of this license along
7  * with this source code in a file named "LICENSE."
8  *
9  * @file win32ArgParser.cxx
10  * @author drose
11  * @date 2011-11-08
12  */
13 
14 #include "win32ArgParser.h"
15 
16 #ifdef _WIN32
17 
18 #include "memoryBase.h"
19 #include "textEncoder.h"
20 #include "globPattern.h"
21 #include "filename.h"
22 #include "executionEnvironment.h"
23 
24 #include <windows.h>
25 #include <tlhelp32.h>
26 
27 using std::string;
28 
29 /**
30  *
31  */
32 Win32ArgParser::
33 Win32ArgParser() :
34  _argv(nullptr),
35  _argc(0)
36 {
37 }
38 
39 /**
40  *
41  */
42 Win32ArgParser::
43 ~Win32ArgParser() {
44  clear();
45 }
46 
47 /**
48  * Resets the parser to empty command line and deallocates the internal argv
49  * array.
50  */
51 void Win32ArgParser::
52 clear() {
53  assert(_argc == (int)_args.size());
54 
55  if (_argv != nullptr) {
56  for (int i = 0; i < _argc; ++i) {
57  PANDA_FREE_ARRAY(_argv[i]);
58  }
59  PANDA_FREE_ARRAY(_argv);
60  _argv = nullptr;
61  }
62 
63  _argc = 0;
64  _args.clear();
65 }
66 
67 /**
68  * Sets the string that indicates the full Win32 command line, and starts
69  * parsing this into argc, argv.
70  */
71 void Win32ArgParser::
72 set_command_line(const string &command_line) {
73  clear();
74 
75  const char *p = command_line.c_str();
76  while (*p != '\0') {
77  parse_unquoted_arg(p);
78 
79  // Skip whitespace.
80  while (*p != '\0' && isspace(*p)) {
81  ++p;
82  }
83  }
84 
85  assert(_argc == 0 && _argv == nullptr);
86  _argc = (int)_args.size();
87  _argv = (char **)PANDA_MALLOC_ARRAY(_argc * sizeof(char *));
88  for (int i = 0; i < _argc; ++i) {
89  const string &arg = _args[i];
90  char *astr = (char *)PANDA_MALLOC_ARRAY(arg.size() + 1);
91  memcpy(astr, arg.data(), arg.size());
92  astr[arg.size()] = '\0';
93  _argv[i] = astr;
94  }
95 }
96 
97 /**
98  * Sets the Unicode string that indicates the full Win32 command line, and
99  * starts parsing this into argc, argv.
100  */
101 void Win32ArgParser::
102 set_command_line(const std::wstring &command_line) {
103  TextEncoder encoder;
105  encoder.set_wtext(command_line);
106  set_command_line(encoder.get_text());
107 }
108 
109 /**
110  * Tells the parser to call GetCommandLine() to query the system command line
111  * string, and parse it into argc, argv.
112  */
113 void Win32ArgParser::
114 set_system_command_line() {
115  LPWSTR command_line = GetCommandLineW();
116  set_command_line(command_line);
117 }
118 
119 /**
120  * Returns the argv array as computed by set_command_line() or
121  * set_system_command_line(). This array indexes directly into data allocated
122  * within the Win32ArgParser object; it will remain valid until
123  * set_command_line() or clear() is again called, or until the parser object
124  * destructs.
125  */
126 char **Win32ArgParser::
127 get_argv() {
128  return _argv;
129 }
130 
131 /**
132  * Returns the number of elements in the argv array.
133  */
134 int Win32ArgParser::
135 get_argc() {
136  return _argc;
137 }
138 
139 /**
140  * Returns true if we should attempt to process (and apply glob matching) to
141  * the command line, or false if we should not (for instance, because it has
142  * already been done by the shell).
143  */
144 bool Win32ArgParser::
145 do_glob() {
146  // First, we check for the PANDA_GLOB environment variable. If this is
147  // present, it overrides any other checks: "0" means not to do the glob, "1"
148  // means to do it.
149  string envvar = ExecutionEnvironment::get_environment_variable("PANDA_GLOB");
150  if (!envvar.empty()) {
151  std::istringstream strm(envvar);
152  int value;
153  strm >> value;
154  if (!strm.fail()) {
155  return (value != 0);
156  }
157  }
158 
159  // Nothing explicit, so the default is to perform globbing only if we were
160  // launched from the Windows default command shell, cmd.exe. Presumably if
161  // we were launched from something else, like Python, the caller won't
162  // expect globbing to be performed; and if we were launched from a Cygwin
163  // shell, it will already have been performed.
164 
165  // Unfortunately, it is surprisingly difficult to determine the parent
166  // process in Windows. We have to enumerate all of the processes to find
167  // it.
168 
169  HANDLE toolhelp = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
170 
171  PROCESSENTRY32 entry;
172  memset(&entry, 0, sizeof(entry));
173  entry.dwSize = sizeof(entry);
174 
175  DWORD current_id = GetCurrentProcessId();
176  DWORD parent_id = -1;
177 
178  if (Process32First(toolhelp, &entry)) {
179  do {
180  if (entry.th32ProcessID == current_id) {
181  parent_id = entry.th32ParentProcessID;
182  break;
183  }
184  } while (Process32Next(toolhelp, &entry));
185  }
186 
187  Filename parent_exe;
188  if (parent_id != -1) {
189  // Now we've got the parent process ID, go back through the list to get
190  // its process name.
191  if (Process32First(toolhelp, &entry)) {
192  do {
193  if (entry.th32ProcessID == parent_id) {
194  parent_exe = Filename::from_os_specific(entry.szExeFile);
195  break;
196  }
197  } while (Process32Next(toolhelp, &entry));
198  }
199  }
200 
201  CloseHandle(toolhelp);
202  string basename = parent_exe.get_basename();
203  if (basename == "cmd.exe") {
204  return true;
205  }
206 
207  return false;
208 }
209 
210 /**
211  * Parses the quoted argument beginning at p and returns it. Advances p to
212  * the first character following the close quote.
213  */
214 string Win32ArgParser::
215 parse_quoted_arg(const char *&p) {
216  char quote = *p;
217  ++p;
218  string result;
219 
220  while (*p != '\0' && *p != quote) {
221  // TODO: handle caret? What does it mean?
222 
223  if (*p == '\\') {
224  // A backslash is an escape character only when it precedes a quote
225  // mark, or a series of backslashes precede a quote mark.
226  int num_slashes = 1;
227  ++p;
228  while (*p == '\\') {
229  ++p;
230  ++num_slashes;
231  }
232  if (*p == quote) {
233  // A series of backslashes precede a quote mark. This means something
234  // special. First, each pair of backslashes means a single backslash.
235  for (int i = 0; i < num_slashes; i += 2) {
236  result += '\\';
237  }
238  // And if there's no odd backslashes left over, we've reached the
239  // closing quote and we're done.
240  if ((num_slashes & 1) == 0) {
241  ++p;
242  return result;
243  }
244 
245  // But if there's an odd backslash, it simply escapes the quote mark.
246  result += quote;
247  ++p;
248 
249  } else {
250  // A series of backslashes not followed by a quote mark is interpreted
251  // literally, not even counting them by twos, per Win32's weird rules.
252  for (int i = 0; i < num_slashes; ++i) {
253  result += '\\';
254  }
255  }
256 
257  } else {
258  // Neither a backslash nor a quote mark, so just interpret it literally.
259  result += *p;
260  ++p;
261  }
262  }
263 
264  if (*p == quote) {
265  ++p;
266  }
267 
268  return result;
269 }
270 
271 /**
272  * Parses the unquoted argument beginning at p and saves it in _char_args.
273  * Advances p to the first whitespace following the argument.
274  */
275 void Win32ArgParser::
276 parse_unquoted_arg(const char *&p) {
277  string result;
278  bool contains_quotes = false;
279  while (*p != '\0' && !isspace(*p)) {
280  if (*p == '"') {
281  // Begin a quoted sequence.
282  contains_quotes = true;
283  result += parse_quoted_arg(p);
284  } else {
285  // A regular character.
286  result += *p;
287  ++p;
288  }
289  }
290 
291  Filename filename = Filename::from_os_specific(result);
292  GlobPattern glob(filename);
293  if (!contains_quotes && glob.has_glob_characters()) {
294  // If the arg contains one or more glob characters (and no quotation
295  // marks), we attempt to expand the files. This means we interpret it as
296  // a Windows-specific filename.
297  vector_string expand;
298  if (glob.match_files(expand) != 0) {
299  // The files matched. Add the expansions.
300  vector_string::const_iterator ei;
301  for (ei = expand.begin(); ei != expand.end(); ++ei) {
302  Filename filename(*ei);
303  save_arg(filename.to_os_specific());
304  }
305  } else {
306  // There wasn't a match. Just add the original, unexpanded string, like
307  // bash does.
308  save_arg(result);
309  }
310 
311  } else {
312  // No glob characters means we just store it directly. Also, an embedded
313  // quoted string, anywhere within the arg, means we can't expand the glob
314  // characters.
315  save_arg(result);
316  }
317 }
318 
319 /**
320  * Stores the indicated string as the next argument in _args.
321  */
322 void Win32ArgParser::
323 save_arg(const string &arg) {
324  _args.push_back(arg);
325 }
326 
327 #endif // _WIN32
get_environment_variable
Returns the definition of the indicated environment variable, or the empty string if the variable is ...
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:39
std::string get_basename() const
Returns the basename part of the filename.
Definition: filename.I:367
std::string to_os_specific() const
Converts the filename from our generic Unix-like convention (forward slashes starting with the root a...
Definition: filename.cxx:1123
static TextEncoder::Encoding get_filesystem_encoding()
Specifies the default encoding to be used for all subsequent Filenames objects.
Definition: filename.I:639
static Filename from_os_specific(const std::string &os_specific, Type type=T_general)
This named constructor returns a Panda-style filename (that is, using forward slashes,...
Definition: filename.cxx:328
This class can be used to test for string matches against standard Unix- shell filename globbing conv...
Definition: globPattern.h:32
This class can be used to convert text between multiple representations, e.g.
Definition: textEncoder.h:33
void set_encoding(Encoding encoding)
Specifies how the string set via set_text() is to be interpreted.
Definition: textEncoder.I:48
get_text
Returns the current text, as encoded via the current encoding system.
Definition: textEncoder.h:124
void set_wtext(const std::wstring &wtext)
Changes the text that is stored in the encoder.
Definition: textEncoder.I:443
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.