Panda3D
 All Classes Functions Variables Enumerations
win32ArgParser.cxx
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
 All Classes Functions Variables Enumerations