1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # Copyright 2004-2008 Jean Privat <jean@pryen.org>
4 # Copyright 2008 Floréal Morandat <morandat@lirmm.fr>
6 # This file is free software, which comes along with NIT. This software is
7 # distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
8 # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
9 # PARTICULAR PURPOSE. You can modify it is you want, provided this header
10 # is kept unaltered, and a notification of the changes is added.
11 # You are allowed to redistribute it and sell it, alone or is a part of
14 # Invocation and management of operating system sub-processes.
15 # Standard input and output can be handled through streams.
33 #include <sys/types.h>
35 // FIXME this should be in the "C" block when bug on module blocks is fixed
36 // or, even better, replace the C structure by a Nit object.
37 typedef struct se_exec_data se_exec_data_t;
50 # The pid of the process
51 fun id
: Int do return data
.id
53 # Is the process finished?
54 fun is_finished
: Bool do return data
.is_finished
56 # Wait the termination of the process
63 # The status once finished
65 # Require: `is_finished`
72 # The target executable
73 # Either a file path or the name of an executable available in PATH.
76 # The arguments of the command
77 # Starts with the first real arguments---ie. does not include the progname (`argv[0]`, in C)
78 var arguments
: nullable Array[Text]
80 # Launch a command with some arguments
81 init(command
: Text, arguments
: Text...) is old_style_init
do
82 self.command
= command
83 self.arguments
= arguments
87 # Launch a simple command with arguments passed as an array
88 init from_a
(command
: Text, arguments
: nullable Array[Text])
90 self.command
= command
91 self.arguments
= arguments
95 # Flags used internally to know which pipe to open
96 private fun pipeflags
: Int do return 0
98 # Internal code to handle execution
101 # Pass the arguments as a big C string where elements are separated with '\0'
102 var args
= new FlatBuffer
103 var l
= 1 # Number of elements in args
105 var arguments
= self.arguments
106 if arguments
!= null then
107 for a
in arguments
do
111 l
+= arguments
.length
113 data
= basic_exec_execute
(command
.to_cstring
, args
.to_s
.to_cstring
, l
, pipeflags
)
116 private var data
: NativeProcess
117 private fun basic_exec_execute
(prog
, args
: CString, argc
: Int, pipeflag
: Int): NativeProcess `{
119 // FIXME use a higher level abstraction to support WIN32
122 se_exec_data_t* result = NULL;
128 int res = pipe(in_fd);
134 int res = pipe(out_fd);
140 int res = pipe(err_fd);
149 char **arg = malloc(sizeof(char*) * (argc+1));
154 for(i=0; i<argc; i++)
190 result = (se_exec_data_t*)malloc(sizeof(se_exec_data_t));
195 result->in_fd = in_fd[1];
202 result->out_fd = out_fd[0];
209 result->err_fd = err_fd[0];
220 # `Process` on which the `stdout` is readable like a `Reader`
225 # File Descriptor used for the input.
226 var stream_in
: FileReader is noinit
228 redef fun close
do stream_in
.close
230 redef fun read_char
do return stream_in
.read_char
232 redef fun read_byte
do return stream_in
.read_byte
234 redef fun eof
do return stream_in
.eof
236 redef fun pipeflags
do return 2
241 stream_in
= new FileReader.from_fd
(data
.out_fd
)
245 # `Process` on which `stdin` is writable like a `Writer`
250 # File Descriptor used for the output.
251 var stream_out
: Writer is noinit
253 redef fun close
do stream_out
.close
255 redef fun is_writable
do return stream_out
.is_writable
257 redef fun write
(s
) do stream_out
.write
(s
)
259 redef fun pipeflags
do return 1
264 var out
= new FileWriter.from_fd
(data
.in_fd
)
265 out
.set_buffering_mode
(0, sys
.buffer_mode_none
)
270 # `Process` on which stdout can be read and stdin can be written to like a `Duplex`
282 redef fun pipeflags
do return 3
284 redef fun execute
do super
286 # Write `input` to process and return its output
288 # Writing and reading are processed line by line,
289 # reading only when something is available.
292 # var proc = new ProcessDuplex("tr", "[:lower:]", "[:upper:]")
293 # assert proc.write_and_read("""
301 fun write_and_read
(input
: Text): String
303 var read
= new Buffer
305 # Main loop, read and write line by line
307 for delimiter
in input
.search_all
('\n') do
308 write input
.substring
(prev
, delimiter
.after-prev
)
309 prev
= delimiter
.after
311 while stream_in
.poll_in
do
312 read
.append stream_in
.read_line
316 # Write the last line
317 write input
.substring_from
(prev
)
320 # Read the rest, may be everything for some programs
321 read
.append stream_in
.read_all
331 # Execute a shell command and return its error code
332 fun system
(command
: Text): Int
334 return command
.to_cstring
.system
337 # The pid of the program
338 fun pid
: Int `{ return getpid(); `}
342 # Execute self as a shell command.
344 # See the posix function system(3).
346 int status
= system
(self);
348 if (WIFSIGNALED(status
) && WTERMSIG(status
) == SIGINT) {
349 // system exited on
SIGINT: in my opinion the user wants the main to be discontinued
350 kill
(getpid
(), SIGINT);
357 private extern class NativeProcess `{ se_exec_data_t* `}
359 fun id
: Int `{ return self->id; `}
360 fun status: Int `{ return self->status; `}
361 fun in_fd
: Int `{ return self->in_fd; `}
362 fun out_fd: Int `{ return self->out_fd; `}
363 fun err_fd
: Int `{ return self->err_fd; `}
365 fun is_finished: Bool `{
367 // FIXME use a higher level abstraction to support
WIN32
373 int id
= waitpid
(self-
>id
, &status
, WNOHANG);
375 /* child
is finished
*/
376 result
= (int
)(id
== self-
>id
);
377 self-
>status
= WEXITSTATUS(status
);
390 // FIXME use a higher level abstraction to support
WIN32
393 waitpid
(self-
>id
, &status
, 0);
394 self-
>status
= WEXITSTATUS(status
);