Panda3D
Loading...
Searching...
No Matches
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"
23
24#include <windows.h>
25#include <tlhelp32.h>
26
27using std::string;
28
29/**
30 *
31 */
32Win32ArgParser::
33Win32ArgParser() :
34 _argv(nullptr),
35 _argc(0)
36{
37}
38
39/**
40 *
41 */
42Win32ArgParser::
43~Win32ArgParser() {
44 clear();
45}
46
47/**
48 * Resets the parser to empty command line and deallocates the internal argv
49 * array.
50 */
51void Win32ArgParser::
52clear() {
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 */
71void Win32ArgParser::
72set_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 */
101void Win32ArgParser::
102set_command_line(const std::wstring &command_line) {
103 TextEncoder encoder;
104 encoder.set_encoding(Filename::get_filesystem_encoding());
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 */
113void Win32ArgParser::
114set_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 */
126char **Win32ArgParser::
127get_argv() {
128 return _argv;
129}
130
131/**
132 * Returns the number of elements in the argv array.
133 */
134int Win32ArgParser::
135get_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 */
144bool Win32ArgParser::
145do_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 */
214string Win32ArgParser::
215parse_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 */
275void Win32ArgParser::
276parse_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 */
322void Win32ArgParser::
323save_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:44
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...
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.
void set_wtext(const std::wstring &wtext)
Changes the text that is stored in the encoder.
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.