v2 / vlib / os / command.c.v
80 lines · 74 sloc · 2.08 KB · 8e35f4d9848f7ad35d857a187dddbfd2eca5e19d
Raw
1module os
2
3import strings
4
5// Command represents a running shell command, that the parent process
6// wishes to monitor for output on its stdout pipe.
7pub struct Command {
8mut:
9 f voidptr
10pub mut:
11 eof bool
12 exit_code int
13pub:
14 path string
15 redirect_stdout bool
16}
17
18// start_new_command will create a new os.Command, and start it right away.
19// The command will represent a process running the passed shell command `cmd`,
20// in a way that you can later call c.read_line() to intercept the new child process
21// output line by line, streaming while the command is running.
22// See also c.eof, c.read_line(), c.close() and c.exit_code .
23pub fn start_new_command(cmd string) !Command {
24 mut res := Command{
25 path: cmd
26 }
27 res.start()!
28 return res
29}
30
31// start will start the command. Use start_new_command/1 instead.
32@[manualfree]
33pub fn (mut c Command) start() ! {
34 pcmd := c.path + ' 2>&1'
35 defer {
36 unsafe { pcmd.free() }
37 }
38 c.f = vpopen(pcmd)
39 if isnil(c.f) {
40 return error('exec("${c.path}") failed')
41 }
42}
43
44// read_line returns a single line from the stdout of the running command.
45// Note: c.eof will be set to true, if the command ended while a line was read.
46// The returned line will contain all of the accumulated output before the process ended.
47// In practice, that often means, you will get a single '' and c.eof == true at the end.
48@[manualfree]
49pub fn (mut c Command) read_line() string {
50 buf := [4096]u8{}
51 mut res := strings.new_builder(1024)
52 defer { unsafe { res.free() } }
53 unsafe {
54 bufbp := &u8(&buf[0])
55 for C.fgets(&char(bufbp), 4096, c.f) != 0 {
56 len := vstrlen(bufbp)
57 for i in 0 .. len {
58 if bufbp[i] == `\n` {
59 res.write_ptr(bufbp, i)
60 final := res.str()
61 return final
62 }
63 }
64 res.write_ptr(bufbp, len)
65 }
66 }
67 c.eof = true
68 final := res.str()
69 return final
70}
71
72// close will close the pipe to the command, and wait for the command to finish,
73// then set .exit_code according to how its final process status.
74pub fn (mut c Command) close() ! {
75 c.exit_code = vpclose(c.f)
76 c.f = unsafe { nil }
77 if c.exit_code == 127 {
78 return error_with_code('error', 127)
79 }
80}
81