about summary refs log tree commit diff
path: root/src/lib/run_program.rs
blob: 5c5f110816bb8b4cd554764ac8adb465cc76821f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130

import str::sbuf;

export program;
export run_program;
export start_program;
export program_output;
export spawn_process;

native "rust" mod rustrt {
    fn rust_run_program(argv: *sbuf, in_fd: int, out_fd: int, err_fd: int) ->
       int;
}

fn arg_vec(prog: str, args: &[str]) -> [sbuf] {
    let argptrs = ~[str::buf(prog)];
    for arg: str in args { argptrs += ~[str::buf(arg)]; }
    argptrs += ~[0 as sbuf];
    ret argptrs;
}

fn spawn_process(prog: str, args: &[str], in_fd: int, out_fd: int,
                 err_fd: int) -> int {
    // Note: we have to hold on to this vector reference while we hold a
    // pointer to its buffer
    let argv = arg_vec(prog, args);
    let pid = rustrt::rust_run_program(
        vec::to_ptr(argv), in_fd, out_fd, err_fd);
    ret pid;
}

fn run_program(prog: str, args: &[str]) -> int {
    ret os::waitpid(spawn_process(prog, args, 0, 0, 0));
}

type program =
    obj {
        fn get_id() -> int;
        fn input() -> io::writer;
        fn output() -> io::reader;
        fn err() -> io::reader;
        fn close_input();
        fn finish() -> int;
        fn destroy();
    };

resource program_res(p: program) {
    p.destroy();
}

fn start_program(prog: str, args: &[str]) -> @program_res {
    let pipe_input = os::pipe();
    let pipe_output = os::pipe();
    let pipe_err = os::pipe();
    let pid = spawn_process(prog, args, pipe_input.in, pipe_output.out,
                            pipe_err.out);

    if pid == -1 { fail; }
    os::libc::close(pipe_input.in);
    os::libc::close(pipe_output.out);
    os::libc::close(pipe_err.out);
    obj new_program(pid: int,
                    mutable in_fd: int,
                    out_file: os::libc::FILE,
                    err_file: os::libc::FILE,
                    mutable finished: bool) {
        fn get_id() -> int { ret pid; }
        fn input() -> io::writer {
            ret io::new_writer(
                io::fd_buf_writer(in_fd, option::none));
        }
        fn output() -> io::reader {
            ret io::new_reader(
                io::FILE_buf_reader(out_file, option::none));
        }
        fn err() -> io::reader {
            ret io::new_reader(
                io::FILE_buf_reader(err_file, option::none));
        }
        fn close_input() {
            let invalid_fd = -1;
            if in_fd != invalid_fd {
                os::libc::close(in_fd);
                in_fd = invalid_fd;
            }
        }
        fn finish() -> int {
            if finished { ret 0; }
            finished = true;
            self.close_input();
            ret os::waitpid(pid);
        }
        fn destroy() {
            self.finish();
            os::libc::fclose(out_file);
            os::libc::fclose(err_file);
        }
    }
    ret @program_res(new_program(pid,
                                 pipe_input.out,
                                 os::fd_FILE(pipe_output.in),
                                 os::fd_FILE(pipe_err.in),
                                 false));
}

fn read_all(rd: &io::reader) -> str {
    let buf = "";
    while !rd.eof() {
        let bytes = rd.read_bytes(4096u);
        buf += str::unsafe_from_bytes(bytes);
    }
    ret buf;
}

fn program_output(prog: str, args: [str])
    -> {status: int, out: str, err: str} {
    let pr = start_program(prog, args);
    pr.close_input();
    ret {status: pr.finish(),
         out: read_all(pr.output()),
         err: read_all(pr.err())};
}
// Local Variables:
// mode: rust
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End: