1     let pclose (stream : CStream) : int
2     let feof (stream : CStream) : int
3     let fgets (s : MutPtr<CChar>
4                n : int
5                stream : CStream) : MutPtr<CChar>
6     def fclose (stream : CStream) : int
7     
8     let kd_get_arg (index : u32) : String
9     let kd_get_current_path () : String
10    
11    object Sys =
12        val unprintable_char = '?'
13        val executable_directory = kd_get_current_path
14            |> { s -> if s.contains '\\'
15                      then s.replace '\\' '/'
16                      else s }
17    
18    type Output =
19        val status : i32
20        val stdout : String
21        val stderr : String
22    
23        def is_success = status == 0
24    
25    type Process @abstract =
26        val output = StringBuilder.new
27        val error = StringBuilder.new
28        var maybe_exit_code : Option<i32> = None
29    
30        def is_running : bool
31        def read_streams
32        def close
33    
34        def is_success = case maybe_exit_code of
35            None -> false
36            Some exit_code -> exit_code == 0
37    
38        def wait =
39            assert maybe_exit_code.is_none
40    
41            while is_running do
42                read_streams
43    
44            close
45    
46            Output status = maybe_exit_code.unwrap
47                   stdout = output.create
48                   stderr = error.create
49    
50        def complete =
51             assert not is_running && maybe_exit_code.is_none
52    
53             read_streams
54             close
55    
56    let remove_non_ascii (buffer : MutPtr<CChar>) (size : u32) =
57        for i = 0 until size do
58            let c = buffer[i]
59            if c == '\0' then
60                break
61    
62            if c as i32 < 0 then
63                buffer[i] = Sys.unprintable_char as CChar
64    
65    type CProcess =
66        inherit Process
67    
68        val stdout : CStream
69        val stderr : CStream
70        let fd : int
71    
72        def Process.is_running =
73            feof stdout == 0
74    
75        def Process.read_streams =
76            let max_buffer : int = 256
77            let mut buffer = [CChar; max_buffer]@zero
78            let c_string = CString buffer.as_ptr
79    
80            repeat
81                if fgets buffer.as_mut_ptr max_buffer stdout <> null then
82                    remove_non_ascii buffer.as_mut_ptr max_buffer.as<u32>
83                    output.append c_string
84                else
85                    break
86    
87            until buffer[0] as i32 == 0 || feof stdout <> 0
88    
89            repeat
90                if fgets buffer.as_mut_ptr max_buffer stderr <> null then
91                    remove_non_ascii buffer.as_mut_ptr max_buffer.as<u32>
92                    error.append c_string
93                else
94                    break
95    
96            until buffer[0] as i32 == 0 || feof stderr <> 0
97    
98        def Process.close =
99            let exit_code = pclose stdout as i32
100           maybe_exit_code = Some exit_code
101   
102           fclose stderr
103           close fd |> ignore
104   
105   os@ windows
106   
107   type WProcessImpl @cpp =
108       fun new (path : String)
109       def is_running : bool
110       def read_from_pipe : MutPtr<CChar>
111       def get_exit_code : i32
112       def close
113   
114   let w_max_buffer "WPROCESS_BUFFER_SIZE" : u32
115   
116   type WProcess =
117       inherit Process
118   
119       let command : String
120       let impl = WProcessImpl command
121   
122       def Process.is_running = impl.is_running
123   
124       def Process.read_streams =
125           repeat
126               let buffer = impl.read_from_pipe
127               if buffer <> null then
128                   remove_non_ascii buffer w_max_buffer
129                   let c_string = CString buffer
130                   output.append c_string
131               else
132                   break
133   
134       def Process.close =
135           let exit_code = impl.get_exit_code
136           maybe_exit_code = Some exit_code
137           impl.close
138   
139   endos@
140