Panda3D
|
00001 // Filename: win32ArgParser.cxx 00002 // Created by: drose (08Nov11) 00003 // 00004 //////////////////////////////////////////////////////////////////// 00005 // 00006 // PANDA 3D SOFTWARE 00007 // Copyright (c) Carnegie Mellon University. All rights reserved. 00008 // 00009 // All use of this software is subject to the terms of the revised BSD 00010 // license. You should have received a copy of this license along 00011 // with this source code in a file named "LICENSE." 00012 // 00013 //////////////////////////////////////////////////////////////////// 00014 00015 #include "win32ArgParser.h" 00016 00017 #ifdef _WIN32 00018 00019 #include "memoryBase.h" 00020 #include "textEncoder.h" 00021 #include "globPattern.h" 00022 #include "filename.h" 00023 #include "executionEnvironment.h" 00024 00025 #include <windows.h> 00026 #include <Tlhelp32.h> 00027 00028 //////////////////////////////////////////////////////////////////// 00029 // Function: Win32ArgParser::Constructor 00030 // Access: Public 00031 // Description: 00032 //////////////////////////////////////////////////////////////////// 00033 Win32ArgParser:: 00034 Win32ArgParser() : 00035 _argv(NULL), 00036 _argc(0) 00037 { 00038 } 00039 00040 //////////////////////////////////////////////////////////////////// 00041 // Function: Win32ArgParser::Destructor 00042 // Access: Public 00043 // Description: 00044 //////////////////////////////////////////////////////////////////// 00045 Win32ArgParser:: 00046 ~Win32ArgParser() { 00047 clear(); 00048 } 00049 00050 //////////////////////////////////////////////////////////////////// 00051 // Function: Win32ArgParser::clear 00052 // Access: Public 00053 // Description: Resets the parser to empty command line and 00054 // deallocates the internal argv array. 00055 //////////////////////////////////////////////////////////////////// 00056 void Win32ArgParser:: 00057 clear() { 00058 assert(_argc == (int)_args.size()); 00059 00060 if (_argv != NULL) { 00061 for (int i = 0; i < _argc; ++i) { 00062 PANDA_FREE_ARRAY(_argv[i]); 00063 } 00064 PANDA_FREE_ARRAY(_argv); 00065 _argv = NULL; 00066 } 00067 00068 _argc = 0; 00069 _args.clear(); 00070 } 00071 00072 //////////////////////////////////////////////////////////////////// 00073 // Function: Win32ArgParser::set_command_line 00074 // Access: Public 00075 // Description: Sets the string that indicates the full Win32 command 00076 // line, and starts parsing this into argc, argv. 00077 //////////////////////////////////////////////////////////////////// 00078 void Win32ArgParser:: 00079 set_command_line(const string &command_line) { 00080 clear(); 00081 00082 const char *p = command_line.c_str(); 00083 while (*p != '\0') { 00084 parse_unquoted_arg(p); 00085 00086 // Skip whitespace. 00087 while (*p != '\0' && isspace(*p)) { 00088 ++p; 00089 } 00090 } 00091 00092 assert(_argc == 0 && _argv == NULL); 00093 _argc = (int)_args.size(); 00094 _argv = (char **)PANDA_MALLOC_ARRAY(_argc * sizeof(char *)); 00095 for (int i = 0; i < _argc; ++i) { 00096 const string &arg = _args[i]; 00097 char *astr = (char *)PANDA_MALLOC_ARRAY(arg.size() + 1); 00098 memcpy(astr, arg.data(), arg.size()); 00099 astr[arg.size()] = '\0'; 00100 _argv[i] = astr; 00101 } 00102 } 00103 00104 //////////////////////////////////////////////////////////////////// 00105 // Function: Win32ArgParser::set_command_line 00106 // Access: Public 00107 // Description: Sets the Unicode string that indicates the full Win32 00108 // command line, and starts parsing this into argc, 00109 // argv. 00110 //////////////////////////////////////////////////////////////////// 00111 void Win32ArgParser:: 00112 set_command_line(const wstring &command_line) { 00113 TextEncoder encoder; 00114 encoder.set_encoding(Filename::get_filesystem_encoding()); 00115 encoder.set_wtext(command_line); 00116 set_command_line(encoder.get_text()); 00117 } 00118 00119 //////////////////////////////////////////////////////////////////// 00120 // Function: Win32ArgParser::set_system_command_line 00121 // Access: Public 00122 // Description: Tells the parser to call GetCommandLine() to query 00123 // the system command line string, and parse it into 00124 // argc, argv. 00125 //////////////////////////////////////////////////////////////////// 00126 void Win32ArgParser:: 00127 set_system_command_line() { 00128 LPWSTR command_line = GetCommandLineW(); 00129 set_command_line(command_line); 00130 } 00131 00132 //////////////////////////////////////////////////////////////////// 00133 // Function: Win32ArgParser::get_argv 00134 // Access: Public 00135 // Description: Returns the argv array as computed by 00136 // set_command_line() or set_system_command_line(). 00137 // This array indexes directly into data allocated 00138 // within the Win32ArgParser object; it will remain 00139 // valid until set_command_line() or clear() is again 00140 // called, or until the parser object destructs. 00141 //////////////////////////////////////////////////////////////////// 00142 char **Win32ArgParser:: 00143 get_argv() { 00144 return _argv; 00145 } 00146 00147 //////////////////////////////////////////////////////////////////// 00148 // Function: Win32ArgParser::get_argc 00149 // Access: Public 00150 // Description: Returns the number of elements in the argv array. 00151 //////////////////////////////////////////////////////////////////// 00152 int Win32ArgParser:: 00153 get_argc() { 00154 return _argc; 00155 } 00156 00157 //////////////////////////////////////////////////////////////////// 00158 // Function: Win32ArgParser::do_glob 00159 // Access: Public, Static 00160 // Description: Returns true if we should attempt to process (and 00161 // apply glob matching) to the command line, or false if 00162 // we should not (for instance, because it has already 00163 // been done by the shell). 00164 //////////////////////////////////////////////////////////////////// 00165 bool Win32ArgParser:: 00166 do_glob() { 00167 // First, we check for the PANDA_GLOB environment variable. If this 00168 // is present, it overrides any other checks: "0" means not to do 00169 // the glob, "1" means to do it. 00170 string envvar = ExecutionEnvironment::get_environment_variable("PANDA_GLOB"); 00171 if (!envvar.empty()) { 00172 istringstream strm(envvar); 00173 int value; 00174 strm >> value; 00175 if (!strm.fail()) { 00176 return (value != 0); 00177 } 00178 } 00179 00180 // Nothing explicit, so the default is to perform globbing only if 00181 // we were launched from the Windows default command shell, cmd.exe. 00182 // Presumably if we were launched from something else, like Python, 00183 // the caller won't expect globbing to be performed; and if we were 00184 // launched from a Cygwin shell, it will already have been 00185 // performed. 00186 00187 // Unfortunately, it is surprisingly difficult to determine the 00188 // parent process in Windows. We have to enumerate all of the 00189 // processes to find it. 00190 00191 HANDLE toolhelp = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); 00192 00193 PROCESSENTRY32 entry; 00194 memset(&entry, 0, sizeof(entry)); 00195 entry.dwSize = sizeof(entry); 00196 00197 DWORD current_id = GetCurrentProcessId(); 00198 DWORD parent_id = -1; 00199 00200 if (Process32First(toolhelp, &entry)) { 00201 do { 00202 if (entry.th32ProcessID == current_id) { 00203 parent_id = entry.th32ParentProcessID; 00204 break; 00205 } 00206 } while (Process32Next(toolhelp, &entry)); 00207 } 00208 00209 Filename parent_exe; 00210 if (parent_id != -1) { 00211 // Now we've got the parent process ID, go back through the list 00212 // to get its process name. 00213 if (Process32First(toolhelp, &entry)) { 00214 do { 00215 if (entry.th32ProcessID == parent_id) { 00216 parent_exe = Filename::from_os_specific(entry.szExeFile); 00217 break; 00218 } 00219 } while (Process32Next(toolhelp, &entry)); 00220 } 00221 } 00222 00223 CloseHandle(toolhelp); 00224 string basename = parent_exe.get_basename(); 00225 if (basename == "cmd.exe") { 00226 return true; 00227 } 00228 00229 return false; 00230 } 00231 00232 //////////////////////////////////////////////////////////////////// 00233 // Function: Win32ArgParser::parse_quoted_arg 00234 // Access: Private 00235 // Description: Parses the quoted argument beginning at p and returns 00236 // it. Advances p to the first character following the 00237 // close quote. 00238 //////////////////////////////////////////////////////////////////// 00239 string Win32ArgParser:: 00240 parse_quoted_arg(const char *&p) { 00241 char quote = *p; 00242 ++p; 00243 string result; 00244 00245 while (*p != '\0' && *p != quote) { 00246 // TODO: handle caret? What does it mean? 00247 00248 if (*p == '\\') { 00249 // A backslash is an escape character only when it precedes a 00250 // quote mark, or a series of backslashes precede a quote mark. 00251 int num_slashes = 1; 00252 ++p; 00253 while (*p == '\\') { 00254 ++p; 00255 ++num_slashes; 00256 } 00257 if (*p == quote) { 00258 // A series of backslashes precede a quote mark. This means 00259 // something special. First, each pair of backslashes means a 00260 // single backslash. 00261 for (int i = 0; i < num_slashes; i += 2) { 00262 result += '\\'; 00263 } 00264 // And if there's no odd backslashes left over, we've reached 00265 // the closing quote and we're done. 00266 if ((num_slashes & 1) == 0) { 00267 ++p; 00268 return result; 00269 } 00270 00271 // But if there's an odd backslash, it simply escapes the 00272 // quote mark. 00273 result += quote; 00274 ++p; 00275 00276 } else { 00277 // A series of backslashes not followed by a quote mark is 00278 // interpreted literally, not even counting them by twos, per 00279 // Win32's weird rules. 00280 for (int i = 0; i < num_slashes; ++i) { 00281 result += '\\'; 00282 } 00283 } 00284 00285 } else { 00286 // Neither a backslash nor a quote mark, so just interpret it 00287 // literally. 00288 result += *p; 00289 ++p; 00290 } 00291 } 00292 00293 if (*p == quote) { 00294 ++p; 00295 } 00296 00297 return result; 00298 } 00299 00300 //////////////////////////////////////////////////////////////////// 00301 // Function: Win32ArgParser::parse_unquoted_arg 00302 // Access: Private 00303 // Description: Parses the unquoted argument beginning at p and saves 00304 // it in _char_args. Advances p to the first whitespace 00305 // following the argument. 00306 //////////////////////////////////////////////////////////////////// 00307 void Win32ArgParser:: 00308 parse_unquoted_arg(const char *&p) { 00309 string result; 00310 bool contains_quotes = false; 00311 while (*p != '\0' && !isspace(*p)) { 00312 if (*p == '"') { 00313 // Begin a quoted sequence. 00314 contains_quotes = true; 00315 result += parse_quoted_arg(p); 00316 } else { 00317 // A regular character. 00318 result += *p; 00319 ++p; 00320 } 00321 } 00322 00323 Filename filename = Filename::from_os_specific(result); 00324 GlobPattern glob(filename); 00325 if (!contains_quotes && glob.has_glob_characters()) { 00326 // If the arg contains one or more glob characters (and no 00327 // quotation marks), we attempt to expand the files. This means 00328 // we interpret it as a Windows-specific filename. 00329 vector_string expand; 00330 if (glob.match_files(expand) != 0) { 00331 // The files matched. Add the expansions. 00332 vector_string::const_iterator ei; 00333 for (ei = expand.begin(); ei != expand.end(); ++ei) { 00334 Filename filename(*ei); 00335 save_arg(filename.to_os_specific()); 00336 } 00337 } else { 00338 // There wasn't a match. Just add the original, unexpanded 00339 // string, like bash does. 00340 save_arg(result); 00341 } 00342 00343 } else { 00344 // No glob characters means we just store it directly. Also, an 00345 // embedded quoted string, anywhere within the arg, means we can't 00346 // expand the glob characters. 00347 save_arg(result); 00348 } 00349 } 00350 00351 //////////////////////////////////////////////////////////////////// 00352 // Function: Win32ArgParser::save_arg 00353 // Access: Private 00354 // Description: Stores the indicated string as the next argument in 00355 // _args. 00356 //////////////////////////////////////////////////////////////////// 00357 void Win32ArgParser:: 00358 save_arg(const string &arg) { 00359 _args.push_back(arg); 00360 } 00361 00362 #endif // _WIN32