diff options
| author | Graydon Hoare <graydon@mozilla.com> | 2012-03-12 20:04:27 -0700 |
|---|---|---|
| committer | Graydon Hoare <graydon@mozilla.com> | 2012-03-12 20:08:29 -0700 |
| commit | 6f5853f5a1767e0c418fd5f348a795b76d701b3e (patch) | |
| tree | 7d054b8471a75c47854edbd52a14d8d2acf81c9d /src/libcore | |
| parent | ac57bb38560fa35d883505af2e8e68498b436fe7 (diff) | |
| download | rust-6f5853f5a1767e0c418fd5f348a795b76d701b3e.tar.gz rust-6f5853f5a1767e0c418fd5f348a795b76d701b3e.zip | |
Libc/os/run/rand/io reorganization. Close #1373. Close #1638.
- Move io, run and rand to core. - Remove incorrect ctypes module (use libc). - Remove os-specific modules for os and fs. - Split fs between core::path and core::os.
Diffstat (limited to 'src/libcore')
| -rw-r--r-- | src/libcore/cmath.rs | 6 | ||||
| -rw-r--r-- | src/libcore/comm.rs | 22 | ||||
| -rw-r--r-- | src/libcore/core.rc | 6 | ||||
| -rw-r--r-- | src/libcore/io.rs | 776 | ||||
| -rw-r--r-- | src/libcore/libc.rs | 37 | ||||
| -rw-r--r-- | src/libcore/os.rs | 151 | ||||
| -rw-r--r-- | src/libcore/ptr.rs | 6 | ||||
| -rw-r--r-- | src/libcore/rand.rs | 113 | ||||
| -rw-r--r-- | src/libcore/run.rs | 394 | ||||
| -rw-r--r-- | src/libcore/str.rs | 2 | ||||
| -rw-r--r-- | src/libcore/sys.rs | 16 | ||||
| -rw-r--r-- | src/libcore/task.rs | 18 | ||||
| -rw-r--r-- | src/libcore/vec.rs | 8 |
13 files changed, 1472 insertions, 83 deletions
diff --git a/src/libcore/cmath.rs b/src/libcore/cmath.rs index e7761a9b383..ba2bad81906 100644 --- a/src/libcore/cmath.rs +++ b/src/libcore/cmath.rs @@ -7,9 +7,9 @@ export c_double; export c_float_targ_consts; export c_double_targ_consts; -import ctypes::c_int; -import ctypes::c_float; -import ctypes::c_double; +import libc::c_int; +import libc::c_float; +import libc::c_double; // function names are almost identical to C's libmath, a few have been // renamed, grep for "rename:" diff --git a/src/libcore/comm.rs b/src/libcore/comm.rs index 413144aff69..e4f0663c443 100644 --- a/src/libcore/comm.rs +++ b/src/libcore/comm.rs @@ -41,20 +41,20 @@ native mod rustrt { fn get_task_id() -> task_id; fn chan_id_send<T: send>(t: *sys::type_desc, target_task: task_id, target_port: port_id, - data: T) -> ctypes::uintptr_t; + data: T) -> libc::uintptr_t; - fn new_port(unit_sz: ctypes::size_t) -> *rust_port; + fn new_port(unit_sz: libc::size_t) -> *rust_port; fn del_port(po: *rust_port); fn rust_port_begin_detach(po: *rust_port, - yield: *ctypes::uintptr_t); + yield: *libc::uintptr_t); fn rust_port_end_detach(po: *rust_port); fn get_port_id(po: *rust_port) -> port_id; - fn rust_port_size(po: *rust_port) -> ctypes::size_t; + fn rust_port_size(po: *rust_port) -> libc::size_t; fn port_recv(dptr: *uint, po: *rust_port, - yield: *ctypes::uintptr_t); + yield: *libc::uintptr_t); fn rust_port_select(dptr: **rust_port, ports: **rust_port, - n_ports: ctypes::size_t, - yield: *ctypes::uintptr_t); + n_ports: libc::size_t, + yield: *libc::uintptr_t); } #[abi = "rust-intrinsic"] @@ -147,7 +147,7 @@ fn recv_<T: send>(p: *rust_port) -> T { // that will grab the value of the return pointer, then call this // function, which we will then use to call the runtime. fn recv(dptr: *uint, port: *rust_port, - yield: *ctypes::uintptr_t) unsafe { + yield: *libc::uintptr_t) unsafe { rustrt::port_recv(dptr, port, yield); } let yield = 0u; @@ -170,13 +170,13 @@ fn select2<A: send, B: send>( ) -> either::t<A, B> unsafe { fn select(dptr: **rust_port, ports: **rust_port, - n_ports: ctypes::size_t, yield: *ctypes::uintptr_t) { + n_ports: libc::size_t, yield: *libc::uintptr_t) { rustrt::rust_port_select(dptr, ports, n_ports, yield) } let mut ports = []; ports += [***p_a, ***p_b]; - let n_ports = 2 as ctypes::size_t; + let n_ports = 2 as libc::size_t; let yield = 0u; let yieldp = ptr::addr_of(yield); @@ -209,7 +209,7 @@ fn select2<A: send, B: send>( #[doc = "Returns true if there are messages available"] fn peek<T: send>(p: port<T>) -> bool { - rustrt::rust_port_size(***p) != 0u as ctypes::size_t + rustrt::rust_port_size(***p) != 0u as libc::size_t } #[doc = " diff --git a/src/libcore/core.rc b/src/libcore/core.rc index c7a85243e25..c28ad0a0282 100644 --- a/src/libcore/core.rc +++ b/src/libcore/core.rc @@ -31,7 +31,7 @@ export uint, u8, u16, u32, u64; export float, f32, f64; export box, char, str, ptr, vec, bool; export either, option, result, iter; -export libc, os, ctypes, sys, unsafe, logging; +export libc, os, io, run, rand, sys, unsafe, logging; export comm, task, future; export extfmt; export tuple; @@ -79,9 +79,11 @@ mod to_str; mod libc; mod os; +mod io; +mod run; +mod rand; mod path; -mod ctypes; mod cmath; mod sys; mod unsafe; diff --git a/src/libcore/io.rs b/src/libcore/io.rs new file mode 100644 index 00000000000..23974b789c3 --- /dev/null +++ b/src/libcore/io.rs @@ -0,0 +1,776 @@ +/* +Module: io + +Basic input/output +*/ + +import libc::{c_int, c_uint, c_void, size_t, ssize_t}; +import libc::consts::os::posix88::*; +import libc::consts::os::extra::*; + +type fd_t = c_int; + +#[abi = "cdecl"] +native mod rustrt { + fn rust_get_stdin() -> *libc::FILE; + fn rust_get_stdout() -> *libc::FILE; + fn rust_get_stderr() -> *libc::FILE; +} + +// Reading + +// FIXME This is all buffered. We might need an unbuffered variant as well +enum seek_style { seek_set, seek_end, seek_cur, } + + +// The raw underlying reader iface. All readers must implement this. +iface reader { + // FIXME: Seekable really should be orthogonal. + fn read_bytes(uint) -> [u8]; + fn read_byte() -> int; + fn unread_byte(int); + fn eof() -> bool; + fn seek(int, seek_style); + fn tell() -> uint; +} + +// Generic utility functions defined on readers + +impl reader_util for reader { + fn read_chars(n: uint) -> [char] { + // returns the (consumed offset, n_req), appends characters to &chars + fn chars_from_buf(buf: [u8], &chars: [char]) -> (uint, uint) { + let mut i = 0u; + while i < vec::len(buf) { + let b0 = buf[i]; + let w = str::utf8_char_width(b0); + let end = i + w; + i += 1u; + assert (w > 0u); + if w == 1u { + chars += [ b0 as char ]; + cont; + } + // can't satisfy this char with the existing data + if end > vec::len(buf) { + ret (i - 1u, end - vec::len(buf)); + } + let mut val = 0u; + while i < end { + let next = buf[i] as int; + i += 1u; + assert (next > -1); + assert (next & 192 == 128); + val <<= 6u; + val += (next & 63) as uint; + } + // See str::char_at + val += ((b0 << ((w + 1u) as u8)) as uint) + << (w - 1u) * 6u - w - 1u; + chars += [ val as char ]; + } + ret (i, 0u); + } + let mut buf: [u8] = []; + let mut chars: [char] = []; + // might need more bytes, but reading n will never over-read + let mut nbread = n; + while nbread > 0u { + let data = self.read_bytes(nbread); + if vec::len(data) == 0u { + // eof - FIXME should we do something if + // we're split in a unicode char? + break; + } + buf += data; + let (offset, nbreq) = chars_from_buf(buf, chars); + let ncreq = n - vec::len(chars); + // again we either know we need a certain number of bytes + // to complete a character, or we make sure we don't + // over-read by reading 1-byte per char needed + nbread = if ncreq > nbreq { ncreq } else { nbreq }; + if nbread > 0u { + buf = vec::slice(buf, offset, vec::len(buf)); + } + } + chars + } + + fn read_char() -> char { + let c = self.read_chars(1u); + if vec::len(c) == 0u { + ret -1 as char; // FIXME will this stay valid? + } + assert(vec::len(c) == 1u); + ret c[0]; + } + + fn read_line() -> str { + let mut buf: [u8] = []; + while true { + let ch = self.read_byte(); + if ch == -1 || ch == 10 { break; } + buf += [ch as u8]; + } + str::from_bytes(buf) + } + + fn read_c_str() -> str { + let mut buf: [u8] = []; + while true { + let ch = self.read_byte(); + if ch < 1 { break; } else { buf += [ch as u8]; } + } + str::from_bytes(buf) + } + + // FIXME deal with eof? + fn read_le_uint(size: uint) -> uint { + let mut val = 0u, pos = 0u, i = size; + while i > 0u { + val += (self.read_byte() as uint) << pos; + pos += 8u; + i -= 1u; + } + val + } + fn read_le_int(size: uint) -> int { + let mut val = 0u, pos = 0u, i = size; + while i > 0u { + val += (self.read_byte() as uint) << pos; + pos += 8u; + i -= 1u; + } + val as int + } + fn read_be_uint(size: uint) -> uint { + let mut val = 0u, i = size; + while i > 0u { + i -= 1u; + val += (self.read_byte() as uint) << i * 8u; + } + val + } + + fn read_whole_stream() -> [u8] { + let mut buf: [u8] = []; + while !self.eof() { buf += self.read_bytes(2048u); } + buf + } +} + +// Reader implementations + +fn convert_whence(whence: seek_style) -> i32 { + ret alt whence { + seek_set { 0i32 } + seek_cur { 1i32 } + seek_end { 2i32 } + }; +} + +impl of reader for *libc::FILE { + fn read_bytes(len: uint) -> [u8] unsafe { + let mut buf : [mutable u8] = [mutable]; + vec::reserve(buf, len); + vec::as_mut_buf(buf) {|b| + let read = libc::fread(b as *mutable c_void, 1u, + len, self); + vec::unsafe::set_len(buf, read); + } + ret vec::from_mut(buf); + } + fn read_byte() -> int { ret libc::fgetc(self) as int; } + fn unread_byte(byte: int) { libc::ungetc(byte as c_int, self); } + fn eof() -> bool { ret libc::feof(self) != 0 as c_int; } + fn seek(offset: int, whence: seek_style) { + assert libc::fseek(self, offset, convert_whence(whence)) + == 0 as c_int; + } + fn tell() -> uint { ret libc::ftell(self) as uint; } +} + +// A forwarding impl of reader that also holds on to a resource for the +// duration of its lifetime. +// FIXME there really should be a better way to do this +impl <T: reader, C> of reader for {base: T, cleanup: C} { + fn read_bytes(len: uint) -> [u8] { self.base.read_bytes(len) } + fn read_byte() -> int { self.base.read_byte() } + fn unread_byte(byte: int) { self.base.unread_byte(byte); } + fn eof() -> bool { self.base.eof() } + fn seek(off: int, whence: seek_style) { self.base.seek(off, whence) } + fn tell() -> uint { self.base.tell() } +} + +resource FILE_res(f: *libc::FILE) { libc::fclose(f); } + +fn FILE_reader(f: *libc::FILE, cleanup: bool) -> reader { + if cleanup { + {base: f, cleanup: FILE_res(f)} as reader + } else { + f as reader + } +} + +// FIXME: this should either be an iface-less impl, a set of top-level +// functions that take a reader, or a set of default methods on reader +// (which can then be called reader) + +fn stdin() -> reader { rustrt::rust_get_stdin() as reader } + +fn file_reader(path: str) -> result::t<reader, str> { + let f = os::as_c_charp(path, {|pathbuf| + os::as_c_charp("r", {|modebuf| + libc::fopen(pathbuf, modebuf) + }) + }); + ret if f as uint == 0u { result::err("error opening " + path) } + else { + result::ok(FILE_reader(f, true)) + } +} + + +// Byte buffer readers + +// TODO: const u8, but this fails with rustboot. +type byte_buf = {buf: [u8], mutable pos: uint, len: uint}; + +impl of reader for byte_buf { + fn read_bytes(len: uint) -> [u8] { + let rest = self.len - self.pos; + let mut to_read = len; + if rest < to_read { to_read = rest; } + let range = vec::slice(self.buf, self.pos, self.pos + to_read); + self.pos += to_read; + ret range; + } + fn read_byte() -> int { + if self.pos == self.len { ret -1; } + let b = self.buf[self.pos]; + self.pos += 1u; + ret b as int; + } + fn unread_byte(_byte: int) { #error("TODO: unread_byte"); fail; } + fn eof() -> bool { self.pos == self.len } + fn seek(offset: int, whence: seek_style) { + let pos = self.pos; + self.pos = seek_in_buf(offset, pos, self.len, whence); + } + fn tell() -> uint { self.pos } +} + +fn bytes_reader(bytes: [u8]) -> reader { + bytes_reader_between(bytes, 0u, vec::len(bytes)) +} + +fn bytes_reader_between(bytes: [u8], start: uint, end: uint) -> reader { + {buf: bytes, mutable pos: start, len: end} as reader +} + +fn with_bytes_reader<t>(bytes: [u8], f: fn(reader) -> t) -> t { + f(bytes_reader(bytes)) +} + +fn with_bytes_reader_between<t>(bytes: [u8], start: uint, end: uint, + f: fn(reader) -> t) -> t { + f(bytes_reader_between(bytes, start, end)) +} + +fn str_reader(s: str) -> reader { + bytes_reader(str::bytes(s)) +} + +fn with_str_reader<T>(s: str, f: fn(reader) -> T) -> T { + str::as_bytes(s) { |bytes| + with_bytes_reader_between(bytes, 0u, str::len(s), f) + } +} + +// Writing +enum fileflag { append, create, truncate, no_flag, } + +// FIXME: Seekable really should be orthogonal. +// FIXME: eventually u64 +iface writer { + fn write([const u8]); + fn seek(int, seek_style); + fn tell() -> uint; + fn flush() -> int; +} + +impl <T: writer, C> of writer for {base: T, cleanup: C} { + fn write(bs: [const u8]) { self.base.write(bs); } + fn seek(off: int, style: seek_style) { self.base.seek(off, style); } + fn tell() -> uint { self.base.tell() } + fn flush() -> int { self.base.flush() } +} + +impl of writer for *libc::FILE { + fn write(v: [const u8]) unsafe { + let len = vec::len(v); + vec::as_buf(v) {|vbuf| + let nout = libc::fwrite(vbuf as *c_void, len, 1u, self); + if nout < 1 as size_t { + #error("error writing buffer"); + log(error, sys::last_os_error()); + fail; + } + } + } + fn seek(offset: int, whence: seek_style) { + assert libc::fseek(self, offset, convert_whence(whence)) + == 0 as c_int; + } + fn tell() -> uint { libc::ftell(self) as uint } + fn flush() -> int { libc::fflush(self) as int } +} + +fn FILE_writer(f: *libc::FILE, cleanup: bool) -> writer { + if cleanup { + {base: f, cleanup: FILE_res(f)} as writer + } else { + f as writer + } +} + +impl of writer for fd_t { + fn write(v: [const u8]) unsafe { + let len = vec::len(v); + let mut count = 0u; + vec::as_buf(v) {|vbuf| + while count < len { + let vb = ptr::offset(vbuf, count) as *c_void; + let nout = libc::write(self, vb, len); + if nout < 0 as ssize_t { + #error("error writing buffer"); + log(error, sys::last_os_error()); + fail; + } + count += nout as uint; + } + } + } + fn seek(_offset: int, _whence: seek_style) { + #error("need 64-bit native calls for seek, sorry"); + fail; + } + fn tell() -> uint { + #error("need 64-bit native calls for tell, sorry"); + fail; + } + fn flush() -> int { 0 } +} + +resource fd_res(fd: fd_t) { libc::close(fd); } + +fn fd_writer(fd: fd_t, cleanup: bool) -> writer { + if cleanup { + {base: fd, cleanup: fd_res(fd)} as writer + } else { + fd as writer + } +} + + +fn mk_file_writer(path: str, flags: [fileflag]) + -> result::t<writer, str> { + + #[cfg(target_os = "win32")] + fn wb() -> c_int { (O_WRONLY | O_BINARY) as c_int } + + #[cfg(target_os = "linux")] + #[cfg(target_os = "macos")] + #[cfg(target_os = "freebsd")] + fn wb() -> c_int { O_WRONLY as c_int } + + let mut fflags: c_int = wb(); + for f: fileflag in flags { + alt f { + append { fflags |= O_APPEND as c_int; } + create { fflags |= O_CREAT as c_int; } + truncate { fflags |= O_TRUNC as c_int; } + no_flag { } + } + } + let fd = os::as_c_charp(path) {|pathbuf| + libc::open(pathbuf, fflags, + (S_IRUSR | S_IWUSR) as c_int) + }; + if fd < (0 as c_int) { + // FIXME don't log this! put it in the returned error string + log(error, sys::last_os_error()); + result::err("error opening " + path) + } else { + result::ok(fd_writer(fd, true)) + } +} + +fn u64_to_le_bytes(n: u64, size: uint) -> [u8] { + let mut bytes: [u8] = [], i = size, n = n; + while i > 0u { + bytes += [(n & 255_u64) as u8]; + n >>= 8_u64; + i -= 1u; + } + ret bytes; +} + +fn u64_to_be_bytes(n: u64, size: uint) -> [u8] { + assert size <= 8u; + let mut bytes: [u8] = []; + let mut i = size; + while i > 0u { + let shift = ((i - 1u) * 8u) as u64; + bytes += [(n >> shift) as u8]; + i -= 1u; + } + ret bytes; +} + +fn u64_from_be_bytes(data: [u8], start: uint, size: uint) -> u64 { + let mut sz = size; + assert (sz <= 8u); + let mut val = 0_u64; + let mut pos = start; + while sz > 0u { + sz -= 1u; + val += (data[pos] as u64) << ((sz * 8u) as u64); + pos += 1u; + } + ret val; +} + +impl writer_util for writer { + fn write_char(ch: char) { + if ch as uint < 128u { + self.write([ch as u8]); + } else { + self.write(str::bytes(str::from_char(ch))); + } + } + fn write_str(s: str) { self.write(str::bytes(s)); } + fn write_line(s: str) { self.write(str::bytes(s + "\n")); } + fn write_int(n: int) { self.write(str::bytes(int::to_str(n, 10u))); } + fn write_uint(n: uint) { self.write(str::bytes(uint::to_str(n, 10u))); } + + fn write_le_uint(n: uint, size: uint) { + self.write(u64_to_le_bytes(n as u64, size)); + } + fn write_le_int(n: int, size: uint) { + self.write(u64_to_le_bytes(n as u64, size)); + } + + fn write_be_uint(n: uint, size: uint) { + self.write(u64_to_be_bytes(n as u64, size)); + } + fn write_be_int(n: int, size: uint) { + self.write(u64_to_be_bytes(n as u64, size)); + } + + fn write_be_u64(n: u64) { self.write(u64_to_be_bytes(n, 8u)); } + fn write_be_u32(n: u32) { self.write(u64_to_be_bytes(n as u64, 4u)); } + fn write_be_u16(n: u16) { self.write(u64_to_be_bytes(n as u64, 2u)); } + + fn write_be_i64(n: i64) { self.write(u64_to_be_bytes(n as u64, 8u)); } + fn write_be_i32(n: i32) { self.write(u64_to_be_bytes(n as u64, 4u)); } + fn write_be_i16(n: i16) { self.write(u64_to_be_bytes(n as u64, 2u)); } + + fn write_le_u64(n: u64) { self.write(u64_to_le_bytes(n, 8u)); } + fn write_le_u32(n: u32) { self.write(u64_to_le_bytes(n as u64, 4u)); } + fn write_le_u16(n: u16) { self.write(u64_to_le_bytes(n as u64, 2u)); } + + fn write_le_i64(n: i64) { self.write(u64_to_le_bytes(n as u64, 8u)); } + fn write_le_i32(n: i32) { self.write(u64_to_le_bytes(n as u64, 4u)); } + fn write_le_i16(n: i16) { self.write(u64_to_le_bytes(n as u64, 2u)); } + + fn write_u8(n: u8) { self.write([n]) } +} + +fn file_writer(path: str, flags: [fileflag]) -> result::t<writer, str> { + result::chain(mk_file_writer(path, flags), { |w| result::ok(w)}) +} + + +// FIXME: fileflags +fn buffered_file_writer(path: str) -> result::t<writer, str> { + let f = os::as_c_charp(path) {|pathbuf| + os::as_c_charp("w") {|modebuf| + libc::fopen(pathbuf, modebuf) + } + }; + ret if f as uint == 0u { result::err("error opening " + path) } + else { result::ok(FILE_writer(f, true)) } +} + +// FIXME it would be great if this could be a const +// FIXME why are these different from the way stdin() is implemented? +fn stdout() -> writer { fd_writer(libc::STDOUT_FILENO as c_int, false) } +fn stderr() -> writer { fd_writer(libc::STDERR_FILENO as c_int, false) } + +fn print(s: str) { stdout().write_str(s); } +fn println(s: str) { stdout().write_line(s); } + +type mem_buffer = @{mutable buf: [mutable u8], + mutable pos: uint}; + +impl of writer for mem_buffer { + fn write(v: [const u8]) { + // Fast path. + if self.pos == vec::len(self.buf) { + for b: u8 in v { self.buf += [mutable b]; } + self.pos += vec::len(v); + ret; + } + // FIXME: Optimize: These should be unique pointers. + let vlen = vec::len(v); + let mut vpos = 0u; + while vpos < vlen { + let b = v[vpos]; + if self.pos == vec::len(self.buf) { + self.buf += [mutable b]; + } else { self.buf[self.pos] = b; } + self.pos += 1u; + vpos += 1u; + } + } + fn seek(offset: int, whence: seek_style) { + let pos = self.pos; + let len = vec::len(self.buf); + self.pos = seek_in_buf(offset, pos, len, whence); + } + fn tell() -> uint { self.pos } + fn flush() -> int { 0 } +} + +fn mk_mem_buffer() -> mem_buffer { + @{mutable buf: [mutable], mutable pos: 0u} +} +fn mem_buffer_writer(b: mem_buffer) -> writer { b as writer } +fn mem_buffer_buf(b: mem_buffer) -> [u8] { vec::from_mut(b.buf) } +fn mem_buffer_str(b: mem_buffer) -> str { + let b_ = vec::from_mut(b.buf); + str::from_bytes(b_) +} + +fn with_str_writer(f: fn(writer)) -> str { + let buf = mk_mem_buffer(); + let wr = mem_buffer_writer(buf); + f(wr); + io::mem_buffer_str(buf) +} + +fn with_buf_writer(f: fn(writer)) -> [u8] { + let buf = mk_mem_buffer(); + let wr = mem_buffer_writer(buf); + f(wr); + io::mem_buffer_buf(buf) +} + +// Utility functions +fn seek_in_buf(offset: int, pos: uint, len: uint, whence: seek_style) -> + uint { + let mut bpos = pos as int; + let blen = len as int; + alt whence { + seek_set { bpos = offset; } + seek_cur { bpos += offset; } + seek_end { bpos = blen + offset; } + } + if bpos < 0 { bpos = 0; } else if bpos > blen { bpos = blen; } + ret bpos as uint; +} + +fn read_whole_file_str(file: str) -> result::t<str, str> { + result::chain(read_whole_file(file), { |bytes| + result::ok(str::from_bytes(bytes)) + }) +} + +// FIXME implement this in a low-level way. Going through the abstractions is +// pointless. +fn read_whole_file(file: str) -> result::t<[u8], str> { + result::chain(file_reader(file), { |rdr| + result::ok(rdr.read_whole_stream()) + }) +} + +// fsync related + +mod fsync { + + enum level { + // whatever fsync does on that platform + fsync, + + // fdatasync on linux, similiar or more on other platforms + fdatasync, + + // full fsync + // + // You must additionally sync the parent directory as well! + fullfsync, + } + + + // Resource of artifacts that need to fsync on destruction + resource res<t>(arg: arg<t>) { + alt arg.opt_level { + option::none { } + option::some(level) { + // fail hard if not succesful + assert(arg.fsync_fn(arg.val, level) != -1); + } + } + } + + type arg<t> = { + val: t, + opt_level: option<level>, + fsync_fn: fn@(t, level) -> int + }; + + // fsync file after executing blk + // FIXME find better way to create resources within lifetime of outer res + fn FILE_res_sync(&&file: FILE_res, opt_level: option<level>, + blk: fn(&&res<*libc::FILE>)) { + blk(res({ + val: *file, opt_level: opt_level, + fsync_fn: fn@(&&file: *libc::FILE, l: level) -> int { + ret os::fsync_fd(libc::fileno(file), l) as int; + } + })); + } + + // fsync fd after executing blk + fn fd_res_sync(&&fd: fd_res, opt_level: option<level>, + blk: fn(&&res<fd_t>)) { + blk(res({ + val: *fd, opt_level: opt_level, + fsync_fn: fn@(&&fd: fd_t, l: level) -> int { + ret os::fsync_fd(fd, l) as int; + } + })); + } + + // Type of objects that may want to fsync + iface t { fn fsync(l: level) -> int; } + + // Call o.fsync after executing blk + fn obj_sync(&&o: t, opt_level: option<level>, blk: fn(&&res<t>)) { + blk(res({ + val: o, opt_level: opt_level, + fsync_fn: fn@(&&o: t, l: level) -> int { ret o.fsync(l); } + })); + } +} + +#[cfg(test)] +mod tests { + + #[test] + fn test_simple() { + let tmpfile: str = "tmp/lib-io-test-simple.tmp"; + log(debug, tmpfile); + let frood: str = "A hoopy frood who really knows where his towel is."; + log(debug, frood); + { + let out: io::writer = + result::get( + io::file_writer(tmpfile, [io::create, io::truncate])); + out.write_str(frood); + } + let inp: io::reader = result::get(io::file_reader(tmpfile)); + let frood2: str = inp.read_c_str(); + log(debug, frood2); + assert (str::eq(frood, frood2)); + } + + #[test] + fn test_readchars_empty() { + let inp : io::reader = io::str_reader(""); + let res : [char] = inp.read_chars(128u); + assert(vec::len(res) == 0u); + } + + #[test] + fn test_readchars_wide() { + let wide_test = "生锈的汤匙切肉汤hello生锈的汤匙切肉汤"; + let ivals : [int] = [ + 29983, 38152, 30340, 27748, + 21273, 20999, 32905, 27748, + 104, 101, 108, 108, 111, + 29983, 38152, 30340, 27748, + 21273, 20999, 32905, 27748]; + fn check_read_ln(len : uint, s: str, ivals: [int]) { + let inp : io::reader = io::str_reader(s); + let res : [char] = inp.read_chars(len); + if (len <= vec::len(ivals)) { + assert(vec::len(res) == len); + } + assert(vec::slice(ivals, 0u, vec::len(res)) == + vec::map(res, {|x| x as int})); + } + let i = 0u; + while i < 8u { + check_read_ln(i, wide_test, ivals); + i += 1u; + } + // check a long read for good measure + check_read_ln(128u, wide_test, ivals); + } + + #[test] + fn test_readchar() { + let inp : io::reader = io::str_reader("生"); + let res : char = inp.read_char(); + assert(res as int == 29983); + } + + #[test] + fn test_readchar_empty() { + let inp : io::reader = io::str_reader(""); + let res : char = inp.read_char(); + assert(res as int == -1); + } + + #[test] + fn file_reader_not_exist() { + alt io::file_reader("not a file") { + result::err(e) { + assert e == "error opening not a file"; + } + result::ok(_) { fail; } + } + } + + #[test] + fn file_writer_bad_name() { + alt io::file_writer("?/?", []) { + result::err(e) { + assert e == "error opening ?/?"; + } + result::ok(_) { fail; } + } + } + + #[test] + fn buffered_file_writer_bad_name() { + alt io::buffered_file_writer("?/?") { + result::err(e) { + assert e == "error opening ?/?"; + } + result::ok(_) { fail; } + } + } +} + +// +// Local Variables: +// mode: rust +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: +// diff --git a/src/libcore/libc.rs b/src/libcore/libc.rs index c1ce3c72a30..876e5881871 100644 --- a/src/libcore/libc.rs +++ b/src/libcore/libc.rs @@ -96,6 +96,14 @@ export size_t, ptrdiff_t, clock_t, time_t; export c_longlong, c_ulonglong, intptr_t, uintptr_t; export off_t, dev_t, ino_t, pid_t, mode_t, ssize_t; +export EXIT_FAILURE, EXIT_SUCCESS, RAND_MAX, + EOF, SEEK_SET, SEEK_CUR, SEEK_END, _IOFBF, _IONBF, _IOLBF, + BUFSIZ, FOPEN_MAX, FILENAME_MAX, L_tmpnam, TMP_MAX, + O_RDONLY, O_WRONLY, O_RDWR, O_APPEND, O_CREAT, O_EXCL, O_TRUNC, + S_IFIFO, S_IFCHR, S_IFBLK, S_IFDIR, S_IFREG, S_IFMT, S_IEXEC, + S_IWRITE, S_IREAD, S_IRWXU, S_IXUSR, S_IWUSR, S_IRUSR, F_OK, R_OK, + W_OK, X_OK, STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO; + export isalnum, isalpha, iscntrl, isdigit, islower, isprint, ispunct, isspace, isupper, isxdigit, tolower, toupper; @@ -111,7 +119,7 @@ export strcpy, strncpy, strcat, strncat, strcmp, strncmp, strcoll, strchr, strxfrm, memcpy, memmove, memcmp, memchr, memset; export chmod, mkdir; -export popen, pclose, fdopen; +export popen, pclose, fdopen, fileno; export open, creat; export access, chdir, close, dup, dup2, execv, execve, execvp, getcwd, getpid, isatty, lseek, pipe, read, rmdir, unlink, write; @@ -495,9 +503,9 @@ mod consts { const R_OK : int = 4; const W_OK : int = 2; const X_OK : int = 1; - const STDERR_FILENO : int = 2; const STDIN_FILENO : int = 0; const STDOUT_FILENO : int = 1; + const STDERR_FILENO : int = 2; } mod posix01 { } mod posix08 { } @@ -558,6 +566,9 @@ mod consts { const R_OK : int = 4; const W_OK : int = 2; const X_OK : int = 1; + const STDIN_FILENO : int = 0; + const STDOUT_FILENO : int = 1; + const STDERR_FILENO : int = 2; const F_LOCK : int = 1; const F_TEST : int = 3; const F_TLOCK : int = 2; @@ -618,9 +629,9 @@ mod consts { const R_OK : int = 4; const W_OK : int = 2; const X_OK : int = 1; - const STDERR_FILENO : int = 2; const STDIN_FILENO : int = 0; const STDOUT_FILENO : int = 1; + const STDERR_FILENO : int = 2; const F_LOCK : int = 1; const F_TEST : int = 3; const F_TLOCK : int = 2; @@ -682,9 +693,9 @@ mod consts { const R_OK : int = 4; const W_OK : int = 2; const X_OK : int = 1; - const STDERR_FILENO : int = 2; const STDIN_FILENO : int = 0; const STDOUT_FILENO : int = 1; + const STDERR_FILENO : int = 2; const F_LOCK : int = 1; const F_TEST : int = 3; const F_TLOCK : int = 2; @@ -744,7 +755,8 @@ mod funcs { fn setbuf(stream: *FILE, buf: *c_char); // Omitted: printf and scanf variants. fn fgetc(stream: *FILE) -> c_int; - fn fgets(buf: *c_char, n: c_int, stream: *FILE) -> *c_char; + fn fgets(buf: *mutable c_char, n: c_int, + stream: *FILE) -> *c_char; fn fputc(c: c_int, stream: *FILE) -> c_int; fn fputs(s: *c_char, stream: *FILE) -> *c_char; // Omitted: getc, getchar (might be macros). @@ -755,7 +767,7 @@ mod funcs { // Omitted: putc, putchar (might be macros). fn puts(s: *c_char) -> c_int; fn ungetc(c: c_int, stream: *FILE) -> c_int; - fn fread(ptr: *c_void, size: size_t, + fn fread(ptr: *mutable c_void, size: size_t, nobj: size_t, stream: *FILE) -> size_t; fn fwrite(ptr: *c_void, size: size_t, nobj: size_t, stream: *FILE) -> size_t; @@ -863,7 +875,7 @@ mod funcs { #[abi = "cdecl"] native mod fcntl { #[link_name = "_open"] - fn open(path: *c_char, oflag: c_int) -> c_int; + fn open(path: *c_char, oflag: c_int, mode: c_int) -> c_int; #[link_name = "_creat"] fn creat(path: *c_char, mode: c_int) -> c_int; @@ -923,7 +935,7 @@ mod funcs { textmode: c_int) -> c_int; #[link_name = "_read"] - fn read(fd: c_int, buf: *c_void, count: c_uint) -> c_int; + fn read(fd: c_int, buf: *mutable c_void, count: c_uint) -> c_int; #[link_name = "_rmdir"] fn rmdir(path: *c_char) -> c_int; @@ -932,7 +944,7 @@ mod funcs { fn unlink(c: *c_char) -> c_int; #[link_name = "_write"] - fn write(fd: c_int, buf: *c_void, count: c_uint) -> c_uint; + fn write(fd: c_int, buf: *c_void, count: c_uint) -> c_int; } } @@ -964,7 +976,7 @@ mod funcs { #[nolink] #[abi = "cdecl"] native mod fcntl { - fn open(path: *c_char, oflag: c_int) -> c_int; + fn open(path: *c_char, oflag: c_int, mode: c_int) -> c_int; fn creat(path: *c_char, mode: mode_t) -> c_int; fn fcntl(fd: c_int, cmd: c_int) -> c_int; } @@ -999,7 +1011,7 @@ mod funcs { fn getegid() -> gid_t; fn geteuid() -> uid_t; fn getgid() -> gid_t ; - fn getgroups(ngroups_max: c_int, groups: *gid_t) -> c_int; + fn getgroups(ngroups_max: c_int, groups: *mutable gid_t) -> c_int; fn getlogin() -> *c_char; fn getopt(argc: c_int, argv: **c_char, optstr: *c_char) -> c_int; fn getpgrp() -> pid_t; @@ -1012,7 +1024,8 @@ mod funcs { fn pathconf(path: *c_char, name: c_int) -> c_long; fn pause() -> c_int; fn pipe(fds: *mutable c_int) -> c_int; - fn read(fd: c_int, buf: *c_void, count: size_t) -> ssize_t; + fn read(fd: c_int, buf: *mutable c_void, + count: size_t) -> ssize_t; fn rmdir(path: *c_char) -> c_int; fn setgid(gid: gid_t) -> c_int; fn setpgid(pid: pid_t, pgid: pid_t) -> c_int; diff --git a/src/libcore/os.rs b/src/libcore/os.rs index 42d74e4a94c..eeca52f7bc0 100644 --- a/src/libcore/os.rs +++ b/src/libcore/os.rs @@ -26,11 +26,15 @@ import option = option::t; import getcwd = rustrt::rust_getcwd; import consts::*; -export close, fclose, fsync_fd; +export close, fclose, fsync_fd, waitpid; export env, getenv, setenv, fdopen, pipe; export getcwd, dll_filename, self_exe_path; export exe_suffix, dll_suffix, sysname; -export homedir, list_dir, path_is_dir, path_exists; +export homedir, list_dir, path_is_dir, path_exists, make_absolute, + make_dir, remove_dir, change_dir, remove_file; + +// FIXME: move these to str perhaps? +export as_c_charp, fill_charp_buf; native mod rustrt { fn rust_env_pairs() -> [str]; @@ -76,16 +80,32 @@ mod win32 { fn fill_utf16_buf_and_decode(f: fn(*mutable u16, dword) -> dword) -> option<str> { - let buf = vec::to_mut(vec::init_elt(tmpbuf_sz, 0u16)); - vec::as_mut_buf(buf) {|b| - let k : dword = f(b, tmpbuf_sz as dword); - if k == (0 as dword) { - none - } else { - let sub = vec::slice(buf, 0u, k as uint); - option::some::<str>(str::from_utf16(sub)) + + // FIXME: remove these when export globs work properly. + import libc::funcs::extra::kernel32::*; + import libc::consts::os::extra::*; + + let mut n = tmpbuf_sz; + let mut res = none; + let mut done = false; + while !done { + let buf = vec::to_mut(vec::init_elt(n, 0u16)); + vec::as_mut_buf(buf) {|b| + let k : dword = f(b, tmpbuf_sz as dword); + if k == (0 as dword) { + done = true; + } else if (k == n && + GetLastError() == + ERROR_INSUFFICIENT_BUFFER as dword) { + n *= (2 as dword); + } else { + let sub = vec::slice(buf, 0u, k as uint); + res = option::some::<str>(str::from_utf16(sub)); + done = true; + } } } + ret res; } fn as_utf16_p<T>(s: str, f: fn(*u16) -> T) -> T { @@ -160,41 +180,29 @@ fn fdopen(fd: c_int) -> *FILE { // fsync related -enum fsync_level { - // whatever fsync does on that platform - fsync, - - // fdatasync on linux, similiar or more on other platforms - fdatasync, - - // full fsync - // - // You must additionally sync the parent directory as well! - fullfsync, -} - #[cfg(target_os = "win32")] -fn fsync_fd(fd: c_int, _level: fsync_level) -> c_int { +fn fsync_fd(fd: c_int, _level: io::fsync::level) -> c_int { import libc::funcs::extra::msvcrt::*; ret commit(fd); } #[cfg(target_os = "linux")] -fn fsync_fd(fd: c_int, level: fsync_level) -> c_int { +fn fsync_fd(fd: c_int, level: io::fsync::level) -> c_int { import libc::funcs::posix01::unistd::*; alt level { - fsync | fullfsync { ret fsync(fd); } - fdatasync { ret fdatasync(fd); } + io::fsync::fsync + | io::fsync::fullfsync { ret fsync(fd); } + io::fsync::fdatasync { ret fdatasync(fd); } } } #[cfg(target_os = "macos")] -fn fsync_fd(fd: c_int, level: fsync_level) -> c_int { +fn fsync_fd(fd: c_int, level: io::fsync::level) -> c_int { import libc::consts::os::extra::*; import libc::funcs::posix88::fcntl::*; import libc::funcs::posix01::unistd::*; alt level { - fsync { ret fsync(fd); } + io::fsync::fsync { ret fsync(fd); } _ { // According to man fnctl, the ok retval is only specified to be !=-1 if (fcntl(F_FULLFSYNC as c_int, fd) == -1 as c_int) @@ -206,7 +214,7 @@ fn fsync_fd(fd: c_int, level: fsync_level) -> c_int { } #[cfg(target_os = "freebsd")] -fn fsync_fd(fd: c_int, _l: fsync_level) -> c_int { +fn fsync_fd(fd: c_int, _l: io::fsync::level) -> c_int { import libc::funcs::posix01::unistd::*; ret fsync(fd); } @@ -577,6 +585,89 @@ mod consts { #[cfg(test)] mod tests { + + fn make_rand_name() -> str { + import rand; + let rng: rand::rng = rand::mk_rng(); + let n = "TEST" + rng.gen_str(10u); + assert option::is_none(getenv(n)); + n + } + + #[test] + #[ignore(reason = "fails periodically on mac")] + fn test_setenv() { + let n = make_rand_name(); + setenv(n, "VALUE"); + assert getenv(n) == option::some("VALUE"); + } + + #[test] + #[ignore(reason = "fails periodically on mac")] + fn test_setenv_overwrite() { + let n = make_rand_name(); + setenv(n, "1"); + setenv(n, "2"); + assert getenv(n) == option::some("2"); + setenv(n, ""); + assert getenv(n) == option::some(""); + } + + // Windows GetEnvironmentVariable requires some extra work to make sure + // the buffer the variable is copied into is the right size + #[test] + #[ignore(reason = "fails periodically on mac")] + fn test_getenv_big() { + let s = ""; + let i = 0; + while i < 100 { s += "aaaaaaaaaa"; i += 1; } + let n = make_rand_name(); + setenv(n, s); + log(debug, s); + assert getenv(n) == option::some(s); + } + + #[test] + fn test_self_exe_path() { + let path = os::self_exe_path(); + assert option::is_some(path); + let path = option::get(path); + log(debug, path); + + // Hard to test this function + if os::sysname() != "win32" { + assert str::starts_with(path, path::path_sep()); + } else { + assert path[1] == ':' as u8; + } + } + + #[test] + fn test_env_getenv() { + let e = env(); + assert vec::len(e) > 0u; + for (n, v) in e { + log(debug, n); + let v2 = getenv(n); + // MingW seems to set some funky environment variables like + // "=C:=C:\MinGW\msys\1.0\bin" and "!::=::\" that are returned + // from env() but not visible from getenv(). + assert option::is_none(v2) || v2 == option::some(v); + } + } + + #[test] + fn test_env_setenv() { + let n = make_rand_name(); + + let e = env(); + setenv(n, "VALUE"); + assert !vec::contains(e, (n, "VALUE")); + + e = env(); + assert vec::contains(e, (n, "VALUE")); + } + #[test] fn test() { assert (!path::path_is_absolute("test-path")); diff --git a/src/libcore/ptr.rs b/src/libcore/ptr.rs index 8206cdd45f7..72552d318d2 100644 --- a/src/libcore/ptr.rs +++ b/src/libcore/ptr.rs @@ -12,9 +12,9 @@ export memmove; #[abi = "rust-intrinsic"] native mod rusti { fn addr_of<T>(val: T) -> *T; - fn ptr_offset<T>(ptr: *T, count: ctypes::uintptr_t) -> *T; - fn memcpy<T>(dst: *T, src: *T, count: ctypes::uintptr_t); - fn memmove<T>(dst: *T, src: *T, count: ctypes::uintptr_t); + fn ptr_offset<T>(ptr: *T, count: libc::uintptr_t) -> *T; + fn memcpy<T>(dst: *T, src: *T, count: libc::uintptr_t); + fn memmove<T>(dst: *T, src: *T, count: libc::uintptr_t); } #[doc = "Get an unsafe pointer to a value"] diff --git a/src/libcore/rand.rs b/src/libcore/rand.rs new file mode 100644 index 00000000000..5744323fe77 --- /dev/null +++ b/src/libcore/rand.rs @@ -0,0 +1,113 @@ +#[doc = "Random number generation"] + +enum rctx {} + +#[abi = "cdecl"] +native mod rustrt { + fn rand_new() -> *rctx; + fn rand_next(c: *rctx) -> u32; + fn rand_free(c: *rctx); +} + +#[doc = "A random number generator"] +iface rng { + #[doc = "Return the next random integer"] + fn next() -> u32; + + #[doc = "Return the next random float"] + fn next_float() -> float; + + #[doc = "Return a random string composed of A-Z, a-z, 0-9."] + fn gen_str(len: uint) -> str; + + #[doc = "Return a random byte string."] + fn gen_bytes(len: uint) -> [u8]; +} + +resource rand_res(c: *rctx) { rustrt::rand_free(c); } + +#[doc = "Create a random number generator"] +fn mk_rng() -> rng { + impl of rng for @rand_res { + fn next() -> u32 { ret rustrt::rand_next(**self); } + fn next_float() -> float { + let u1 = rustrt::rand_next(**self) as float; + let u2 = rustrt::rand_next(**self) as float; + let u3 = rustrt::rand_next(**self) as float; + let scale = u32::max_value as float; + ret ((u1 / scale + u2) / scale + u3) / scale; + } + fn gen_str(len: uint) -> str { + let charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + + "abcdefghijklmnopqrstuvwxyz" + + "0123456789"; + let mut s = ""; + let mut i = 0u; + while (i < len) { + let n = rustrt::rand_next(**self) as uint % + str::len(charset); + s = s + str::from_char(str::char_at(charset, n)); + i += 1u; + } + s + } + fn gen_bytes(len: uint) -> [u8] { + let mut v = []; + let mut i = 0u; + while i < len { + let n = rustrt::rand_next(**self) as uint; + v += [(n % (u8::max_value as uint)) as u8]; + i += 1u; + } + v + } + } + @rand_res(rustrt::rand_new()) as rng +} + +#[cfg(test)] +mod tests { + + #[test] + fn test() { + let r1: rand::rng = rand::mk_rng(); + log(debug, r1.next()); + log(debug, r1.next()); + { + let r2 = rand::mk_rng(); + log(debug, r1.next()); + log(debug, r2.next()); + log(debug, r1.next()); + log(debug, r1.next()); + log(debug, r2.next()); + log(debug, r2.next()); + log(debug, r1.next()); + log(debug, r1.next()); + log(debug, r1.next()); + log(debug, r2.next()); + log(debug, r2.next()); + log(debug, r2.next()); + } + log(debug, r1.next()); + log(debug, r1.next()); + } + + #[test] + fn genstr() { + let r: rand::rng = rand::mk_rng(); + log(debug, r.gen_str(10u)); + log(debug, r.gen_str(10u)); + log(debug, r.gen_str(10u)); + assert(str::len(r.gen_str(10u)) == 10u); + assert(str::len(r.gen_str(16u)) == 16u); + } +} + + +// Local Variables: +// mode: rust; +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: diff --git a/src/libcore/run.rs b/src/libcore/run.rs new file mode 100644 index 00000000000..4777b74c00e --- /dev/null +++ b/src/libcore/run.rs @@ -0,0 +1,394 @@ +#[doc ="Process spawning"]; +import option::{some, none}; +import str::sbuf; +import libc::{pid_t, c_void, c_int}; + +export program; +export run_program; +export start_program; +export program_output; +export spawn_process; +export waitpid; + +#[abi = "cdecl"] +native mod rustrt { + fn rust_run_program(argv: *sbuf, envp: *c_void, dir: sbuf, + in_fd: c_int, out_fd: c_int, err_fd: c_int) + -> pid_t; +} + +#[doc ="A value representing a child process"] +iface program { + #[doc ="Returns the process id of the program"] + fn get_id() -> pid_t; + + #[doc ="Returns an io::writer that can be used to write to stdin"] + fn input() -> io::writer; + + #[doc ="Returns an io::reader that can be used to read from stdout"] + fn output() -> io::reader; + + #[doc ="Returns an io::reader that can be used to read from stderr"] + fn err() -> io::reader; + + #[doc = "Closes the handle to the child processes standard input"] + fn close_input(); + + #[doc = " + Waits for the child process to terminate. Closes the handle + to stdin if necessary. + "] + fn finish() -> int; + + #[doc ="Closes open handles"] + fn destroy(); +} + + +#[doc = " +Run a program, providing stdin, stdout and stderr handles + +# Arguments + +* prog - The path to an executable +* args - Vector of arguments to pass to the child process +* env - optional env-modification for child +* dir - optional dir to run child in (default current dir) +* in_fd - A file descriptor for the child to use as std input +* out_fd - A file descriptor for the child to use as std output +* err_fd - A file descriptor for the child to use as std error + +# Return value + +The process id of the spawned process +"] +fn spawn_process(prog: str, args: [str], + env: option<[(str,str)]>, + dir: option<str>, + in_fd: c_int, out_fd: c_int, err_fd: c_int) + -> pid_t unsafe { + with_argv(prog, args) {|argv| + with_envp(env) { |envp| + with_dirp(dir) { |dirp| + rustrt::rust_run_program(argv, envp, dirp, + in_fd, out_fd, err_fd) + } + } + } +} + +fn with_argv<T>(prog: str, args: [str], + cb: fn(*sbuf) -> T) -> T unsafe { + let mut argptrs = str::as_buf(prog) {|b| [b] }; + let mut tmps = []; + for arg in args { + let t = @arg; + tmps += [t]; + argptrs += str::as_buf(*t) {|b| [b] }; + } + argptrs += [ptr::null()]; + vec::as_buf(argptrs, cb) +} + +#[cfg(target_os = "macos")] +#[cfg(target_os = "linux")] +#[cfg(target_os = "freebsd")] +fn with_envp<T>(env: option<[(str,str)]>, + cb: fn(*c_void) -> T) -> T unsafe { + // On posixy systems we can pass a char** for envp, which is + // a null-terminated array of "k=v\n" strings. + alt env { + some (es) { + let mut tmps = []; + let mut ptrs = []; + + for (k,v) in es { + let t = @(#fmt("%s=%s", k, v)); + vec::push(tmps, t); + ptrs += str::as_buf(*t) {|b| [b]}; + } + ptrs += [ptr::null()]; + vec::as_buf(ptrs) { |p| cb(::unsafe::reinterpret_cast(p)) } + } + none { + cb(ptr::null()) + } + } +} + +#[cfg(target_os = "win32")] +fn with_envp<T>(env: option<[(str,str)]>, + cb: fn(*c_void) -> T) -> T unsafe { + // On win32 we pass an "environment block" which is not a char**, but + // rather a concatenation of null-terminated k=v\0 sequences, with a final + // \0 to terminate. + alt env { + some (es) { + let mut blk : [u8] = []; + for (k,v) in es { + let t = #fmt("%s=%s", k, v); + let mut v : [u8] = ::unsafe::reinterpret_cast(t); + blk += v; + ::unsafe::leak(v); + } + blk += [0_u8]; + vec::as_buf(blk) {|p| cb(::unsafe::reinterpret_cast(p)) } + } + none { + cb(ptr::null()) + } + } +} + +fn with_dirp<T>(d: option<str>, + cb: fn(sbuf) -> T) -> T unsafe { + alt d { + some(dir) { str::as_buf(dir, cb) } + none { cb(ptr::null()) } + } +} + +#[doc =" +Spawns a process and waits for it to terminate + +# Arguments + +* prog - The path to an executable +* args - Vector of arguments to pass to the child process + +# Return value + +The process id +"] +fn run_program(prog: str, args: [str]) -> int { + ret waitpid(spawn_process(prog, args, none, none, + 0i32, 0i32, 0i32)); +} + +#[doc =" +Spawns a process and returns a program + +The returned value is a boxed resource containing a <program> object that can +be used for sending and recieving data over the standard file descriptors. +The resource will ensure that file descriptors are closed properly. + +# Arguments + +* prog - The path to an executable +* args - Vector of arguments to pass to the child process + +# Return value + +A boxed resource of <program> +"] +fn start_program(prog: str, args: [str]) -> program { + let pipe_input = os::pipe(); + let pipe_output = os::pipe(); + let pipe_err = os::pipe(); + let pid = + spawn_process(prog, args, none, none, + pipe_input.in, pipe_output.out, + pipe_err.out); + + if pid == -1i32 { fail; } + libc::close(pipe_input.in); + libc::close(pipe_output.out); + libc::close(pipe_err.out); + + type prog_repr = {pid: pid_t, + mutable in_fd: c_int, + out_file: *libc::FILE, + err_file: *libc::FILE, + mutable finished: bool}; + + fn close_repr_input(r: prog_repr) { + let invalid_fd = -1i32; + if r.in_fd != invalid_fd { + libc::close(r.in_fd); + r.in_fd = invalid_fd; + } + } + fn finish_repr(r: prog_repr) -> int { + if r.finished { ret 0; } + r.finished = true; + close_repr_input(r); + ret waitpid(r.pid); + } + fn destroy_repr(r: prog_repr) { + finish_repr(r); + libc::fclose(r.out_file); + libc::fclose(r.err_file); + } + resource prog_res(r: prog_repr) { destroy_repr(r); } + + impl of program for prog_res { + fn get_id() -> pid_t { ret self.pid; } + fn input() -> io::writer { io::fd_writer(self.in_fd, false) } + fn output() -> io::reader { io::FILE_reader(self.out_file, false) } + fn err() -> io::reader { io::FILE_reader(self.err_file, false) } + fn close_input() { close_repr_input(*self); } + fn finish() -> int { finish_repr(*self) } + fn destroy() { destroy_repr(*self); } + } + let repr = {pid: pid, + mutable in_fd: pipe_input.out, + out_file: os::fdopen(pipe_output.in), + err_file: os::fdopen(pipe_err.in), + mutable finished: false}; + ret prog_res(repr) as program; +} + +fn read_all(rd: io::reader) -> str { + let mut buf = ""; + while !rd.eof() { + let bytes = rd.read_bytes(4096u); + buf += str::from_bytes(bytes); + } + ret buf; +} + +#[doc =" +Spawns a process, waits for it to exit, and returns the exit code, and +contents of stdout and stderr. + +# Arguments + +* prog - The path to an executable +* args - Vector of arguments to pass to the child process + +# Return value + +A record, {status: int, out: str, err: str} containing the exit code, +the contents of stdout and the contents of stderr. +"] +fn program_output(prog: str, args: [str]) -> + {status: int, out: str, err: str} { + let pr = start_program(prog, args); + pr.close_input(); + let out = read_all(pr.output()); + let err = read_all(pr.err()); + ret {status: pr.finish(), out: out, err: err}; +} + +#[doc ="Waits for a process to exit and returns the exit code"] +fn waitpid(pid: pid_t) -> int { + ret waitpid_os(pid); + + #[cfg(target_os = "win32")] + fn waitpid_os(pid: pid_t) -> int { + os::waitpid(pid) as int + } + + #[cfg(target_os = "linux")] + #[cfg(target_os = "macos")] + #[cfg(target_os = "freebsd")] + fn waitpid_os(pid: pid_t) -> int { + #[cfg(target_os = "linux")] + fn WIFEXITED(status: i32) -> bool { + (status & 0xffi32) == 0i32 + } + + #[cfg(target_os = "macos")] + #[cfg(target_os = "freebsd")] + fn WIFEXITED(status: i32) -> bool { + (status & 0x7fi32) == 0i32 + } + + #[cfg(target_os = "linux")] + fn WEXITSTATUS(status: i32) -> i32 { + (status >> 8i32) & 0xffi32 + } + + #[cfg(target_os = "macos")] + #[cfg(target_os = "freebsd")] + fn WEXITSTATUS(status: i32) -> i32 { + status >> 8i32 + } + + let status = os::waitpid(pid); + ret if WIFEXITED(status) { + WEXITSTATUS(status) as int + } else { + 1 + }; + } +} + +#[cfg(test)] +mod tests { + + import io::writer_util; + + // Regression test for memory leaks + #[ignore(cfg(target_os = "win32"))] // FIXME + fn test_leaks() { + run::run_program("echo", []); + run::start_program("echo", []); + run::program_output("echo", []); + } + + #[test] + fn test_pipes() { + let pipe_in = os::pipe(); + let pipe_out = os::pipe(); + let pipe_err = os::pipe(); + + let pid = + run::spawn_process( + "cat", [], none, none, + pipe_in.in, pipe_out.out, pipe_err.out); + os::close(pipe_in.in); + os::close(pipe_out.out); + os::close(pipe_err.out); + + if pid == -1i32 { fail; } + let expected = "test"; + writeclose(pipe_in.out, expected); + let actual = readclose(pipe_out.in); + readclose(pipe_err.in); + os::waitpid(pid); + + log(debug, expected); + log(debug, actual); + assert (expected == actual); + + fn writeclose(fd: c_int, s: str) { + #error("writeclose %d, %s", fd as int, s); + let writer = io::fd_writer(fd, false); + writer.write_str(s); + + os::close(fd); + } + + fn readclose(fd: c_int) -> str { + // Copied from run::program_output + let file = os::fdopen(fd); + let reader = io::FILE_reader(file, false); + let buf = ""; + while !reader.eof() { + let bytes = reader.read_bytes(4096u); + buf += str::from_bytes(bytes); + } + os::fclose(file); + ret buf; + } + } + + #[test] + fn waitpid() { + let pid = run::spawn_process("false", [], + none, none, + 0i32, 0i32, 0i32); + let status = run::waitpid(pid); + assert status == 1; + } + +} + +// Local Variables: +// mode: rust +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: diff --git a/src/libcore/str.rs b/src/libcore/str.rs index 52f342c81d5..b057d8d1483 100644 --- a/src/libcore/str.rs +++ b/src/libcore/str.rs @@ -99,7 +99,7 @@ export #[abi = "cdecl"] native mod rustrt { fn rust_str_push(&s: str, ch: u8); - fn str_reserve_shared(&ss: str, nn: ctypes::size_t); + fn str_reserve_shared(&ss: str, nn: libc::size_t); } // FIXME: add pure to a lot of functions diff --git a/src/libcore/sys.rs b/src/libcore/sys.rs index fc166df7fd4..b402f98ed26 100644 --- a/src/libcore/sys.rs +++ b/src/libcore/sys.rs @@ -10,9 +10,9 @@ export log_str; export set_exit_status; enum type_desc = { - first_param: **ctypes::c_int, - size: ctypes::size_t, - align: ctypes::size_t + first_param: **libc::c_int, + size: libc::size_t, + align: libc::size_t // Remaining fields not listed }; @@ -22,10 +22,10 @@ native mod rustrt { // available outside this crate. Otherwise it's // visible-in-crate, but not re-exported. fn last_os_error() -> str; - fn refcount<T>(t: @T) -> ctypes::intptr_t; + fn refcount<T>(t: @T) -> libc::intptr_t; fn unsupervise(); fn shape_log_str<T>(t: *sys::type_desc, data: T) -> str; - fn rust_set_exit_status(code: ctypes::intptr_t); + fn rust_set_exit_status(code: libc::intptr_t); } #[abi = "rust-intrinsic"] @@ -34,7 +34,7 @@ native mod rusti { // Invokes __builtin_frame_address(). // See <http://gcc.gnu.org/onlinedocs/gcc/Return-Address.html>. - fn frame_address(n: ctypes::c_uint) -> ctypes::uintptr_t; + fn frame_address(n: libc::c_uint) -> libc::uintptr_t; } #[doc = " @@ -64,7 +64,7 @@ fn align_of<T>() -> uint unsafe { #[doc = "Returns the refcount of a shared box"] fn refcount<T>(t: @T) -> uint { - ret rustrt::refcount::<T>(t); + ret rustrt::refcount::<T>(t) as uint; } fn log_str<T>(t: T) -> str { @@ -80,7 +80,7 @@ supervised by the scheduler then any user-specified exit status is ignored and the process exits with the default failure status "] fn set_exit_status(code: int) { - rustrt::rust_set_exit_status(code as ctypes::intptr_t); + rustrt::rust_set_exit_status(code as libc::intptr_t); } #[cfg(test)] diff --git a/src/libcore/task.rs b/src/libcore/task.rs index 322b43e223d..580636043ed 100644 --- a/src/libcore/task.rs +++ b/src/libcore/task.rs @@ -465,8 +465,8 @@ type task_id = int; // These are both opaque runtime/compiler types that we don't know the // structure of and should only deal with via unsafe pointer -type rust_task = ctypes::void; -type rust_closure = ctypes::void; +type rust_task = libc::c_void; +type rust_closure = libc::c_void; fn spawn_raw(opts: task_opts, +f: fn~()) unsafe { @@ -537,7 +537,7 @@ native mod rusti { native mod rustrt { fn rust_get_sched_id() -> sched_id; - fn rust_new_sched(num_threads: ctypes::uintptr_t) -> sched_id; + fn rust_new_sched(num_threads: libc::uintptr_t) -> sched_id; fn get_task_id() -> task_id; fn rust_get_task() -> *rust_task; @@ -749,12 +749,12 @@ fn test_spawn_sched_childs_on_same_sched() { #[nolink] #[cfg(test)] native mod testrt { - fn rust_dbg_lock_create() -> *ctypes::void; - fn rust_dbg_lock_destroy(lock: *ctypes::void); - fn rust_dbg_lock_lock(lock: *ctypes::void); - fn rust_dbg_lock_unlock(lock: *ctypes::void); - fn rust_dbg_lock_wait(lock: *ctypes::void); - fn rust_dbg_lock_signal(lock: *ctypes::void); + fn rust_dbg_lock_create() -> *libc::c_void; + fn rust_dbg_lock_destroy(lock: *libc::c_void); + fn rust_dbg_lock_lock(lock: *libc::c_void); + fn rust_dbg_lock_unlock(lock: *libc::c_void); + fn rust_dbg_lock_wait(lock: *libc::c_void); + fn rust_dbg_lock_signal(lock: *libc::c_void); } #[test] diff --git a/src/libcore/vec.rs b/src/libcore/vec.rs index 2b3829dd910..cf2d208955b 100644 --- a/src/libcore/vec.rs +++ b/src/libcore/vec.rs @@ -73,17 +73,17 @@ export u8; #[abi = "rust-intrinsic"] native mod rusti { - fn vec_len<T>(&&v: [const T]) -> ctypes::size_t; + fn vec_len<T>(&&v: [const T]) -> libc::size_t; } #[abi = "cdecl"] native mod rustrt { fn vec_reserve_shared<T>(t: *sys::type_desc, &v: [const T], - n: ctypes::size_t); + n: libc::size_t); fn vec_from_buf_shared<T>(t: *sys::type_desc, ptr: *T, - count: ctypes::size_t) -> [T]; + count: libc::size_t) -> [T]; } #[doc = "A function used to initialize the elements of a vector"] @@ -912,7 +912,7 @@ mod u8 { pure fn cmp(&&a: [u8], &&b: [u8]) -> int unsafe { let a_len = len(a); let b_len = len(b); - let n = uint::min(a_len, b_len) as ctypes::size_t; + let n = uint::min(a_len, b_len) as libc::size_t; let r = libc::memcmp(unsafe::to_ptr(a) as *libc::c_void, unsafe::to_ptr(b) as *libc::c_void, n) as int; |
