right_popen 1.0.2-x86-mswin32-60 → 1.0.5-x86-mswin32-60
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.rdoc +8 -3
 - data/Rakefile +5 -0
 - data/ext/win32/right_popen.c +238 -30
 - data/ext/win32/right_popen.h +21 -21
 - data/lib/right_popen.rb +35 -2
 - data/lib/win32/right_popen.rb +231 -36
 - data/lib/win32/right_popen.so +0 -0
 - data/right_popen.gemspec +15 -9
 - data/spec/print_env.rb +2 -0
 - data/spec/right_popen_spec.rb +102 -8
 - data/spec/spec_helper.rb +3 -1
 - metadata +10 -2
 
    
        data/README.rdoc
    CHANGED
    
    | 
         @@ -41,8 +41,13 @@ to report issues. 
     | 
|
| 
       41 
41 
     | 
    
         | 
| 
       42 
42 
     | 
    
         
             
              EM.run do
         
     | 
| 
       43 
43 
     | 
    
         
             
                EM.next_tick do
         
     | 
| 
       44 
     | 
    
         
            -
                   
     | 
| 
       45 
     | 
    
         
            -
                  RightScale.popen3( 
     | 
| 
      
 44 
     | 
    
         
            +
                  command = "ruby -e \"puts 'some stdout text'; $stderr.puts 'some stderr text'\; exit 99\""
         
     | 
| 
      
 45 
     | 
    
         
            +
                  RightScale.popen3(:command        => command,
         
     | 
| 
      
 46 
     | 
    
         
            +
                                    :target         => self,
         
     | 
| 
      
 47 
     | 
    
         
            +
                                    :environment    => nil,
         
     | 
| 
      
 48 
     | 
    
         
            +
                                    :stdout_handler => :on_read_stdout,
         
     | 
| 
      
 49 
     | 
    
         
            +
                                    :stderr_handler => :on_read_stderr,
         
     | 
| 
      
 50 
     | 
    
         
            +
                                    :exit_handler   => :on_exit)
         
     | 
| 
       46 
51 
     | 
    
         
             
                end
         
     | 
| 
       47 
52 
     | 
    
         
             
                timer = EM::PeriodicTimer.new(0.1) do
         
     | 
| 
       48 
53 
     | 
    
         
             
                  if @exit_status
         
     | 
| 
         @@ -90,7 +95,7 @@ The build can be tested using the RSpec gem. Create a link to the installed 
     | 
|
| 
       90 
95 
     | 
    
         
             
            under Windows) and run the following command from the gem directory to execute
         
     | 
| 
       91 
96 
     | 
    
         
             
            the RightPopen tests:
         
     | 
| 
       92 
97 
     | 
    
         | 
| 
       93 
     | 
    
         
            -
               
     | 
| 
      
 98 
     | 
    
         
            +
              rake spec
         
     | 
| 
       94 
99 
     | 
    
         | 
| 
       95 
100 
     | 
    
         | 
| 
       96 
101 
     | 
    
         
             
            == LICENSE
         
     | 
    
        data/Rakefile
    CHANGED
    
    
    
        data/ext/win32/right_popen.c
    CHANGED
    
    | 
         @@ -1,24 +1,24 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            ///////////////////////////////////////////////////////////////////////////////
         
     | 
| 
       2 
     | 
    
         
            -
            // Copyright (c) 2010 RightScale Inc
         
     | 
| 
       3 
     | 
    
         
            -
            //
         
     | 
| 
       4 
     | 
    
         
            -
            // Permission is hereby granted, free of charge, to any person obtaining
         
     | 
| 
       5 
     | 
    
         
            -
            // a copy of this software and associated documentation files (the
         
     | 
| 
       6 
     | 
    
         
            -
            // "Software"), to deal in the Software without restriction, including
         
     | 
| 
       7 
     | 
    
         
            -
            // without limitation the rights to use, copy, modify, merge, publish,
         
     | 
| 
       8 
     | 
    
         
            -
            // distribute, sublicense, and/or sell copies of the Software, and to
         
     | 
| 
       9 
     | 
    
         
            -
            // permit persons to whom the Software is furnished to do so, subject to
         
     | 
| 
       10 
     | 
    
         
            -
            // the following conditions:
         
     | 
| 
       11 
     | 
    
         
            -
            //
         
     | 
| 
       12 
     | 
    
         
            -
            // The above copyright notice and this permission notice shall be
         
     | 
| 
       13 
     | 
    
         
            -
            // included in all copies or substantial portions of the Software.
         
     | 
| 
       14 
     | 
    
         
            -
            //
         
     | 
| 
       15 
     | 
    
         
            -
            // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
         
     | 
| 
       16 
     | 
    
         
            -
            // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
         
     | 
| 
       17 
     | 
    
         
            -
            // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
         
     | 
| 
       18 
     | 
    
         
            -
            // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
         
     | 
| 
       19 
     | 
    
         
            -
            // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
         
     | 
| 
       20 
     | 
    
         
            -
            // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
         
     | 
| 
       21 
     | 
    
         
            -
            // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
         
     | 
| 
      
 1 
     | 
    
         
            +
            ///////////////////////////////////////////////////////////////////////////////
         
     | 
| 
      
 2 
     | 
    
         
            +
            // Copyright (c) 2010 RightScale Inc
         
     | 
| 
      
 3 
     | 
    
         
            +
            //
         
     | 
| 
      
 4 
     | 
    
         
            +
            // Permission is hereby granted, free of charge, to any person obtaining
         
     | 
| 
      
 5 
     | 
    
         
            +
            // a copy of this software and associated documentation files (the
         
     | 
| 
      
 6 
     | 
    
         
            +
            // "Software"), to deal in the Software without restriction, including
         
     | 
| 
      
 7 
     | 
    
         
            +
            // without limitation the rights to use, copy, modify, merge, publish,
         
     | 
| 
      
 8 
     | 
    
         
            +
            // distribute, sublicense, and/or sell copies of the Software, and to
         
     | 
| 
      
 9 
     | 
    
         
            +
            // permit persons to whom the Software is furnished to do so, subject to
         
     | 
| 
      
 10 
     | 
    
         
            +
            // the following conditions:
         
     | 
| 
      
 11 
     | 
    
         
            +
            //
         
     | 
| 
      
 12 
     | 
    
         
            +
            // The above copyright notice and this permission notice shall be
         
     | 
| 
      
 13 
     | 
    
         
            +
            // included in all copies or substantial portions of the Software.
         
     | 
| 
      
 14 
     | 
    
         
            +
            //
         
     | 
| 
      
 15 
     | 
    
         
            +
            // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
         
     | 
| 
      
 16 
     | 
    
         
            +
            // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
         
     | 
| 
      
 17 
     | 
    
         
            +
            // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
         
     | 
| 
      
 18 
     | 
    
         
            +
            // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
         
     | 
| 
      
 19 
     | 
    
         
            +
            // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
         
     | 
| 
      
 20 
     | 
    
         
            +
            // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
         
     | 
| 
      
 21 
     | 
    
         
            +
            // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
         
     | 
| 
       22 
22 
     | 
    
         
             
            ///////////////////////////////////////////////////////////////////////////////
         
     | 
| 
       23 
23 
     | 
    
         | 
| 
       24 
24 
     | 
    
         
             
            #include "right_popen.h"
         
     | 
| 
         @@ -60,6 +60,7 @@ static const DWORD CHILD_PROCESS_EXIT_WAIT_MSECS = 500;    // 0.5 secs 
     | 
|
| 
       60 
60 
     | 
    
         | 
| 
       61 
61 
     | 
    
         
             
            static Open3ProcessData* win32_process_data_list = NULL;
         
     | 
| 
       62 
62 
     | 
    
         
             
            static DWORD win32_named_pipe_serial_number = 1;
         
     | 
| 
      
 63 
     | 
    
         
            +
            static HMODULE hUserEnvLib = NULL;
         
     | 
| 
       63 
64 
     | 
    
         | 
| 
       64 
65 
     | 
    
         
             
            // Summary:
         
     | 
| 
       65 
66 
     | 
    
         
             
            //  allocates a new Ruby I/O object.
         
     | 
| 
         @@ -219,6 +220,10 @@ static VALUE right_popen_close_io_array(VALUE vRubyIoObjectArray) 
     | 
|
| 
       219 
220 
     | 
    
         
             
            //   bShowWindow
         
     | 
| 
       220 
221 
     | 
    
         
             
            //      true if process window is initially visible, false if process has no UI or is invisible
         
     | 
| 
       221 
222 
     | 
    
         
             
            //
         
     | 
| 
      
 223 
     | 
    
         
            +
            //   pszEnvironmentStrings
         
     | 
| 
      
 224 
     | 
    
         
            +
            //      enviroment strings in a double-nul terminated "str1\0str2\0...\0"
         
     | 
| 
      
 225 
     | 
    
         
            +
            //      block to give child process or NULL.
         
     | 
| 
      
 226 
     | 
    
         
            +
            //
         
     | 
| 
       222 
227 
     | 
    
         
             
            // Returns:
         
     | 
| 
       223 
228 
     | 
    
         
             
            //  true if successful, false otherwise (call GetLastError() for more information)
         
     | 
| 
       224 
229 
     | 
    
         
             
            static BOOL win32_create_process(char*     szCommand,
         
     | 
| 
         @@ -227,10 +232,12 @@ static BOOL win32_create_process(char*     szCommand, 
     | 
|
| 
       227 
232 
     | 
    
         
             
                                             HANDLE    hStderr,
         
     | 
| 
       228 
233 
     | 
    
         
             
                                             HANDLE*   phProcess,
         
     | 
| 
       229 
234 
     | 
    
         
             
                                             rb_pid_t* pPid,
         
     | 
| 
       230 
     | 
    
         
            -
                                             BOOL      bShowWindow 
     | 
| 
      
 235 
     | 
    
         
            +
                                             BOOL      bShowWindow,
         
     | 
| 
      
 236 
     | 
    
         
            +
                                             char*     pszEnvironmentStrings)
         
     | 
| 
       231 
237 
     | 
    
         
             
            {
         
     | 
| 
       232 
238 
     | 
    
         
             
                PROCESS_INFORMATION pi;
         
     | 
| 
       233 
239 
     | 
    
         
             
                STARTUPINFO si;
         
     | 
| 
      
 240 
     | 
    
         
            +
                BOOL bResult = FALSE;
         
     | 
| 
       234 
241 
     | 
    
         | 
| 
       235 
242 
     | 
    
         
             
                ZeroMemory(&si, sizeof(STARTUPINFO));
         
     | 
| 
       236 
243 
     | 
    
         | 
| 
         @@ -246,7 +253,7 @@ static BOOL win32_create_process(char*     szCommand, 
     | 
|
| 
       246 
253 
     | 
    
         
             
                                  NULL,
         
     | 
| 
       247 
254 
     | 
    
         
             
                                  TRUE,
         
     | 
| 
       248 
255 
     | 
    
         
             
                                  0,
         
     | 
| 
       249 
     | 
    
         
            -
                                   
     | 
| 
      
 256 
     | 
    
         
            +
                                  pszEnvironmentStrings,
         
     | 
| 
       250 
257 
     | 
    
         
             
                                  NULL,
         
     | 
| 
       251 
258 
     | 
    
         
             
                                  &si,
         
     | 
| 
       252 
259 
     | 
    
         
             
                                  &pi))
         
     | 
| 
         @@ -256,11 +263,11 @@ static BOOL win32_create_process(char*     szCommand, 
     | 
|
| 
       256 
263 
     | 
    
         | 
| 
       257 
264 
     | 
    
         
             
                    // Return process handle
         
     | 
| 
       258 
265 
     | 
    
         
             
                    *phProcess = pi.hProcess;
         
     | 
| 
       259 
     | 
    
         
            -
                    *pPid 
     | 
| 
       260 
     | 
    
         
            -
                     
     | 
| 
      
 266 
     | 
    
         
            +
                    *pPid      = (rb_pid_t)pi.dwProcessId;
         
     | 
| 
      
 267 
     | 
    
         
            +
                    bResult    = TRUE;
         
     | 
| 
       261 
268 
     | 
    
         
             
                }
         
     | 
| 
       262 
269 
     | 
    
         | 
| 
       263 
     | 
    
         
            -
                return  
     | 
| 
      
 270 
     | 
    
         
            +
                return bResult;
         
     | 
| 
       264 
271 
     | 
    
         
             
            }
         
     | 
| 
       265 
272 
     | 
    
         | 
| 
       266 
273 
     | 
    
         
             
            // Summary:
         
     | 
