<commit_before>module ChildProcess
  module Windows
    class ProcessBuilder
      attr_accessor :detach, :duplex, :environment, :stdout, :stderr, :cwd
      attr_reader :stdin

      def initialize(args)
        @args        = args

        @detach      = false
        @duplex      = false
        @environment = nil
        @cwd         = nil

        @stdout      = nil
        @stderr      = nil
        @stdin       = nil

        @flags       = 0
        @job_ptr     = nil
        @cmd_ptr     = nil
        @env_ptr     = nil
        @cwd_ptr     = nil
      end

      def start
        create_command_pointer
        create_environment_pointer
        create_cwd_pointer

        setup_flags
        setup_io

        pid = create_process
        close_handles

        pid
      end

      private

      def create_command_pointer
        string = @args.map { |arg| quote_if_necessary(arg.to_s) }.join ' '
        @cmd_ptr = FFI::MemoryPointer.from_string string
      end

      def create_environment_pointer
        return unless @environment.kind_of?(Hash) && @environment.any?

        strings = []

        ENV.to_hash.merge(@environment).each do |key, val|
          next if val.nil?

          if key.to_s =~ /=|\0/ || val.to_s.include?("\0")
            raise InvalidEnvironmentVariable, "#{key.inspect} => #{val.inspect}"
          end

          strings << "#{key}=#{val}\0"
        end

        strings << "\0" # terminate the env block
        env_str = strings.join

        @env_ptr = FFI::MemoryPointer.new(:long, env_str.bytesize)
        @env_ptr.put_bytes 0, env_str, 0, env_str.bytesize
      end

      def create_cwd_pointer
        @cwd_ptr = FFI::MemoryPointer.from_string(@cwd || Dir.pwd)
      end

      def create_process
        ok = Lib.create_process(
          nil,          # application name
          @cmd_ptr,     # command line
          nil,          # process attributes
          nil,          # thread attributes
          true,         # inherit handles
          @flags,       # creation flags
          @env_ptr,     # environment
          @cwd_ptr,     # current directory
          startup_info, # startup info
          process_info  # process info
        )

        ok or raise LaunchError, Lib.last_error_message

        process_info[:dwProcessId]
      end

      def startup_info
        @startup_info ||= StartupInfo.new
      end

      def process_info
        @process_info ||= ProcessInfo.new
      end

      def setup_flags
        @flags |= DETACHED_PROCESS if @detach
        @flags |= CREATE_BREAKAWAY_FROM_JOB
      end

      def setup_io
        startup_info[:dwFlags] ||= 0
        startup_info[:dwFlags] |= STARTF_USESTDHANDLES

        if @stdout
          startup_info[:hStdOutput] = std_stream_handle_for(@stdout)
        end

        if @stderr
          startup_info[:hStdError] = std_stream_handle_for(@stderr)
        end

        if @duplex
          read_pipe_ptr  = FFI::MemoryPointer.new(:pointer)
          write_pipe_ptr = FFI::MemoryPointer.new(:pointer)
          sa             = SecurityAttributes.new(:inherit => true)

          ok = Lib.create_pipe(read_pipe_ptr, write_pipe_ptr, sa, 0)
          Lib.check_error ok

          @read_pipe  = read_pipe_ptr.read_pointer<commit_msg>Windows: Remove CREATE_BREAKAWAY_FROM_JOB, fixes #76.<commit_after>module ChildProcess
  module Windows
    class ProcessBuilder
      attr_accessor :detach, :duplex, :environment, :stdout, :stderr, :cwd
      attr_reader :stdin

      def initialize(args)
        @args        = args

        @detach      = false
        @duplex      = false
        @environment = nil
        @cwd         = nil

        @stdout      = nil
        @stderr      = nil
        @stdin       = nil

        @flags       = 0
        @job_ptr     = nil
        @cmd_ptr     = nil
        @env_ptr     = nil
        @cwd_ptr     = nil
      end

      def start
        create_command_pointer
        create_environment_pointer
        create_cwd_pointer

        setup_flags
        setup_io

        pid = create_process
        close_handles

        pid
      end

      private

      def create_command_pointer
        string = @args.map { |arg| quote_if_necessary(arg.to_s) }.join ' '
        @cmd_ptr = FFI::MemoryPointer.from_string string
      end

      def create_environment_pointer
        return unless @environment.kind_of?(Hash) && @environment.any?

        strings = []

        ENV.to_hash.merge(@environment).each do |key, val|
          next if val.nil?

          if key.to_s =~ /=|\0/ || val.to_s.include?("\0")
            raise InvalidEnvironmentVariable, "#{key.inspect} => #{val.inspect}"
          end

          strings << "#{key}=#{val}\0"
        end

        strings << "\0" # terminate the env block
        env_str = strings.join

        @env_ptr = FFI::MemoryPointer.new(:long, env_str.bytesize)
        @env_ptr.put_bytes 0, env_str, 0, env_str.bytesize
      end

      def create_cwd_pointer
        @cwd_ptr = FFI::MemoryPointer.from_string(@cwd || Dir.pwd)
      end

      def create_process
        ok = Lib.create_process(
          nil,          # application name
          @cmd_ptr,     # command line
          nil,          # process attributes
          nil,          # thread attributes
          true,         # inherit handles
          @flags,       # creation flags
          @env_ptr,     # environment
          @cwd_ptr,     # current directory
          startup_info, # startup info
          process_info  # process info
        )

        ok or raise LaunchError, Lib.last_error_message

        process_info[:dwProcessId]
      end

      def startup_info
        @startup_info ||= StartupInfo.new
      end

      def process_info
        @process_info ||= ProcessInfo.new
      end

      def setup_flags
        @flags |= DETACHED_PROCESS if @detach
      end

      def setup_io
        startup_info[:dwFlags] ||= 0
        startup_info[:dwFlags] |= STARTF_USESTDHANDLES

        if @stdout
          startup_info[:hStdOutput] = std_stream_handle_for(@stdout)
        end

        if @stderr
          startup_info[:hStdError] = std_stream_handle_for(@stderr)
        end

        if @duplex
          read_pipe_ptr  = FFI::MemoryPointer.new(:pointer)
          write_pipe_ptr = FFI::MemoryPointer.new(:pointer)
          sa             = SecurityAttributes.new(:inherit => true)

          ok = Lib.create_pipe(read_pipe_ptr, write_pipe_ptr, sa, 0)
          Lib.check_error ok

          @read_pipe  = read_pipe_ptr.read_pointer
{'fraction_non_alphanumeric': 0.08340957609027143, 'fraction_numerical': 0.0030497102775236353, 'mean_word_length': 1.711451012815213, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2262659', 'n_tokens_mistral': 1976, 'n_tokens_neox': 1903, 'n_words': 421}