| 
         @@ -710,12 +717,16 @@ static VALUE ruby_create_io_object(rb_pid_t pid, int iFileMode, HANDLE hFile, BO 
     | 
|
| 
       710 
717 
     | 
    
         
             
            //          false to read synchronously, true to read asynchronously. see
         
     | 
| 
       711 
718 
     | 
    
         
             
            //          also RightPopen::async_read() (defaults to Qfalse).
         
     | 
| 
       712 
719 
     | 
    
         
             
            //
         
     | 
| 
      
 720 
     | 
    
         
            +
            //      pszEnvironmentStrings
         
     | 
| 
      
 721 
     | 
    
         
            +
            //          enviroment strings in a double-nul terminated "str1\0str2\0...\0"
         
     | 
| 
      
 722 
     | 
    
         
            +
            //          block to give child process or NULL.
         
     | 
| 
      
 723 
     | 
    
         
            +
            //
         
     | 
| 
       713 
724 
     | 
    
         
             
            // Returns:
         
     | 
| 
       714 
725 
     | 
    
         
             
            //  a Ruby array containing [stdin write, stdout read, stderr read, pid]
         
     | 
| 
       715 
726 
     | 
    
         
             
            //
         
     | 
| 
       716 
727 
     | 
    
         
             
            // Throws:
         
     | 
| 
       717 
728 
     | 
    
         
             
            //  raises a Ruby RuntimeError on failure
         
     | 
| 
       718 
     | 
    
         
            -
            static VALUE win32_popen4(char* szCommand, int iMode, BOOL bShowWindow, BOOL bAsynchronousOutput)
         
     | 
| 
      
 729 
     | 
    
         
            +
            static VALUE win32_popen4(char* szCommand, int iMode, BOOL bShowWindow, BOOL bAsynchronousOutput, char* pszEnvironmentStrings)
         
     | 
| 
       719 
730 
     | 
    
         
             
            {
         
     | 
| 
       720 
731 
     | 
    
         
             
                VALUE vReturnArray = Qnil;
         
     | 
| 
       721 
732 
     | 
    
         
             
                HANDLE hProcess = NULL;
         
     | 
| 
         @@ -733,7 +744,8 @@ static VALUE win32_popen4(char* szCommand, int iMode, BOOL bShowWindow, BOOL bAs 
     | 
|
| 
       733 
744 
     | 
    
         
             
                                              pData->childStderrPair.hWrite,
         
     | 
| 
       734 
745 
     | 
    
         
             
                                              &hProcess,
         
     | 
| 
       735 
746 
     | 
    
         
             
                                              &pid,
         
     | 
| 
       736 
     | 
    
         
            -
                                              bShowWindow 
     | 
| 
      
 747 
     | 
    
         
            +
                                              bShowWindow,
         
     | 
| 
      
 748 
     | 
    
         
            +
                                              pszEnvironmentStrings))
         
     | 
| 
       737 
749 
     | 
    
         
             
                {
         
     | 
| 
       738 
750 
     | 
    
         
             
                    DWORD dwLastError = GetLastError();
         
     | 
| 
       739 
751 
     | 
    
         
             
                    win32_free_process_data(pData);
         
     | 
| 
         @@ -819,10 +831,12 @@ static VALUE right_popen_popen4(int argc, VALUE *argv, VALUE klass) 
     | 
|
| 
       819 
831 
     | 
    
         
             
                VALUE vReturnArray = Qnil;
         
     | 
| 
       820 
832 
     | 
    
         
             
                VALUE vShowWindowFlag = Qfalse;
         
     | 
| 
       821 
833 
     | 
    
         
             
                VALUE vAsynchronousOutputFlag = Qfalse;
         
     | 
| 
      
 834 
     | 
    
         
            +
                VALUE vEnvironmentStrings = Qnil;
         
     | 
| 
       822 
835 
     | 
    
         
             
                int iMode = 0;
         
     | 
| 
       823 
836 
     | 
    
         
             
                char* mode = "t";
         
     | 
| 
      
 837 
     | 
    
         
            +
                char* pszEnvironmentStrings = NULL;
         
     | 
| 
       824 
838 
     | 
    
         | 
| 
       825 
     | 
    
         
            -
                rb_scan_args(argc, argv, " 
     | 
| 
      
 839 
     | 
    
         
            +
                rb_scan_args(argc, argv, "14", &vCommand, &vMode, &vShowWindowFlag, &vAsynchronousOutputFlag, &vEnvironmentStrings);
         
     | 
| 
       826 
840 
     | 
    
         | 
| 
       827 
841 
     | 
    
         
             
                if (!NIL_P(vMode))
         
     | 
| 
       828 
842 
     | 
    
         
             
                {
         
     | 
| 
         @@ -840,11 +854,16 @@ static VALUE right_popen_popen4(int argc, VALUE *argv, VALUE klass) 
     | 
|
| 
       840 
854 
     | 
    
         
             
                {
         
     | 
| 
       841 
855 
     | 
    
         
             
                    iMode = _O_BINARY;
         
     | 
| 
       842 
856 
     | 
    
         
             
                }
         
     | 
| 
      
 857 
     | 
    
         
            +
                if (!NIL_P(vEnvironmentStrings))
         
     | 
| 
      
 858 
     | 
    
         
            +
                {
         
     | 
| 
      
 859 
     | 
    
         
            +
                    pszEnvironmentStrings = StringValuePtr(vEnvironmentStrings);
         
     | 
| 
      
 860 
     | 
    
         
            +
                }
         
     | 
| 
       843 
861 
     | 
    
         | 
| 
       844 
862 
     | 
    
         
             
                vReturnArray = win32_popen4(StringValuePtr(vCommand),
         
     | 
| 
       845 
863 
     | 
    
         
             
                                            iMode,
         
     | 
| 
       846 
864 
     | 
    
         
             
                                            Qfalse != vShowWindowFlag,
         
     | 
| 
       847 
     | 
    
         
            -
                                            Qfalse != vAsynchronousOutputFlag 
     | 
| 
      
 865 
     | 
    
         
            +
                                            Qfalse != vAsynchronousOutputFlag,
         
     | 
| 
      
 866 
     | 
    
         
            +
                                            pszEnvironmentStrings);
         
     | 
| 
       848 
867 
     | 
    
         | 
| 
       849 
868 
     | 
    
         
             
                // ensure handles are closed in block form.
         
     | 
| 
       850 
869 
     | 
    
         
             
                if (rb_block_given_p())
         
     | 
| 
         @@ -985,6 +1004,193 @@ static VALUE right_popen_async_read(VALUE vSelf, VALUE vRubyIoObject) 
     | 
|
| 
       985 
1004 
     | 
    
         
             
                }
         
     | 
| 
       986 
1005 
     | 
    
         
             
            }
         
     | 
| 
       987 
1006 
     | 
    
         | 
| 
      
 1007 
     | 
    
         
            +
            // Summary:
         
     | 
| 
      
 1008 
     | 
    
         
            +
            //  scans the given nul-terminated block of nul-terminated Unicode strings to
         
     | 
| 
      
 1009 
     | 
    
         
            +
            //  convert the block from Unicode to Multi-Byte and determine the correct
         
     | 
| 
      
 1010 
     | 
    
         
            +
            //  length of the converted block. the converted block is then used to create
         
     | 
| 
      
 1011 
     | 
    
         
            +
            //  a Ruby string value containing multiple nul-terminated strings. in Ruby,
         
     | 
| 
      
 1012 
     | 
    
         
            +
            //  the block must be scanned again using index(0.chr, ...) logic.
         
     | 
| 
      
 1013 
     | 
    
         
            +
            //
         
     | 
| 
      
 1014 
     | 
    
         
            +
            // Returns:
         
     | 
| 
      
 1015 
     | 
    
         
            +
            //  a Ruby string representing the environment block
         
     | 
| 
      
 1016 
     | 
    
         
            +
            static VALUE win32_unicode_environment_block_to_ruby(const void* pvEnvironmentBlock)
         
     | 
| 
      
 1017 
     | 
    
         
            +
            {
         
     | 
| 
      
 1018 
     | 
    
         
            +
                const WCHAR* pszStart = (const WCHAR*)pvEnvironmentBlock;
         
     | 
| 
      
 1019 
     | 
    
         
            +
                const WCHAR* pszEnvString = pszStart;
         
     | 
| 
      
 1020 
     | 
    
         
            +
             
     | 
| 
      
 1021 
     | 
    
         
            +
                VALUE vResult = Qnil;
         
     | 
| 
      
 1022 
     | 
    
         
            +
             
     | 
| 
      
 1023 
     | 
    
         
            +
                while (*pszEnvString != 0)
         
     | 
| 
      
 1024 
     | 
    
         
            +
                {
         
     | 
| 
      
 1025 
     | 
    
         
            +
                    const int iEnvStringLength = wcslen(pszEnvString);
         
     | 
| 
      
 1026 
     | 
    
         
            +
             
     | 
| 
      
 1027 
     | 
    
         
            +
                    pszEnvString += iEnvStringLength + 1;
         
     | 
| 
      
 1028 
     | 
    
         
            +
                }
         
     | 
| 
      
 1029 
     | 
    
         
            +
             
     | 
| 
      
 1030 
     | 
    
         
            +
                // convert from wide to multi-byte.
         
     | 
| 
      
 1031 
     | 
    
         
            +
                {
         
     | 
| 
      
 1032 
     | 
    
         
            +
                    int iBlockLength = (int)(pszEnvString - pszStart);
         
     | 
| 
      
 1033 
     | 
    
         
            +
                    DWORD dwBufferLength = WideCharToMultiByte(CP_ACP, 0, pszStart, iBlockLength, NULL, 0, NULL, NULL);
         
     | 
| 
      
 1034 
     | 
    
         
            +
            	    char* pszBuffer = (char*)malloc(dwBufferLength + 2);
         
     | 
| 
      
 1035 
     | 
    
         
            +
             
     | 
| 
      
 1036 
     | 
    
         
            +
                    // FIX: the Ruby kernel appears to use the CP_ACP code page for
         
     | 
| 
      
 1037 
     | 
    
         
            +
                    // in-memory conversion, but I still have not seen a definitive
         
     | 
| 
      
 1038 
     | 
    
         
            +
                    // statement on which code page should be used.
         
     | 
| 
      
 1039 
     | 
    
         
            +
                    ZeroMemory(pszBuffer, dwBufferLength + 2);
         
     | 
| 
      
 1040 
     | 
    
         
            +
                    WideCharToMultiByte(CP_ACP, 0, pszStart, iBlockLength, pszBuffer, dwBufferLength + 2, NULL, NULL);
         
     | 
| 
      
 1041 
     | 
    
         
            +
                    vResult = rb_str_new(pszBuffer, dwBufferLength + 1);
         
     | 
| 
      
 1042 
     | 
    
         
            +
                    free(pszBuffer);
         
     | 
| 
      
 1043 
     | 
    
         
            +
                    pszBuffer = NULL;
         
     | 
| 
      
 1044 
     | 
    
         
            +
                }
         
     | 
| 
      
 1045 
     | 
    
         
            +
             
     | 
| 
      
 1046 
     | 
    
         
            +
                return vResult;
         
     | 
| 
      
 1047 
     | 
    
         
            +
            }
         
     | 
| 
      
 1048 
     | 
    
         
            +
             
     | 
| 
      
 1049 
     | 
    
         
            +
            // Summary:
         
     | 
| 
      
 1050 
     | 
    
         
            +
            //  scans the given nul-terminated block of nul-terminated Multi-Byte strings
         
     | 
| 
      
 1051 
     | 
    
         
            +
            //  to determine the correct length of the converted block. the converted block
         
     | 
| 
      
 1052 
     | 
    
         
            +
            //  is then used to create a Ruby string value containing multiple nul-
         
     | 
| 
      
 1053 
     | 
    
         
            +
            //  terminated strings. in Ruby, the block must be scanned again using
         
     | 
| 
      
 1054 
     | 
    
         
            +
            //  index(0.chr, ...) logic.
         
     | 
| 
      
 1055 
     | 
    
         
            +
            //
         
     | 
| 
      
 1056 
     | 
    
         
            +
            // Returns:
         
     | 
| 
      
 1057 
     | 
    
         
            +
            //  a Ruby string representing the environment block
         
     | 
| 
      
 1058 
     | 
    
         
            +
            static VALUE win32_multibyte_environment_block_to_ruby(const void* pvEnvironmentBlock)
         
     | 
| 
      
 1059 
     | 
    
         
            +
            {
         
     | 
| 
      
 1060 
     | 
    
         
            +
                const char* pszStart = (const char*)pvEnvironmentBlock;
         
     | 
| 
      
 1061 
     | 
    
         
            +
                const char* pszEnvString = pszStart;
         
     | 
| 
      
 1062 
     | 
    
         
            +
             
     | 
| 
      
 1063 
     | 
    
         
            +
                while (*pszEnvString != 0)
         
     | 
| 
      
 1064 
     | 
    
         
            +
                {
         
     | 
| 
      
 1065 
     | 
    
         
            +
                    const int iEnvStringLength = strlen(pszEnvString);
         
     | 
| 
      
 1066 
     | 
    
         
            +
             
     | 
| 
      
 1067 
     | 
    
         
            +
                    pszEnvString += iEnvStringLength + 1;
         
     | 
| 
      
 1068 
     | 
    
         
            +
                }
         
     | 
| 
      
 1069 
     | 
    
         
            +
             
     | 
| 
      
 1070 
     | 
    
         
            +
                // convert from wide to multi-byte.
         
     | 
| 
      
 1071 
     | 
    
         
            +
                {
         
     | 
| 
      
 1072 
     | 
    
         
            +
                    int iBlockLength = (int)(pszEnvString - pszStart);
         
     | 
| 
      
 1073 
     | 
    
         
            +
             
     | 
| 
      
 1074 
     | 
    
         
            +
                    return rb_str_new(pszStart, iBlockLength + 1);
         
     | 
| 
      
 1075 
     | 
    
         
            +
                }
         
     | 
| 
      
 1076 
     | 
    
         
            +
            }
         
     | 
| 
      
 1077 
     | 
    
         
            +
             
     | 
| 
      
 1078 
     | 
    
         
            +
            // Summary:
         
     | 
| 
      
 1079 
     | 
    
         
            +
            //  gets the environment strings from the registry for the current thread/process user.
         
     | 
| 
      
 1080 
     | 
    
         
            +
            //
         
     | 
| 
      
 1081 
     | 
    
         
            +
            // Returns:
         
     | 
| 
      
 1082 
     | 
    
         
            +
            //  nul-terminated block of nul-terminated environment strings as a Ruby string value.
         
     | 
| 
      
 1083 
     | 
    
         
            +
            static VALUE right_popen_get_current_user_environment(VALUE vSelf)
         
     | 
| 
      
 1084 
     | 
    
         
            +
            {
         
     | 
| 
      
 1085 
     | 
    
         
            +
                typedef BOOL (STDMETHODCALLTYPE FAR * LPFN_CREATEENVIRONMENTBLOCK)(LPVOID* lpEnvironment, HANDLE hToken, BOOL bInherit);
         
     | 
| 
      
 1086 
     | 
    
         
            +
                typedef BOOL (STDMETHODCALLTYPE FAR * LPFN_DESTROYENVIRONMENTBLOCK)(LPVOID lpEnvironment);
         
     | 
| 
      
 1087 
     | 
    
         
            +
             
     | 
| 
      
 1088 
     | 
    
         
            +
                HANDLE hToken = NULL;
         
     | 
| 
      
 1089 
     | 
    
         
            +
             
     | 
| 
      
 1090 
     | 
    
         
            +
                // dynamically load "userenv.dll" once (because the MSVC 6.0 compiler
         
     | 
| 
      
 1091 
     | 
    
         
            +
                // doesn't have the .h or .lib for it).
         
     | 
| 
      
 1092 
     | 
    
         
            +
                if (NULL == hUserEnvLib)
         
     | 
| 
      
 1093 
     | 
    
         
            +
                {
         
     | 
| 
      
 1094 
     | 
    
         
            +
                    // note we will intentionally leave library loaded for efficiency
         
     | 
| 
      
 1095 
     | 
    
         
            +
                    // reasons even though it is proper to call FreeLibrary().
         
     | 
| 
      
 1096 
     | 
    
         
            +
                    hUserEnvLib = LoadLibrary("userenv.dll");
         
     | 
| 
      
 1097 
     | 
    
         
            +
                    if (NULL == hUserEnvLib)
         
     | 
| 
      
 1098 
     | 
    
         
            +
                    {
         
     | 
| 
      
 1099 
     | 
    
         
            +
                        rb_raise(rb_eRuntimeError, "LoadLibrary() failed: %s", win32_error_description(GetLastError()));
         
     | 
| 
      
 1100 
     | 
    
         
            +
                    }
         
     | 
| 
      
 1101 
     | 
    
         
            +
                }
         
     | 
| 
      
 1102 
     | 
    
         
            +
             
     | 
| 
      
 1103 
     | 
    
         
            +
                // get the calling thread's access token.
         
     | 
| 
      
 1104 
     | 
    
         
            +
                if (FALSE == OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &hToken))
         
     | 
| 
      
 1105 
     | 
    
         
            +
                {
         
     | 
| 
      
 1106 
     | 
    
         
            +
                    switch (GetLastError())
         
     | 
| 
      
 1107 
     | 
    
         
            +
                    {
         
     | 
| 
      
 1108 
     | 
    
         
            +
                    case ERROR_NO_TOKEN:
         
     | 
| 
      
 1109 
     | 
    
         
            +
                        // retry against process token if no thread token exists.
         
     | 
| 
      
 1110 
     | 
    
         
            +
                        if (FALSE == OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
         
     | 
| 
      
 1111 
     | 
    
         
            +
                        {
         
     | 
| 
      
 1112 
     | 
    
         
            +
                            rb_raise(rb_eRuntimeError, "OpenProcessToken() failed: %s", win32_error_description(GetLastError()));
         
     | 
| 
      
 1113 
     | 
    
         
            +
                        }
         
     | 
| 
      
 1114 
     | 
    
         
            +
                        break;
         
     | 
| 
      
 1115 
     | 
    
         
            +
                    default:
         
     | 
| 
      
 1116 
     | 
    
         
            +
                        rb_raise(rb_eRuntimeError, "OpenThreadToken() failed: %s", win32_error_description(GetLastError()));
         
     | 
| 
      
 1117 
     | 
    
         
            +
                    }
         
     | 
| 
      
 1118 
     | 
    
         
            +
                }
         
     | 
| 
      
 1119 
     | 
    
         
            +
             
     | 
| 
      
 1120 
     | 
    
         
            +
                // get user's environment (from registry) without inheriting from the
         
     | 
| 
      
 1121 
     | 
    
         
            +
                // current process environment.
         
     | 
| 
      
 1122 
     | 
    
         
            +
                {
         
     | 
| 
      
 1123 
     | 
    
         
            +
                    LPVOID lpEnvironment = NULL;
         
     | 
| 
      
 1124 
     | 
    
         
            +
                    BOOL bResult = FALSE;
         
     | 
| 
      
 1125 
     | 
    
         
            +
                    {
         
     | 
| 
      
 1126 
     | 
    
         
            +
                        LPFN_CREATEENVIRONMENTBLOCK lpfnCreateEnvironmentBlock = (LPFN_CREATEENVIRONMENTBLOCK)GetProcAddress(hUserEnvLib, "CreateEnvironmentBlock");
         
     | 
| 
      
 1127 
     | 
    
         
            +
             
     | 
| 
      
 1128 
     | 
    
         
            +
                        if (NULL != lpfnCreateEnvironmentBlock)
         
     | 
| 
      
 1129 
     | 
    
         
            +
                        {
         
     | 
| 
      
 1130 
     | 
    
         
            +
                            bResult = lpfnCreateEnvironmentBlock(&lpEnvironment, hToken, FALSE);
         
     | 
| 
      
 1131 
     | 
    
         
            +
                        }
         
     | 
| 
      
 1132 
     | 
    
         
            +
                    }
         
     | 
| 
      
 1133 
     | 
    
         
            +
             
     | 
| 
      
 1134 
     | 
    
         
            +
                    // check result.
         
     | 
| 
      
 1135 
     | 
    
         
            +
                    {
         
     | 
| 
      
 1136 
     | 
    
         
            +
                        DWORD dwLastError = GetLastError();
         
     | 
| 
      
 1137 
     | 
    
         
            +
             
     | 
| 
      
 1138 
     | 
    
         
            +
                        CloseHandle(hToken);
         
     | 
| 
      
 1139 
     | 
    
         
            +
                        hToken = NULL;
         
     | 
| 
      
 1140 
     | 
    
         
            +
                        SetLastError(dwLastError);
         
     | 
| 
      
 1141 
     | 
    
         
            +
                        if (FALSE == bResult || NULL == lpEnvironment)
         
     | 
| 
      
 1142 
     | 
    
         
            +
                        {
         
     | 
| 
      
 1143 
     | 
    
         
            +
                            rb_raise(rb_eRuntimeError, "OpenThreadToken() failed: %s", win32_error_description(GetLastError()));
         
     | 
| 
      
 1144 
     | 
    
         
            +
                        }
         
     | 
| 
      
 1145 
     | 
    
         
            +
                    }
         
     | 
| 
      
 1146 
     | 
    
         
            +
             
     | 
| 
      
 1147 
     | 
    
         
            +
                    // merge environment.
         
     | 
| 
      
 1148 
     | 
    
         
            +
                    //
         
     | 
| 
      
 1149 
     | 
    
         
            +
                    // note that there is only a unicode form of this API call (instead of
         
     | 
| 
      
 1150 
     | 
    
         
            +
                    // the usual _A and _W pair) and that the environment strings appear to
         
     | 
| 
      
 1151 
     | 
    
         
            +
                    // always be Unicode (which the docs only hint at indirectly).
         
     | 
| 
      
 1152 
     | 
    
         
            +
                    {
         
     | 
| 
      
 1153 
     | 
    
         
            +
                        VALUE value = win32_unicode_environment_block_to_ruby(lpEnvironment);
         
     | 
| 
      
 1154 
     | 
    
         
            +
                        LPFN_DESTROYENVIRONMENTBLOCK lpfnDestroyEnvironmentBlock = (LPFN_DESTROYENVIRONMENTBLOCK)GetProcAddress(hUserEnvLib, "DestroyEnvironmentBlock");
         
     | 
| 
      
 1155 
     | 
    
         
            +
             
     | 
| 
      
 1156 
     | 
    
         
            +
                        if (NULL != lpfnDestroyEnvironmentBlock)
         
     | 
| 
      
 1157 
     | 
    
         
            +
                        {
         
     | 
| 
      
 1158 
     | 
    
         
            +
                            lpfnDestroyEnvironmentBlock(lpEnvironment);
         
     | 
| 
      
 1159 
     | 
    
         
            +
                            lpEnvironment = NULL;
         
     | 
| 
      
 1160 
     | 
    
         
            +
                        }
         
     | 
| 
      
 1161 
     | 
    
         
            +
                        CloseHandle(hToken);
         
     | 
| 
      
 1162 
     | 
    
         
            +
                        hToken = NULL;
         
     | 
| 
      
 1163 
     | 
    
         
            +
             
     | 
| 
      
 1164 
     | 
    
         
            +
                        return value;
         
     | 
| 
      
 1165 
     | 
    
         
            +
                    }
         
     | 
| 
      
 1166 
     | 
    
         
            +
                }
         
     | 
| 
      
 1167 
     | 
    
         
            +
            }
         
     | 
| 
      
 1168 
     | 
    
         
            +
             
     | 
| 
      
 1169 
     | 
    
         
            +
            // Summary:
         
     | 
| 
      
 1170 
     | 
    
         
            +
            //  gets the environment strings for the current process.
         
     | 
| 
      
 1171 
     | 
    
         
            +
            //
         
     | 
| 
      
 1172 
     | 
    
         
            +
            // Returns:
         
     | 
| 
      
 1173 
     | 
    
         
            +
            //  nul-terminated block of nul-terminated environment strings as a Ruby string value.
         
     | 
| 
      
 1174 
     | 
    
         
            +
            static VALUE right_popen_get_process_environment(VALUE vSelf)
         
     | 
| 
      
 1175 
     | 
    
         
            +
            {
         
     | 
| 
      
 1176 
     | 
    
         
            +
                char* lpEnvironment = GetEnvironmentStringsA();
         
     | 
| 
      
 1177 
     | 
    
         
            +
             
     | 
| 
      
 1178 
     | 
    
         
            +
                if (NULL == lpEnvironment)
         
     | 
| 
      
 1179 
     | 
    
         
            +
                {
         
     | 
| 
      
 1180 
     | 
    
         
            +
                    rb_raise(rb_eRuntimeError, "GetEnvironmentStringsA() failed: %s", win32_error_description(GetLastError()));
         
     | 
| 
      
 1181 
     | 
    
         
            +
                }
         
     | 
| 
      
 1182 
     | 
    
         
            +
             
     | 
| 
      
 1183 
     | 
    
         
            +
                // create a Ruby string from block.
         
     | 
| 
      
 1184 
     | 
    
         
            +
                {
         
     | 
| 
      
 1185 
     | 
    
         
            +
                    VALUE value = win32_multibyte_environment_block_to_ruby(lpEnvironment);
         
     | 
| 
      
 1186 
     | 
    
         
            +
             
     | 
| 
      
 1187 
     | 
    
         
            +
                    FreeEnvironmentStrings(lpEnvironment);
         
     | 
| 
      
 1188 
     | 
    
         
            +
                    lpEnvironment = NULL;
         
     | 
| 
      
 1189 
     | 
    
         
            +
             
     | 
| 
      
 1190 
     | 
    
         
            +
                    return value;
         
     | 
| 
      
 1191 
     | 
    
         
            +
                }
         
     | 
| 
      
 1192 
     | 
    
         
            +
            }
         
     | 
| 
      
 1193 
     | 
    
         
            +
             
     | 
| 
       988 
1194 
     | 
    
         
             
            // Summary:
         
     | 
| 
       989 
1195 
     | 
    
         
             
            //  'RightPopen' module entry point
         
     | 
| 
       990 
1196 
     | 
    
         
             
            void Init_right_popen()
         
     | 
| 
         @@ -993,4 +1199,6 @@ void Init_right_popen() 
     | 
|
| 
       993 
1199 
     | 
    
         | 
| 
       994 
1200 
     | 
    
         
             
                rb_define_module_function(vModule, "popen4", (VALUE(*)(ANYARGS))right_popen_popen4, -1);
         
     | 
| 
       995 
1201 
     | 
    
         
             
                rb_define_module_function(vModule, "async_read", (VALUE(*)(ANYARGS))right_popen_async_read, 1);
         
     | 
| 
      
 1202 
     | 
    
         
            +
                rb_define_module_function(vModule, "get_current_user_environment", (VALUE(*)(ANYARGS))right_popen_get_current_user_environment, 0);
         
     | 
| 
      
 1203 
     | 
    
         
            +
                rb_define_module_function(vModule, "get_process_environment", (VALUE(*)(ANYARGS))right_popen_get_process_environment, 0);
         
     | 
| 
       996 
1204 
     | 
    
         
             
            }
         
     | 
    
        data/ext/win32/right_popen.h
    CHANGED
    
    | 
         @@ -1,24 +1,24 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            ///////////////////////////////////////////////////////////////////////////////
         
     | 
| 
       2 
     | 
    
         
            -
            // Copyright (c) 2010 RightScale Inc
         
     | 
| 
       3 
     | 
    
         
            -
            //
         
     | 
| 
       4 
     | 
    
         
            -
            // Permission is hereby granted, free of charge, to any person obtaining
         
     | 
| 
       5 
     | 
    
         
            -
            // a copy of this software and associated documentation files (the
         
     | 
| 
       6 
     | 
    
         
            -
            // "Software"), to deal in the Software without restriction, including
         
     | 
| 
       7 
     | 
    
         
            -
            // without limitation the rights to use, copy, modify, merge, publish,
         
     | 
| 
       8 
     | 
    
         
            -
            // distribute, sublicense, and/or sell copies of the Software, and to
         
     | 
| 
       9 
     | 
    
         
            -
            // permit persons to whom the Software is furnished to do so, subject to
         
     | 
| 
       10 
     | 
    
         
            -
            // the following conditions:
         
     | 
| 
       11 
     | 
    
         
            -
            //
         
     | 
| 
       12 
     | 
    
         
            -
            // The above copyright notice and this permission notice shall be
         
     | 
| 
       13 
     | 
    
         
            -
            // included in all copies or substantial portions of the Software.
         
     | 
| 
       14 
     | 
    
         
            -
            //
         
     | 
| 
       15 
     | 
    
         
            -
            // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
         
     | 
| 
       16 
     | 
    
         
            -
            // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
         
     | 
| 
       17 
     | 
    
         
            -
            // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
         
     | 
| 
       18 
     | 
    
         
            -
            // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
         
     | 
| 
       19 
     | 
    
         
            -
            // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
         
     | 
| 
       20 
     | 
    
         
            -
            // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
         
     | 
| 
       21 
     | 
    
         
            -
            // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
         
     | 
| 
      
 1 
     | 
    
         
            +
            ///////////////////////////////////////////////////////////////////////////////
         
     | 
| 
      
 2 
     | 
    
         
            +
            // Copyright (c) 2010 RightScale Inc
         
     | 
| 
      
 3 
     | 
    
         
            +
            //
         
     | 
| 
      
 4 
     | 
    
         
            +
            // Permission is hereby granted, free of charge, to any person obtaining
         
     | 
| 
      
 5 
     | 
    
         
            +
            // a copy of this software and associated documentation files (the
         
     | 
| 
      
 6 
     | 
    
         
            +
            // "Software"), to deal in the Software without restriction, including
         
     | 
| 
      
 7 
     | 
    
         
            +
            // without limitation the rights to use, copy, modify, merge, publish,
         
     | 
| 
      
 8 
     | 
    
         
            +
            // distribute, sublicense, and/or sell copies of the Software, and to
         
     | 
| 
      
 9 
     | 
    
         
            +
            // permit persons to whom the Software is furnished to do so, subject to
         
     | 
| 
      
 10 
     | 
    
         
            +
            // the following conditions:
         
     | 
| 
      
 11 
     | 
    
         
            +
            //
         
     | 
| 
      
 12 
     | 
    
         
            +
            // The above copyright notice and this permission notice shall be
         
     | 
| 
      
 13 
     | 
    
         
            +
            // included in all copies or substantial portions of the Software.
         
     | 
| 
      
 14 
     | 
    
         
            +
            //
         
     | 
| 
      
 15 
     | 
    
         
            +
            // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
         
     | 
| 
      
 16 
     | 
    
         
            +
            // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
         
     | 
| 
      
 17 
     | 
    
         
            +
            // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
         
     | 
| 
      
 18 
     | 
    
         
            +
            // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
         
     | 
| 
      
 19 
     | 
    
         
            +
            // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
         
     | 
| 
      
 20 
     | 
    
         
            +
            // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
         
     | 
| 
      
 21 
     | 
    
         
            +
            // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
         
     | 
| 
       22 
22 
     | 
    
         
             
            ///////////////////////////////////////////////////////////////////////////////
         
     | 
| 
       23 
23 
     | 
    
         | 
| 
       24 
24 
     | 
    
         
             
            #include "ruby.h"
         
     | 
    
        data/lib/right_popen.rb
    CHANGED
    
    | 
         @@ -1,4 +1,4 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
             
     | 
| 
      
 1 
     | 
    
         
            +
            #--
         
     | 
| 
       2 
2 
     | 
    
         
             
            # Copyright (c) 2009 RightScale Inc
         
     | 
| 
       3 
3 
     | 
    
         
             
            #
         
     | 
| 
       4 
4 
     | 
    
         
             
            # Permission is hereby granted, free of charge, to any person obtaining
         
     | 
| 
         @@ -19,7 +19,7 @@ 
     | 
|
| 
       19 
19 
     | 
    
         
             
            # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
         
     | 
| 
       20 
20 
     | 
    
         
             
            # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
         
     | 
| 
       21 
21 
     | 
    
         
             
            # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
         
     | 
| 
       22 
     | 
    
         
            -
             
     | 
| 
      
 22 
     | 
    
         
            +
            #++
         
     | 
| 
       23 
23 
     | 
    
         | 
| 
       24 
24 
     | 
    
         
             
            # RightScale.popen3 allows running external processes aynchronously
         
     | 
| 
       25 
25 
     | 
    
         
             
            # while still capturing their standard and error outputs.
         
     | 
| 
         @@ -30,3 +30,36 @@ if RUBY_PLATFORM =~ /mswin/ 
     | 
|
| 
       30 
30 
     | 
    
         
             
            else
         
     | 
| 
       31 
31 
     | 
    
         
             
              require File.expand_path(File.join(File.dirname(__FILE__), 'linux', 'right_popen'))
         
     | 
| 
       32 
32 
     | 
    
         
             
            end
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
            module RightScale
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
              # Spawn process to run given command asynchronously, hooking all three
         
     | 
| 
      
 37 
     | 
    
         
            +
              # standard streams of the child process.
         
     | 
| 
      
 38 
     | 
    
         
            +
              #
         
     | 
| 
      
 39 
     | 
    
         
            +
              # Streams the command's stdout and stderr to the given handlers. Time-
         
     | 
| 
      
 40 
     | 
    
         
            +
              # ordering of bytes sent to stdout and stderr is not preserved.
         
     | 
| 
      
 41 
     | 
    
         
            +
              #
         
     | 
| 
      
 42 
     | 
    
         
            +
              # Calls given exit handler upon command process termination, passing in the
         
     | 
| 
      
 43 
     | 
    
         
            +
              # resulting Process::Status.
         
     | 
| 
      
 44 
     | 
    
         
            +
              #
         
     | 
| 
      
 45 
     | 
    
         
            +
              # All handlers must be methods exposed by the given target.
         
     | 
| 
      
 46 
     | 
    
         
            +
              #
         
     | 
| 
      
 47 
     | 
    
         
            +
              # === Parameters
         
     | 
| 
      
 48 
     | 
    
         
            +
              # options[:command](String):: Command to execute, including any arguments
         
     | 
| 
      
 49 
     | 
    
         
            +
              # options[:environment](Hash):: Hash of environment variables values keyed by name
         
     | 
| 
      
 50 
     | 
    
         
            +
              # options[:target](Object):: object defining handler methods to be called, optional (no handlers can be defined if not specified)
         
     | 
| 
      
 51 
     | 
    
         
            +
              # options[:stdout_handler](String):: Stdout handler method name, optional
         
     | 
| 
      
 52 
     | 
    
         
            +
              # options[:stderr_handler](String):: Stderr handler method name, optional
         
     | 
| 
      
 53 
     | 
    
         
            +
              # options[:exit_handler](String):: Exit handler method name, optional
         
     | 
| 
      
 54 
     | 
    
         
            +
              #
         
     | 
| 
      
 55 
     | 
    
         
            +
              # === Returns
         
     | 
| 
      
 56 
     | 
    
         
            +
              # true:: Always returns true
         
     | 
| 
      
 57 
     | 
    
         
            +
              def self.popen3(options)
         
     | 
| 
      
 58 
     | 
    
         
            +
                raise "EventMachine reactor must be started" unless EM.reactor_running?
         
     | 
| 
      
 59 
     | 
    
         
            +
                raise "Missing command" unless options[:command]
         
     | 
| 
      
 60 
     | 
    
         
            +
                raise "Missing target" unless options[:target] || !options[:stdout_handler] && !options[:stderr_handler] && !options[:exit_handler]
         
     | 
| 
      
 61 
     | 
    
         
            +
                RightScale.popen3_imp(options)
         
     | 
| 
      
 62 
     | 
    
         
            +
                true
         
     | 
| 
      
 63 
     | 
    
         
            +
              end
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/win32/right_popen.rb
    CHANGED
    
    | 
         @@ -1,4 +1,4 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
             
     | 
| 
      
 1 
     | 
    
         
            +
            #--
         
     | 
| 
       2 
2 
     | 
    
         
             
            # Copyright (c) 2009 RightScale Inc
         
     | 
| 
       3 
3 
     | 
    
         
             
            #
         
     | 
| 
       4 
4 
     | 
    
         
             
            # Permission is hereby granted, free of charge, to any person obtaining
         
     | 
| 
         @@ -19,17 +19,13 @@ 
     | 
|
| 
       19 
19 
     | 
    
         
             
            # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
         
     | 
| 
       20 
20 
     | 
    
         
             
            # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
         
     | 
| 
       21 
21 
     | 
    
         
             
            # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
         
     | 
| 
       22 
     | 
    
         
            -
             
     | 
| 
       23 
     | 
    
         
            -
             
     | 
| 
       24 
     | 
    
         
            -
            # RightScale.popen3 allows running external processes aynchronously
         
     | 
| 
       25 
     | 
    
         
            -
            # while still capturing their standard and error outputs.
         
     | 
| 
       26 
     | 
    
         
            -
            # It relies on EventMachine for most of its internal mechanisms.
         
     | 
| 
      
 22 
     | 
    
         
            +
            #++
         
     | 
| 
       27 
23 
     | 
    
         | 
| 
       28 
24 
     | 
    
         
             
            require 'rubygems'
         
     | 
| 
       29 
25 
     | 
    
         
             
            begin
         
     | 
| 
       30 
26 
     | 
    
         
             
              gem 'eventmachine', '=0.12.8.1'  # patched version for Windows-only socket close fix
         
     | 
| 
       31 
27 
     | 
    
         
             
            rescue Gem::LoadError
         
     | 
| 
       32 
     | 
    
         
            -
              gem 'eventmachine', '=0.12.8'  # notify_readable is deprecated  
     | 
| 
      
 28 
     | 
    
         
            +
              gem 'eventmachine', '=0.12.8'  # notify_readable is deprecated, so currently cannot use >=0.12.10 in Windows gem
         
     | 
| 
       33 
29 
     | 
    
         
             
            end
         
     | 
| 
       34 
30 
     | 
    
         
             
            require 'eventmachine'
         
     | 
| 
       35 
31 
     | 
    
         
             
            require 'win32/process'
         
     | 
| 
         @@ -81,15 +77,10 @@ module RightScale 
     | 
|
| 
       81 
77 
     | 
    
         | 
| 
       82 
78 
     | 
    
         
             
                # === Parameters
         
     | 
| 
       83 
79 
     | 
    
         
             
                # target(Object):: Object defining handler methods to be called.
         
     | 
| 
       84 
     | 
    
         
            -
                #
         
     | 
| 
       85 
80 
     | 
    
         
             
                # stdout_handler(String):: Token for stdout handler method name.
         
     | 
| 
       86 
     | 
    
         
            -
                #
         
     | 
| 
       87 
81 
     | 
    
         
             
                # exit_handler(String):: Token for exit handler method name.
         
     | 
| 
       88 
     | 
    
         
            -
                #
         
     | 
| 
       89 
82 
     | 
    
         
             
                # stderr_eventable(Connector):: EM object representing stderr handler.
         
     | 
| 
       90 
     | 
    
         
            -
                #
         
     | 
| 
       91 
83 
     | 
    
         
             
                # stream_out(IO):: Standard output stream.
         
     | 
| 
       92 
     | 
    
         
            -
                #
         
     | 
| 
       93 
84 
     | 
    
         
             
                # pid(Integer):: Child process ID.
         
     | 
| 
       94 
85 
     | 
    
         
             
                def initialize(target, stdout_handler, exit_handler, stderr_eventable, stream_out, pid)
         
     | 
| 
       95 
86 
     | 
    
         
             
                  @target = target
         
     | 
| 
         @@ -186,34 +177,20 @@ module RightScale 
     | 
|
| 
       186 
177 
     | 
    
         
             
              # Creates a child process and connects event handlers to the standard output
         
     | 
| 
       187 
178 
     | 
    
         
             
              # and error streams used by the created process. Connectors use named pipes
         
     | 
| 
       188 
179 
     | 
    
         
             
              # and asynchronous I/O in the native Windows implementation.
         
     | 
| 
       189 
     | 
    
         
            -
              # 
         
     | 
| 
       190 
     | 
    
         
            -
              # Streams the command's stdout and stderr to the given handlers. Time-
         
     | 
| 
       191 
     | 
    
         
            -
              # ordering of bytes sent to stdout and stderr is not preserved.
         
     | 
| 
       192 
     | 
    
         
            -
              #
         
     | 
| 
       193 
     | 
    
         
            -
              # Calls given exit handler upon command process termination, passing in the
         
     | 
| 
       194 
     | 
    
         
            -
              # resulting Process::Status.
         
     | 
| 
       195 
     | 
    
         
            -
              #
         
     | 
| 
       196 
     | 
    
         
            -
              # All handlers must be methods exposed by the given target.
         
     | 
| 
       197 
180 
     | 
    
         
             
              #
         
     | 
| 
       198 
     | 
    
         
            -
              #  
     | 
| 
       199 
     | 
    
         
            -
               
     | 
| 
       200 
     | 
    
         
            -
              #
         
     | 
| 
       201 
     | 
    
         
            -
              # target(Object): object defining handler methods to be called.
         
     | 
| 
       202 
     | 
    
         
            -
              #
         
     | 
| 
       203 
     | 
    
         
            -
              # stdout_handler(String): token for stdout handler method name.
         
     | 
| 
       204 
     | 
    
         
            -
              #
         
     | 
| 
       205 
     | 
    
         
            -
              # stderr_handler(String): token for stderr handler method name.
         
     | 
| 
       206 
     | 
    
         
            -
              #
         
     | 
| 
       207 
     | 
    
         
            -
              # exit_handler(String): token for exit handler method name.
         
     | 
| 
       208 
     | 
    
         
            -
              def self.popen3(cmd, target, stdout_handler = nil, stderr_handler = nil, exit_handler = nil)
         
     | 
| 
      
 181 
     | 
    
         
            +
              # See RightScale.popen3
         
     | 
| 
      
 182 
     | 
    
         
            +
              def self.popen3_imp(options)
         
     | 
| 
       209 
183 
     | 
    
         
             
                raise "EventMachine reactor must be started" unless EM.reactor_running?
         
     | 
| 
       210 
184 
     | 
    
         | 
| 
       211 
     | 
    
         
            -
                #  
     | 
| 
       212 
     | 
    
         
            -
                 
     | 
| 
      
 185 
     | 
    
         
            +
                # merge and format environment strings, if necessary.
         
     | 
| 
      
 186 
     | 
    
         
            +
                environment_hash = options[:environment] || {}
         
     | 
| 
      
 187 
     | 
    
         
            +
                environment_strings = RightPopenEx.merge_environment(environment_hash)
         
     | 
| 
      
 188 
     | 
    
         
            +
             
     | 
| 
      
 189 
     | 
    
         
            +
                # launch cmd and request asynchronous output.
         
     | 
| 
       213 
190 
     | 
    
         
             
                mode = "t"
         
     | 
| 
       214 
191 
     | 
    
         
             
                show_window = false
         
     | 
| 
       215 
192 
     | 
    
         
             
                asynchronous_output = true
         
     | 
| 
       216 
     | 
    
         
            -
                stream_in, stream_out, stream_err, pid = RightPopen.popen4( 
     | 
| 
      
 193 
     | 
    
         
            +
                stream_in, stream_out, stream_err, pid = RightPopen.popen4(options[:command], mode, show_window, asynchronous_output, environment_strings)
         
     | 
| 
       217 
194 
     | 
    
         | 
| 
       218 
195 
     | 
    
         
             
                # close input immediately.
         
     | 
| 
       219 
196 
     | 
    
         
             
                stream_in.close
         
     | 
| 
         @@ -221,8 +198,8 @@ module RightScale 
     | 
|
| 
       221 
198 
     | 
    
         
             
                # attach handlers to event machine and let it monitor incoming data. the
         
     | 
| 
       222 
199 
     | 
    
         
             
                # streams aren't used directly by the connectors except that they are closed
         
     | 
| 
       223 
200 
     | 
    
         
             
                # on unbind.
         
     | 
| 
       224 
     | 
    
         
            -
                stderr_eventable = EM.attach(stream_err, StdErrHandler, target, stderr_handler, stream_err) if stderr_handler
         
     | 
| 
       225 
     | 
    
         
            -
                EM.attach(stream_out, StdOutHandler, target, stdout_handler, exit_handler, stderr_eventable, stream_out, pid)
         
     | 
| 
      
 201 
     | 
    
         
            +
                stderr_eventable = EM.attach(stream_err, StdErrHandler, options[:target], options[:stderr_handler], stream_err) if options[:stderr_handler]
         
     | 
| 
      
 202 
     | 
    
         
            +
                EM.attach(stream_out, StdOutHandler, options[:target], options[:stdout_handler], options[:exit_handler], stderr_eventable, stream_out, pid)
         
     | 
| 
       226 
203 
     | 
    
         | 
| 
       227 
204 
     | 
    
         
             
                # note that control returns to the caller, but the launched cmd continues
         
     | 
| 
       228 
205 
     | 
    
         
             
                # running and sends output to the handlers. the caller is not responsible
         
     | 
| 
         @@ -231,4 +208,222 @@ module RightScale 
     | 
|
| 
       231 
208 
     | 
    
         
             
                # sent to the exit_handler on process termination.
         
     | 
| 
       232 
209 
     | 
    
         
             
              end
         
     | 
| 
       233 
210 
     | 
    
         | 
| 
      
 211 
     | 
    
         
            +
              protected
         
     | 
| 
      
 212 
     | 
    
         
            +
             
     | 
| 
      
 213 
     | 
    
         
            +
              module RightPopenEx
         
     | 
| 
      
 214 
     | 
    
         
            +
                # Key class for case-insensitive hash insertion/lookup.
         
     | 
| 
      
 215 
     | 
    
         
            +
                class NoCaseKey
         
     | 
| 
      
 216 
     | 
    
         
            +
                  # Internal key
         
     | 
| 
      
 217 
     | 
    
         
            +
                  attr_reader :key
         
     | 
| 
      
 218 
     | 
    
         
            +
             
     | 
| 
      
 219 
     | 
    
         
            +
                  # Stringizes object to be used as key
         
     | 
| 
      
 220 
     | 
    
         
            +
                  def initialize key
         
     | 
| 
      
 221 
     | 
    
         
            +
                    @key = key.to_s
         
     | 
| 
      
 222 
     | 
    
         
            +
                   end
         
     | 
| 
      
 223 
     | 
    
         
            +
             
     | 
| 
      
 224 
     | 
    
         
            +
                  # Hash code
         
     | 
| 
      
 225 
     | 
    
         
            +
                  def hash
         
     | 
| 
      
 226 
     | 
    
         
            +
                    @key.downcase.hash
         
     | 
| 
      
 227 
     | 
    
         
            +
                  end
         
     | 
| 
      
 228 
     | 
    
         
            +
             
     | 
| 
      
 229 
     | 
    
         
            +
                  # Equality for hash
         
     | 
| 
      
 230 
     | 
    
         
            +
                  def eql? other
         
     | 
| 
      
 231 
     | 
    
         
            +
                    @key.downcase.hash == other.key.downcase.hash
         
     | 
| 
      
 232 
     | 
    
         
            +
                  end
         
     | 
| 
      
 233 
     | 
    
         
            +
             
     | 
| 
      
 234 
     | 
    
         
            +
                  # Sort operator
         
     | 
| 
      
 235 
     | 
    
         
            +
                  def <=> other
         
     | 
| 
      
 236 
     | 
    
         
            +
                    @key.downcase <=> other.key.downcase
         
     | 
| 
      
 237 
     | 
    
         
            +
                  end
         
     | 
| 
      
 238 
     | 
    
         
            +
             
     | 
| 
      
 239 
     | 
    
         
            +
                  # Stringizer
         
     | 
| 
      
 240 
     | 
    
         
            +
                  def to_s
         
     | 
| 
      
 241 
     | 
    
         
            +
                    @key
         
     | 
| 
      
 242 
     | 
    
         
            +
                  end
         
     | 
| 
      
 243 
     | 
    
         
            +
             
     | 
| 
      
 244 
     | 
    
         
            +
                  # Inspector
         
     | 
| 
      
 245 
     | 
    
         
            +
                  def inspect
         
     | 
| 
      
 246 
     | 
    
         
            +
                    "\"#{@key}\""
         
     | 
| 
      
 247 
     | 
    
         
            +
                  end
         
     | 
| 
      
 248 
     | 
    
         
            +
                end
         
     | 
| 
      
 249 
     | 
    
         
            +
             
     | 
| 
      
 250 
     | 
    
         
            +
                # Hash of known environment variable keys to special merge method proc.
         
     | 
| 
      
 251 
     | 
    
         
            +
                SPECIAL_MERGE_ENV_KEY_HASH = {
         
     | 
| 
      
 252 
     | 
    
         
            +
                  NoCaseKey.new('PATH') => lambda { |from_value, to_value| merge_environment_path_value(from_value, to_value) }
         
     | 
| 
      
 253 
     | 
    
         
            +
                }
         
     | 
| 
      
 254 
     | 
    
         
            +
             
     | 
| 
      
 255 
     | 
    
         
            +
                # Merges the given environment hash with the current environment for this
         
     | 
| 
      
 256 
     | 
    
         
            +
                # process and the current environment for the current thread user from the
         
     | 
| 
      
 257 
     | 
    
         
            +
                # registry. The result is a nul-terminated block of nul-terminated strings
         
     | 
| 
      
 258 
     | 
    
         
            +
                # suitable for use in creating the child process.
         
     | 
| 
      
 259 
     | 
    
         
            +
                #
         
     | 
| 
      
 260 
     | 
    
         
            +
                # === Parameters
         
     | 
| 
      
 261 
     | 
    
         
            +
                # environment_hash(Hash):: Hash of environment key/value pairs or empty to
         
     | 
| 
      
 262 
     | 
    
         
            +
                # only merge the current process and currend thread user environment.
         
     | 
| 
      
 263 
     | 
    
         
            +
                #
         
     | 
| 
      
 264 
     | 
    
         
            +
                # === Returns
         
     | 
| 
      
 265 
     | 
    
         
            +
                # merged string block
         
     | 
| 
      
 266 
     | 
    
         
            +
                def self.merge_environment(environment_hash)
         
     | 
| 
      
 267 
     | 
    
         
            +
                  current_user_environment_hash = get_current_user_environment
         
     | 
| 
      
 268 
     | 
    
         
            +
                  result_environment_hash = get_process_environment
         
     | 
| 
      
 269 
     | 
    
         
            +
             
     | 
| 
      
 270 
     | 
    
         
            +
                  # user environment from registry supercedes process.
         
     | 
| 
      
 271 
     | 
    
         
            +
                  merge_environment2(current_user_environment_hash, result_environment_hash)
         
     | 
| 
      
 272 
     | 
    
         
            +
             
     | 
| 
      
 273 
     | 
    
         
            +
                  # caller's environment supercedes all.
         
     | 
| 
      
 274 
     | 
    
         
            +
                  merge_environment2(environment_hash, result_environment_hash)
         
     | 
| 
      
 275 
     | 
    
         
            +
             
     | 
| 
      
 276 
     | 
    
         
            +
                  return environment_hash_to_string_block(result_environment_hash)
         
     | 
| 
      
 277 
     | 
    
         
            +
                end
         
     | 
| 
      
 278 
     | 
    
         
            +
             
     | 
| 
      
 279 
     | 
    
         
            +
                # Merges from hash to another with special handling for known env vars.
         
     | 
| 
      
 280 
     | 
    
         
            +
                #
         
     | 
| 
      
 281 
     | 
    
         
            +
                # === Parameters
         
     | 
| 
      
 282 
     | 
    
         
            +
                # from_hash(Hash):: hash of string or environment keys to environment values
         
     | 
| 
      
 283 
     | 
    
         
            +
                # to_hash(Hash):: resulting hash or environment keys to environment values
         
     | 
| 
      
 284 
     | 
    
         
            +
                #
         
     | 
| 
      
 285 
     | 
    
         
            +
                # === Returns
         
     | 
| 
      
 286 
     | 
    
         
            +
                # to_hash(Hash):: merged 'to' hash
         
     | 
| 
      
 287 
     | 
    
         
            +
                def self.merge_environment2(from_hash, to_hash)
         
     | 
| 
      
 288 
     | 
    
         
            +
                  from_hash.each do |from_key, from_value|
         
     | 
| 
      
 289 
     | 
    
         
            +
                    to_key = from_key.kind_of?(NoCaseKey) ?
         
     | 
| 
      
 290 
     | 
    
         
            +
                             from_key :
         
     | 
| 
      
 291 
     | 
    
         
            +
                             NoCaseKey.new(from_key)
         
     | 
| 
      
 292 
     | 
    
         
            +
                    to_value = to_hash[to_key]
         
     | 
| 
      
 293 
     | 
    
         
            +
                    if to_value
         
     | 
| 
      
 294 
     | 
    
         
            +
                      special_merge_proc = SPECIAL_MERGE_ENV_KEY_HASH[to_key]
         
     | 
| 
      
 295 
     | 
    
         
            +
                      if special_merge_proc
         
     | 
| 
      
 296 
     | 
    
         
            +
                        # special merge
         
     | 
| 
      
 297 
     | 
    
         
            +
                        to_hash[to_key] = special_merge_proc.call(from_value, to_value)
         
     | 
| 
      
 298 
     | 
    
         
            +
                      else
         
     | 
| 
      
 299 
     | 
    
         
            +
                        # 'from' value supercedes existing 'to' value
         
     | 
| 
      
 300 
     | 
    
         
            +
                        to_hash[to_key] = from_value
         
     | 
| 
      
 301 
     | 
    
         
            +
                      end
         
     | 
| 
      
 302 
     | 
    
         
            +
                    else
         
     | 
| 
      
 303 
     | 
    
         
            +
                      # 'from' value replaces missing 'to' value
         
     | 
| 
      
 304 
     | 
    
         
            +
                      to_hash[to_key] = from_value
         
     | 
| 
      
 305 
     | 
    
         
            +
                    end
         
     | 
| 
      
 306 
     | 
    
         
            +
                  end
         
     | 
| 
      
 307 
     | 
    
         
            +
                end
         
     | 
| 
      
 308 
     | 
    
         
            +
             
     | 
| 
      
 309 
     | 
    
         
            +
                # Merges a PATH-style variable by appending any missing subpaths on the
         
     | 
| 
      
 310 
     | 
    
         
            +
                # 'to' side to the value on the 'from' side in order of appearance. note
         
     | 
| 
      
 311 
     | 
    
         
            +
                # that the ordering of paths on the 'to' side is not preserved when some of
         
     | 
| 
      
 312 
     | 
    
         
            +
                # the paths also appear on the 'from' side. This is because paths on the
         
     | 
| 
      
 313 
     | 
    
         
            +
                # 'from' side always take precedence. This is an issue if two paths
         
     | 
| 
      
 314 
     | 
    
         
            +
                # reference similarly named executables and swapping the order of paths
         
     | 
| 
      
 315 
     | 
    
         
            +
                # would cause the wrong executable to be invoked. To resolve this, the
         
     | 
| 
      
 316 
     | 
    
         
            +
                # higher precedence path can be changed to ensure that the conflicting paths
         
     | 
| 
      
 317 
     | 
    
         
            +
                # are both specified in the proper order. There is no trivial algorithm
         
     | 
| 
      
 318 
     | 
    
         
            +
                # which can predict the proper ordering of such paths.
         
     | 
| 
      
 319 
     | 
    
         
            +
                #
         
     | 
| 
      
 320 
     | 
    
         
            +
                # === Parameters
         
     | 
| 
      
 321 
     | 
    
         
            +
                # from_value(String):: value to merge from
         
     | 
| 
      
 322 
     | 
    
         
            +
                # to_value(String):: value to merge to
         
     | 
| 
      
 323 
     | 
    
         
            +
                #
         
     | 
| 
      
 324 
     | 
    
         
            +
                # === Returns
         
     | 
| 
      
 325 
     | 
    
         
            +
                # merged_value(String):: merged value
         
     | 
| 
      
 326 
     | 
    
         
            +
                def self.merge_environment_path_value(from_value, to_value)
         
     | 
| 
      
 327 
     | 
    
         
            +
                  # normalize to backslashes for Windows-style PATH variable.
         
     | 
| 
      
 328 
     | 
    
         
            +
                  from_value = from_value.gsub(File::SEPARATOR, File::ALT_SEPARATOR)
         
     | 
| 
      
 329 
     | 
    
         
            +
                  to_value = to_value.gsub(File::SEPARATOR, File::ALT_SEPARATOR)
         
     | 
| 
      
 330 
     | 
    
         
            +
             
     | 
| 
      
 331 
     | 
    
         
            +
                  # quick outs.
         
     | 
| 
      
 332 
     | 
    
         
            +
                  return from_value if to_value.empty?
         
     | 
| 
      
 333 
     | 
    
         
            +
                  return to_value if from_value.empty?
         
     | 
| 
      
 334 
     | 
    
         
            +
             
     | 
| 
      
 335 
     | 
    
         
            +
                  # Windows paths are case-insensitive, so we want to match paths efficiently
         
     | 
| 
      
 336 
     | 
    
         
            +
                  # while being case-insensitive. we will make use of NoCaseKey again.
         
     | 
| 
      
 337 
     | 
    
         
            +
                  from_value_hash = {}
         
     | 
| 
      
 338 
     | 
    
         
            +
                  from_value.split(File::PATH_SEPARATOR).each { |path| from_value_hash[NoCaseKey.new(path)] = true }
         
     | 
| 
      
 339 
     | 
    
         
            +
                  appender = ""
         
     | 
| 
      
 340 
     | 
    
         
            +
                  to_value.split(File::PATH_SEPARATOR).each do |path|
         
     | 
| 
      
 341 
     | 
    
         
            +
                    if not from_value_hash[NoCaseKey.new(path)]
         
     | 
| 
      
 342 
     | 
    
         
            +
                      appender += File::PATH_SEPARATOR + path
         
     | 
| 
      
 343 
     | 
    
         
            +
                    end
         
     | 
| 
      
 344 
     | 
    
         
            +
                  end
         
     | 
| 
      
 345 
     | 
    
         
            +
             
     | 
| 
      
 346 
     | 
    
         
            +
                  return from_value + appender
         
     | 
| 
      
 347 
     | 
    
         
            +
                end
         
     | 
| 
      
 348 
     | 
    
         
            +
             
     | 
| 
      
 349 
     | 
    
         
            +
                # Queries the environment strings from the current thread/process user's
         
     | 
| 
      
 350 
     | 
    
         
            +
                # environment (which is stored in the registry on Windows as a combination of
         
     | 
| 
      
 351 
     | 
    
         
            +
                # system and user-specific environment variables). The resulting hash
         
     | 
| 
      
 352 
     | 
    
         
            +
                # represents any variables set for the persisted user context but any set
         
     | 
| 
      
 353 
     | 
    
         
            +
                # dynamically in the current process context.
         
     | 
| 
      
 354 
     | 
    
         
            +
                #
         
     | 
| 
      
 355 
     | 
    
         
            +
                # === Returns
         
     | 
| 
      
 356 
     | 
    
         
            +
                # environment_hash(Hash):: hash of environment key (String) to value (String).
         
     | 
| 
      
 357 
     | 
    
         
            +
                def self.get_current_user_environment
         
     | 
| 
      
 358 
     | 
    
         
            +
                  environment_strings = RightPopen.get_current_user_environment
         
     | 
| 
      
 359 
     | 
    
         
            +
             
     | 
| 
      
 360 
     | 
    
         
            +
                  return string_block_to_environment_hash(environment_strings)
         
     | 
| 
      
 361 
     | 
    
         
            +
                end
         
     | 
| 
      
 362 
     | 
    
         
            +
             
     | 
| 
      
 363 
     | 
    
         
            +
                # Queries the environment strings from the process environment (which is kept
         
     | 
| 
      
 364 
     | 
    
         
            +
                # in memory for each process and generally begins life as a copy of the
         
     | 
| 
      
 365 
     | 
    
         
            +
                # process user's environment context plus any changes made by ancestral
         
     | 
| 
      
 366 
     | 
    
         
            +
                # processes).
         
     | 
| 
      
 367 
     | 
    
         
            +
                #
         
     | 
| 
      
 368 
     | 
    
         
            +
                # === Returns
         
     | 
| 
      
 369 
     | 
    
         
            +
                # environment_hash(Hash):: hash of environment key (String) to value (String).
         
     | 
| 
      
 370 
     | 
    
         
            +
                def self.get_process_environment
         
     | 
| 
      
 371 
     | 
    
         
            +
                  environment_strings = RightPopen.get_process_environment
         
     | 
| 
      
 372 
     | 
    
         
            +
             
     | 
| 
      
 373 
     | 
    
         
            +
                  return string_block_to_environment_hash(environment_strings)
         
     | 
| 
      
 374 
     | 
    
         
            +
                end
         
     | 
| 
      
 375 
     | 
    
         
            +
             
     | 
| 
      
 376 
     | 
    
         
            +
                # Converts a nul-terminated block of nul-terminated strings to a hash by
         
     | 
| 
      
 377 
     | 
    
         
            +
                # splitting the block on nul characters until the empty string is found.
         
     | 
| 
      
 378 
     | 
    
         
            +
                # splits substrings on the '=' character which is used to delimit key from
         
     | 
| 
      
 379 
     | 
    
         
            +
                # value in Windows environment blocks.
         
     | 
| 
      
 380 
     | 
    
         
            +
                #
         
     | 
| 
      
 381 
     | 
    
         
            +
                # === Paramters
         
     | 
| 
      
 382 
     | 
    
         
            +
                # string_block(String):: string containing nul-terminated substrings followed
         
     | 
| 
      
 383 
     | 
    
         
            +
                # by a nul-terminator.
         
     | 
| 
      
 384 
     | 
    
         
            +
                #
         
     | 
| 
      
 385 
     | 
    
         
            +
                # === Returns
         
     | 
| 
      
 386 
     | 
    
         
            +
                # string_hash(Hash):: hash of string to string
         
     | 
| 
      
 387 
     | 
    
         
            +
                def self.string_block_to_environment_hash(string_block)
         
     | 
| 
      
 388 
     | 
    
         
            +
                  result_hash = {}
         
     | 
| 
      
 389 
     | 
    
         
            +
                  last_offset = 0
         
     | 
| 
      
 390 
     | 
    
         
            +
                  string_block_length = string_block.length
         
     | 
| 
      
 391 
     | 
    
         
            +
                  while last_offset < string_block_length
         
     | 
| 
      
 392 
     | 
    
         
            +
                    offset = string_block.index(0.chr, last_offset)
         
     | 
| 
      
 393 
     | 
    
         
            +
                    if offset.nil?
         
     | 
| 
      
 394 
     | 
    
         
            +
                      offset = string_block.length
         
     | 
| 
      
 395 
     | 
    
         
            +
                    end
         
     | 
| 
      
 396 
     | 
    
         
            +
                    env_string = string_block[last_offset, offset - last_offset]
         
     | 
| 
      
 397 
     | 
    
         
            +
                    break if env_string.empty?
         
     | 
| 
      
 398 
     | 
    
         
            +
                    last_offset = offset + 1
         
     | 
| 
      
 399 
     | 
    
         
            +
             
     | 
| 
      
 400 
     | 
    
         
            +
                    # note that Windows uses "=C:=C:\" notation for working directory info, so
         
     | 
| 
      
 401 
     | 
    
         
            +
                    # ignore equals if it is the first character.
         
     | 
| 
      
 402 
     | 
    
         
            +
                    equals_offset = env_string.index('=', 1)
         
     | 
| 
      
 403 
     | 
    
         
            +
                    if equals_offset
         
     | 
| 
      
 404 
     | 
    
         
            +
                      env_key = env_string[0, equals_offset]
         
     | 
| 
      
 405 
     | 
    
         
            +
                      env_value = env_string[equals_offset + 1..-1]
         
     | 
| 
      
 406 
     | 
    
         
            +
                      result_hash[NoCaseKey.new(env_key)] = env_value
         
     | 
| 
      
 407 
     | 
    
         
            +
                    end
         
     | 
| 
      
 408 
     | 
    
         
            +
                  end
         
     | 
| 
      
 409 
     | 
    
         
            +
             
     | 
| 
      
 410 
     | 
    
         
            +
                  return result_hash
         
     | 
| 
      
 411 
     | 
    
         
            +
                end
         
     | 
| 
      
 412 
     | 
    
         
            +
             
     | 
| 
      
 413 
     | 
    
         
            +
                # Converts a hash of string to string to a string block by combining pairs
         
     | 
| 
      
 414 
     | 
    
         
            +
                # into a single string delimited by the '=' character and then placing nul-
         
     | 
| 
      
 415 
     | 
    
         
            +
                # terminators after each pair, followed by a final nul-terminator.
         
     | 
| 
      
 416 
     | 
    
         
            +
                #
         
     | 
| 
      
 417 
     | 
    
         
            +
                # === Parameters
         
     | 
| 
      
 418 
     | 
    
         
            +
                # environment_hash(Hash):: hash of
         
     | 
| 
      
 419 
     | 
    
         
            +
                def self.environment_hash_to_string_block(environment_hash)
         
     | 
| 
      
 420 
     | 
    
         
            +
                  result_block = ""
         
     | 
| 
      
 421 
     | 
    
         
            +
                  environment_hash.keys.sort.each do |key|
         
     | 
| 
      
 422 
     | 
    
         
            +
                    result_block += "#{key}=#{environment_hash[key]}\0"
         
     | 
| 
      
 423 
     | 
    
         
            +
                  end
         
     | 
| 
      
 424 
     | 
    
         
            +
             
     | 
| 
      
 425 
     | 
    
         
            +
                  return result_block + "\0"
         
     | 
| 
      
 426 
     | 
    
         
            +
                end
         
     | 
| 
      
 427 
     | 
    
         
            +
             
     | 
| 
      
 428 
     | 
    
         
            +
              end
         
     | 
| 
       234 
429 
     | 
    
         
             
            end
         
     | 
    
        data/lib/win32/right_popen.so
    CHANGED
    
    | 
         Binary file 
     | 
    
        data/right_popen.gemspec
    CHANGED
    
    | 
         @@ -1,14 +1,16 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            require 'rubygems'
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
       3 
     | 
    
         
            -
             
     | 
| 
       4 
     | 
    
         
            -
               
     | 
| 
      
 3 
     | 
    
         
            +
            def is_windows?
         
     | 
| 
      
 4 
     | 
    
         
            +
              return RUBY_PLATFORM =~ /mswin/
         
     | 
| 
      
 5 
     | 
    
         
            +
            end
         
     | 
| 
       5 
6 
     | 
    
         | 
| 
      
 7 
     | 
    
         
            +
            spec = Gem::Specification.new do |spec|
         
     | 
| 
       6 
8 
     | 
    
         
             
              spec.name      = 'right_popen'
         
     | 
| 
       7 
     | 
    
         
            -
              spec.version   = '1.0. 
     | 
| 
      
 9 
     | 
    
         
            +
              spec.version   = '1.0.5'
         
     | 
| 
       8 
10 
     | 
    
         
             
              spec.authors   = ['Scott Messier', 'Raphael Simon']
         
     | 
| 
       9 
11 
     | 
    
         
             
              spec.email     = 'scott@rightscale.com'
         
     | 
| 
       10 
12 
     | 
    
         
             
              spec.homepage  = 'https://github.com/rightscale/right_popen'
         
     | 
| 
       11 
     | 
    
         
            -
              if is_windows
         
     | 
| 
      
 13 
     | 
    
         
            +
              if is_windows?
         
     | 
| 
       12 
14 
     | 
    
         
             
                spec.platform = 'x86-mswin32-60'
         
     | 
| 
       13 
15 
     | 
    
         
             
              else
         
     | 
| 
       14 
16 
     | 
    
         
             
                spec.platform  = Gem::Platform::RUBY
         
     | 
| 
         @@ -27,7 +29,7 @@ of its internal mechanisms. The Linux implementation is valid for any Linux 
     | 
|
| 
       27 
29 
     | 
    
         
             
            platform but there is also a native implementation for Windows platforms.
         
     | 
| 
       28 
30 
     | 
    
         
             
            EOF
         
     | 
| 
       29 
31 
     | 
    
         | 
| 
       30 
     | 
    
         
            -
              if is_windows
         
     | 
| 
      
 32 
     | 
    
         
            +
              if is_windows?
         
     | 
| 
       31 
33 
     | 
    
         
             
                extension_dir = "ext,"
         
     | 
| 
       32 
34 
     | 
    
         
             
              else
         
     | 
| 
       33 
35 
     | 
    
         
             
                extension_dir = ""
         
     | 
| 
         @@ -38,7 +40,7 @@ EOF 
     | 
|
| 
       38 
40 
     | 
    
         
             
                item.include?("Makefile") || item.include?(".obj") || item.include?(".pdb") || item.include?(".def") || item.include?(".exp") || item.include?(".lib")
         
     | 
| 
       39 
41 
     | 
    
         
             
              end
         
     | 
| 
       40 
42 
     | 
    
         
             
              candidates = candidates.delete_if do |item|
         
     | 
| 
       41 
     | 
    
         
            -
                if is_windows
         
     | 
| 
      
 43 
     | 
    
         
            +
                if is_windows?
         
     | 
| 
       42 
44 
     | 
    
         
             
                  item.include?("/linux/")
         
     | 
| 
       43 
45 
     | 
    
         
             
                else
         
     | 
| 
       44 
46 
     | 
    
         
             
                  item.include?("/win32/")
         
     | 
| 
         @@ -46,9 +48,13 @@ EOF 
     | 
|
| 
       46 
48 
     | 
    
         
             
              end
         
     | 
| 
       47 
49 
     | 
    
         
             
              spec.files = candidates.sort!
         
     | 
| 
       48 
50 
     | 
    
         | 
| 
       49 
     | 
    
         
            -
              # Current implementation  
     | 
| 
       50 
     | 
    
         
            -
              spec.add_runtime_dependency(%q<eventmachine>, [">= 0.12.8" 
     | 
| 
       51 
     | 
    
         
            -
              if is_windows
         
     | 
| 
      
 51 
     | 
    
         
            +
              # Current implementation supports >= 0.12.8
         
     | 
| 
      
 52 
     | 
    
         
            +
              spec.add_runtime_dependency(%q<eventmachine>, [">= 0.12.8"])
         
     | 
| 
      
 53 
     | 
    
         
            +
              if is_windows?
         
     | 
| 
      
 54 
     | 
    
         
            +
                # Windows implementation currently depends on deprecated behavior from
         
     | 
| 
      
 55 
     | 
    
         
            +
                # 0.12.8, but we also need to support the 0.12.8.1 patch version. the Linux
         
     | 
| 
      
 56 
     | 
    
         
            +
                # side is free to use 0.12.10+
         
     | 
| 
      
 57 
     | 
    
         
            +
                spec.add_runtime_dependency(%q<eventmachine>, ["< 0.12.9"])
         
     | 
| 
       52 
58 
     | 
    
         
             
                spec.add_runtime_dependency(%q<win32-process>, [">= 0.6.1"])
         
     | 
| 
       53 
59 
     | 
    
         
             
              end
         
     | 
| 
       54 
60 
     | 
    
         
             
            end
         
     | 
    
        data/spec/print_env.rb
    ADDED
    
    
    
        data/spec/right_popen_spec.rb
    CHANGED
    
    | 
         @@ -1,5 +1,4 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            require File.join(File.dirname(__FILE__), 'spec_helper')
         
     | 
| 
       2 
     | 
    
         
            -
            require 'right_popen'
         
     | 
| 
       3 
2 
     | 
    
         | 
| 
       4 
3 
     | 
    
         
             
            RUBY_CMD         = 'ruby'
         
     | 
| 
       5 
4 
     | 
    
         
             
            STANDARD_MESSAGE = 'Standard message'
         
     | 
| 
         @@ -10,32 +9,82 @@ EXIT_STATUS      = 146 
     | 
|
| 
       10 
9 
     | 
    
         
             
            # for a quick smoke test
         
     | 
| 
       11 
10 
     | 
    
         
             
            LARGE_OUTPUT_COUNTER = 1000
         
     | 
| 
       12 
11 
     | 
    
         | 
| 
      
 12 
     | 
    
         
            +
            # bump up count for most exhaustive leak detection.
         
     | 
| 
      
 13 
     | 
    
         
            +
            REPEAT_TEST_COUNTER = 256
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
            def is_windows?
         
     | 
| 
      
 16 
     | 
    
         
            +
              return RUBY_PLATFORM =~ /mswin/
         
     | 
| 
      
 17 
     | 
    
         
            +
            end
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
       13 
19 
     | 
    
         
             
            describe 'RightScale::popen3' do
         
     | 
| 
       14 
20 
     | 
    
         | 
| 
       15 
21 
     | 
    
         
             
              module RightPopenSpec
         
     | 
| 
       16 
22 
     | 
    
         | 
| 
       17 
23 
     | 
    
         
             
                class Runner
         
     | 
| 
       18 
24 
     | 
    
         
             
                  def initialize
         
     | 
| 
       19 
     | 
    
         
            -
                    @done 
     | 
| 
      
 25 
     | 
    
         
            +
                    @done           = false
         
     | 
| 
      
 26 
     | 
    
         
            +
                    @output_text    = nil
         
     | 
| 
      
 27 
     | 
    
         
            +
                    @error_text     = nil
         
     | 
| 
      
 28 
     | 
    
         
            +
                    @status         = nil
         
     | 
| 
      
 29 
     | 
    
         
            +
                    @last_exception = nil
         
     | 
| 
      
 30 
     | 
    
         
            +
                    @last_iteration = 0
         
     | 
| 
      
 31 
     | 
    
         
            +
                  end
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                  attr_reader :output_text, :error_text, :status
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                  def do_right_popen(command, env=nil)
         
     | 
| 
       20 
36 
     | 
    
         
             
                    @output_text = ''
         
     | 
| 
       21 
37 
     | 
    
         
             
                    @error_text  = ''
         
     | 
| 
       22 
38 
     | 
    
         
             
                    @status      = nil
         
     | 
| 
      
 39 
     | 
    
         
            +
                    RightScale.popen3(:command        => command, 
         
     | 
| 
      
 40 
     | 
    
         
            +
                                      :target         => self, 
         
     | 
| 
      
 41 
     | 
    
         
            +
                                      :environment    => env,
         
     | 
| 
      
 42 
     | 
    
         
            +
                                      :stdout_handler => :on_read_stdout, 
         
     | 
| 
      
 43 
     | 
    
         
            +
                                      :stderr_handler => :on_read_stderr, 
         
     | 
| 
      
 44 
     | 
    
         
            +
                                      :exit_handler   => :on_exit)
         
     | 
| 
       23 
45 
     | 
    
         
             
                  end
         
     | 
| 
       24 
46 
     | 
    
         | 
| 
       25 
     | 
    
         
            -
                   
     | 
| 
       26 
     | 
    
         
            -
             
     | 
| 
       27 
     | 
    
         
            -
             
     | 
| 
      
 47 
     | 
    
         
            +
                  def run_right_popen(command, env=nil, count = 1)
         
     | 
| 
      
 48 
     | 
    
         
            +
                    puts "#{count}>" if count > 1
         
     | 
| 
      
 49 
     | 
    
         
            +
                    last_iteration = 0
         
     | 
| 
       28 
50 
     | 
    
         
             
                    EM.next_tick do
         
     | 
| 
       29 
     | 
    
         
            -
                       
     | 
| 
      
 51 
     | 
    
         
            +
                      do_right_popen(command, env)
         
     | 
| 
       30 
52 
     | 
    
         
             
                    end
         
     | 
| 
       31 
53 
     | 
    
         
             
                    EM.run do
         
     | 
| 
       32 
     | 
    
         
            -
                      timer = EM::PeriodicTimer.new(0. 
     | 
| 
       33 
     | 
    
         
            -
                         
     | 
| 
      
 54 
     | 
    
         
            +
                      timer = EM::PeriodicTimer.new(0.05) do
         
     | 
| 
      
 55 
     | 
    
         
            +
                        begin
         
     | 
| 
      
 56 
     | 
    
         
            +
                          if @done || @last_exception
         
     | 
| 
      
 57 
     | 
    
         
            +
                            last_iteration = last_iteration + 1
         
     | 
| 
      
 58 
     | 
    
         
            +
                            if @last_exception.nil? && last_iteration < count
         
     | 
| 
      
 59 
     | 
    
         
            +
                              @done = false
         
     | 
| 
      
 60 
     | 
    
         
            +
                              EM.next_tick do
         
     | 
| 
      
 61 
     | 
    
         
            +
                                if count > 1
         
     | 
| 
      
 62 
     | 
    
         
            +
                                  print '+'
         
     | 
| 
      
 63 
     | 
    
         
            +
                                  STDOUT.flush
         
     | 
| 
      
 64 
     | 
    
         
            +
                                end
         
     | 
| 
      
 65 
     | 
    
         
            +
                                do_right_popen(command, env)
         
     | 
| 
      
 66 
     | 
    
         
            +
                              end
         
     | 
| 
      
 67 
     | 
    
         
            +
                            else
         
     | 
| 
      
 68 
     | 
    
         
            +
                              puts "<" if count > 1
         
     | 
| 
      
 69 
     | 
    
         
            +
                              timer.cancel
         
     | 
| 
      
 70 
     | 
    
         
            +
                              EM.stop
         
     | 
| 
      
 71 
     | 
    
         
            +
                            end
         
     | 
| 
      
 72 
     | 
    
         
            +
                          end
         
     | 
| 
      
 73 
     | 
    
         
            +
                        rescue Exception => e
         
     | 
| 
      
 74 
     | 
    
         
            +
                          @last_exception = e
         
     | 
| 
       34 
75 
     | 
    
         
             
                          timer.cancel
         
     | 
| 
       35 
76 
     | 
    
         
             
                          EM.stop
         
     | 
| 
       36 
77 
     | 
    
         
             
                        end
         
     | 
| 
       37 
78 
     | 
    
         
             
                      end
         
     | 
| 
       38 
79 
     | 
    
         
             
                    end
         
     | 
| 
      
 80 
     | 
    
         
            +
                    if @last_exception
         
     | 
| 
      
 81 
     | 
    
         
            +
                      if count > 1
         
     | 
| 
      
 82 
     | 
    
         
            +
                        message = "<#{last_iteration + 1}\n#{last_exception.message}"
         
     | 
| 
      
 83 
     | 
    
         
            +
                      else
         
     | 
| 
      
 84 
     | 
    
         
            +
                        message = last_exception.message
         
     | 
| 
      
 85 
     | 
    
         
            +
                      end
         
     | 
| 
      
 86 
     | 
    
         
            +
                      raise @last_exception.class, "#{message}\n#{@last_exception.backtrace.join("\n")}"
         
     | 
| 
      
 87 
     | 
    
         
            +
                    end
         
     | 
| 
       39 
88 
     | 
    
         
             
                  end
         
     | 
| 
       40 
89 
     | 
    
         | 
| 
       41 
90 
     | 
    
         
             
                  def on_read_stdout(data)
         
     | 
| 
         @@ -121,5 +170,50 @@ describe 'RightScale::popen3' do 
     | 
|
| 
       121 
170 
     | 
    
         
             
                end
         
     | 
| 
       122 
171 
     | 
    
         
             
                runner.error_text.should == results
         
     | 
| 
       123 
172 
     | 
    
         
             
              end
         
     | 
| 
      
 173 
     | 
    
         
            +
              
         
     | 
| 
      
 174 
     | 
    
         
            +
              it 'should setup environment variables' do
         
     | 
| 
      
 175 
     | 
    
         
            +
                command = "\"#{RUBY_CMD}\" \"#{File.expand_path(File.join(File.dirname(__FILE__), 'print_env.rb'))}\""
         
     | 
| 
      
 176 
     | 
    
         
            +
                runner = RightPopenSpec::Runner.new
         
     | 
| 
      
 177 
     | 
    
         
            +
                runner.run_right_popen(command)
         
     | 
| 
      
 178 
     | 
    
         
            +
                runner.status.exitstatus.should == 0
         
     | 
| 
      
 179 
     | 
    
         
            +
                runner.output_text.should_not include('_test_')
         
     | 
| 
      
 180 
     | 
    
         
            +
                runner.run_right_popen(command, :__test__ => '42')
         
     | 
| 
      
 181 
     | 
    
         
            +
                runner.status.exitstatus.should == 0
         
     | 
| 
      
 182 
     | 
    
         
            +
                runner.output_text.should match(/^__test__=42$/)
         
     | 
| 
      
 183 
     | 
    
         
            +
              end
         
     | 
| 
      
 184 
     | 
    
         
            +
             
     | 
| 
      
 185 
     | 
    
         
            +
              it 'should restore environment variables' do
         
     | 
| 
      
 186 
     | 
    
         
            +
                ENV['__test__'] = '41'
         
     | 
| 
      
 187 
     | 
    
         
            +
                old_envs = {}
         
     | 
| 
      
 188 
     | 
    
         
            +
                ENV.each { |k, v| old_envs[k] = v }
         
     | 
| 
      
 189 
     | 
    
         
            +
                command = "\"#{RUBY_CMD}\" \"#{File.expand_path(File.join(File.dirname(__FILE__), 'print_env.rb'))}\""
         
     | 
| 
      
 190 
     | 
    
         
            +
                runner = RightPopenSpec::Runner.new
         
     | 
| 
      
 191 
     | 
    
         
            +
                runner.run_right_popen(command, :__test__ => '42')
         
     | 
| 
      
 192 
     | 
    
         
            +
                runner.status.exitstatus.should == 0
         
     | 
| 
      
 193 
     | 
    
         
            +
                runner.output_text.should match(/^__test__=42$/)
         
     | 
| 
      
 194 
     | 
    
         
            +
                ENV.each { |k, v| old_envs[k].should == v }
         
     | 
| 
      
 195 
     | 
    
         
            +
                old_envs.each { |k, v| ENV[k].should == v }
         
     | 
| 
      
 196 
     | 
    
         
            +
              end
         
     | 
| 
      
 197 
     | 
    
         
            +
             
     | 
| 
      
 198 
     | 
    
         
            +
              if is_windows?
         
     | 
| 
      
 199 
     | 
    
         
            +
                # FIX: this behavior is currently specific to Windows but should probably be
         
     | 
| 
      
 200 
     | 
    
         
            +
                # implemented for Linux.
         
     | 
| 
      
 201 
     | 
    
         
            +
                it 'should merge the PATH variable instead of overriding it' do
         
     | 
| 
      
 202 
     | 
    
         
            +
                  command = "\"#{RUBY_CMD}\" \"#{File.expand_path(File.join(File.dirname(__FILE__), 'print_env.rb'))}\""
         
     | 
| 
      
 203 
     | 
    
         
            +
                  runner = RightPopenSpec::Runner.new
         
     | 
| 
      
 204 
     | 
    
         
            +
                  runner.run_right_popen(command, 'PATH' => "c:/bogus\\bin")
         
     | 
| 
      
 205 
     | 
    
         
            +
                  runner.status.exitstatus.should == 0
         
     | 
| 
      
 206 
     | 
    
         
            +
                  runner.output_text.should include('PATH=c:\\bogus\\bin;')
         
     | 
| 
      
 207 
     | 
    
         
            +
                end
         
     | 
| 
      
 208 
     | 
    
         
            +
              end
         
     | 
| 
      
 209 
     | 
    
         
            +
             
     | 
| 
      
 210 
     | 
    
         
            +
              it 'should run repeatedly without leaking resources' do
         
     | 
| 
      
 211 
     | 
    
         
            +
                command = "\"#{RUBY_CMD}\" \"#{File.expand_path(File.join(File.dirname(__FILE__), 'produce_output.rb'))}\" \"#{STANDARD_MESSAGE}\" \"#{ERROR_MESSAGE}\""
         
     | 
| 
      
 212 
     | 
    
         
            +
                runner = RightPopenSpec::Runner.new
         
     | 
| 
      
 213 
     | 
    
         
            +
                runner.run_right_popen(command, nil, REPEAT_TEST_COUNTER)
         
     | 
| 
      
 214 
     | 
    
         
            +
                runner.status.exitstatus.should == 0
         
     | 
| 
      
 215 
     | 
    
         
            +
                runner.output_text.should == STANDARD_MESSAGE + "\n"
         
     | 
| 
      
 216 
     | 
    
         
            +
                runner.error_text.should == ERROR_MESSAGE + "\n"
         
     | 
| 
      
 217 
     | 
    
         
            +
              end
         
     | 
| 
       124 
218 
     | 
    
         | 
| 
       125 
219 
     | 
    
         
             
            end
         
     | 
    
        data/spec/spec_helper.rb
    CHANGED
    
    
    
        metadata
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            --- !ruby/object:Gem::Specification 
         
     | 
| 
       2 
2 
     | 
    
         
             
            name: right_popen
         
     | 
| 
       3 
3 
     | 
    
         
             
            version: !ruby/object:Gem::Version 
         
     | 
| 
       4 
     | 
    
         
            -
              version: 1.0. 
     | 
| 
      
 4 
     | 
    
         
            +
              version: 1.0.5
         
     | 
| 
       5 
5 
     | 
    
         
             
            platform: x86-mswin32-60
         
     | 
| 
       6 
6 
     | 
    
         
             
            authors: 
         
     | 
| 
       7 
7 
     | 
    
         
             
            - Scott Messier
         
     | 
| 
         @@ -10,7 +10,7 @@ autorequire: 
     | 
|
| 
       10 
10 
     | 
    
         
             
            bindir: bin
         
     | 
| 
       11 
11 
     | 
    
         
             
            cert_chain: []
         
     | 
| 
       12 
12 
     | 
    
         | 
| 
       13 
     | 
    
         
            -
            date: 2010-02- 
     | 
| 
      
 13 
     | 
    
         
            +
            date: 2010-02-23 00:00:00 -08:00
         
     | 
| 
       14 
14 
     | 
    
         
             
            default_executable: 
         
     | 
| 
       15 
15 
     | 
    
         
             
            dependencies: 
         
     | 
| 
       16 
16 
     | 
    
         
             
            - !ruby/object:Gem::Dependency 
         
     | 
| 
         @@ -22,6 +22,13 @@ dependencies: 
     | 
|
| 
       22 
22 
     | 
    
         
             
                - - ">="
         
     | 
| 
       23 
23 
     | 
    
         
             
                  - !ruby/object:Gem::Version 
         
     | 
| 
       24 
24 
     | 
    
         
             
                    version: 0.12.8
         
     | 
| 
      
 25 
     | 
    
         
            +
                version: 
         
     | 
| 
      
 26 
     | 
    
         
            +
            - !ruby/object:Gem::Dependency 
         
     | 
| 
      
 27 
     | 
    
         
            +
              name: eventmachine
         
     | 
| 
      
 28 
     | 
    
         
            +
              type: :runtime
         
     | 
| 
      
 29 
     | 
    
         
            +
              version_requirement: 
         
     | 
| 
      
 30 
     | 
    
         
            +
              version_requirements: !ruby/object:Gem::Requirement 
         
     | 
| 
      
 31 
     | 
    
         
            +
                requirements: 
         
     | 
| 
       25 
32 
     | 
    
         
             
                - - <
         
     | 
| 
       26 
33 
     | 
    
         
             
                  - !ruby/object:Gem::Version 
         
     | 
| 
       27 
34 
     | 
    
         
             
                    version: 0.12.9
         
     | 
| 
         @@ -58,6 +65,7 @@ files: 
     | 
|
| 
       58 
65 
     | 
    
         
             
            - lib/win32/right_popen.rb
         
     | 
| 
       59 
66 
     | 
    
         
             
            - lib/win32/right_popen.so
         
     | 
| 
       60 
67 
     | 
    
         
             
            - right_popen.gemspec
         
     | 
| 
      
 68 
     | 
    
         
            +
            - spec/print_env.rb
         
     | 
| 
       61 
69 
     | 
    
         
             
            - spec/produce_mixed_output.rb
         
     | 
| 
       62 
70 
     | 
    
         
             
            - spec/produce_output.rb
         
     | 
| 
       63 
71 
     | 
    
         
             
            - spec/produce_status.rb
         
     |