mirror of
https://github.com/rust-lang/rust.git
synced 2025-04-30 03:57:37 +00:00

This commit is an implementation of [RFC 592][r592] and [RFC 840][r840]. These two RFCs tweak the behavior of `CString` and add a new `CStr` unsized slice type to the module. [r592]: https://github.com/rust-lang/rfcs/blob/master/text/0592-c-str-deref.md [r840]: https://github.com/rust-lang/rfcs/blob/master/text/0840-no-panic-in-c-string.md The new `CStr` type is only constructable via two methods: 1. By `deref`'ing from a `CString` 2. Unsafely via `CStr::from_ptr` The purpose of `CStr` is to be an unsized type which is a thin pointer to a `libc::c_char` (currently it is a fat pointer slice due to implementation limitations). Strings from C can be safely represented with a `CStr` and an appropriate lifetime as well. Consumers of `&CString` should now consume `&CStr` instead to allow producers to pass in C-originating strings instead of just Rust-allocated strings. A new constructor was added to `CString`, `new`, which takes `T: IntoBytes` instead of separate `from_slice` and `from_vec` methods (both have been deprecated in favor of `new`). The `new` method returns a `Result` instead of panicking. The error variant contains the relevant information about where the error happened and bytes (if present). Conversions are provided to the `io::Error` and `old_io::IoError` types via the `FromError` trait which translate to `InvalidInput`. This is a breaking change due to the modification of existing `#[unstable]` APIs and new deprecation, and more detailed information can be found in the two RFCs. Notable breakage includes: * All construction of `CString` now needs to use `new` and handle the outgoing `Result`. * Usage of `CString` as a byte slice now explicitly needs a `.as_bytes()` call. * The `as_slice*` methods have been removed in favor of just having the `as_bytes*` methods. Closes #22469 Closes #22470 [breaking-change]
224 lines
6.9 KiB
Rust
224 lines
6.9 KiB
Rust
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
|
|
// file at the top-level directory of this distribution and at
|
|
// http://rust-lang.org/COPYRIGHT.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
// option. This file may not be copied, modified, or distributed
|
|
// except according to those terms.
|
|
|
|
#![allow(missing_docs)]
|
|
#![allow(non_camel_case_types)]
|
|
#![allow(unused_imports)]
|
|
#![allow(dead_code)]
|
|
#![allow(unused_unsafe)]
|
|
#![allow(unused_mut)]
|
|
|
|
use prelude::v1::*;
|
|
|
|
use ffi::CStr;
|
|
use io::{self, ErrorKind};
|
|
use libc;
|
|
use num::{Int, SignedInt};
|
|
use num;
|
|
use old_io::{self, IoResult, IoError};
|
|
use str;
|
|
use sys_common::mkerr_libc;
|
|
|
|
macro_rules! helper_init { (static $name:ident: Helper<$m:ty>) => (
|
|
static $name: Helper<$m> = Helper {
|
|
lock: ::sync::MUTEX_INIT,
|
|
cond: ::sync::CONDVAR_INIT,
|
|
chan: ::cell::UnsafeCell { value: 0 as *mut Sender<$m> },
|
|
signal: ::cell::UnsafeCell { value: 0 },
|
|
initialized: ::cell::UnsafeCell { value: false },
|
|
shutdown: ::cell::UnsafeCell { value: false },
|
|
};
|
|
) }
|
|
|
|
pub mod backtrace;
|
|
pub mod c;
|
|
pub mod condvar;
|
|
pub mod ext;
|
|
pub mod fd;
|
|
pub mod fs; // support for std::old_io
|
|
pub mod fs2; // support for std::fs
|
|
pub mod helper_signal;
|
|
pub mod mutex;
|
|
pub mod net;
|
|
pub mod os;
|
|
pub mod os_str;
|
|
pub mod pipe;
|
|
pub mod pipe2;
|
|
pub mod process;
|
|
pub mod process2;
|
|
pub mod rwlock;
|
|
pub mod stack_overflow;
|
|
pub mod sync;
|
|
pub mod tcp;
|
|
pub mod thread;
|
|
pub mod thread_local;
|
|
pub mod time;
|
|
pub mod timer;
|
|
pub mod tty;
|
|
pub mod udp;
|
|
|
|
pub mod addrinfo {
|
|
pub use sys_common::net::get_host_addresses;
|
|
pub use sys_common::net::get_address_name;
|
|
}
|
|
|
|
// FIXME: move these to c module
|
|
pub type sock_t = self::fs::fd_t;
|
|
pub type wrlen = libc::size_t;
|
|
pub type msglen_t = libc::size_t;
|
|
pub unsafe fn close_sock(sock: sock_t) { let _ = libc::close(sock); }
|
|
|
|
pub fn last_error() -> IoError {
|
|
decode_error_detailed(os::errno() as i32)
|
|
}
|
|
|
|
pub fn last_net_error() -> IoError {
|
|
last_error()
|
|
}
|
|
|
|
extern "system" {
|
|
fn gai_strerror(errcode: libc::c_int) -> *const libc::c_char;
|
|
}
|
|
|
|
pub fn last_gai_error(s: libc::c_int) -> IoError {
|
|
|
|
let mut err = decode_error(s);
|
|
err.detail = Some(unsafe {
|
|
let data = CStr::from_ptr(gai_strerror(s));
|
|
str::from_utf8(data.to_bytes()).unwrap().to_string()
|
|
});
|
|
err
|
|
}
|
|
|
|
/// Convert an `errno` value into a high-level error variant and description.
|
|
pub fn decode_error(errno: i32) -> IoError {
|
|
// FIXME: this should probably be a bit more descriptive...
|
|
let (kind, desc) = match errno {
|
|
libc::EOF => (old_io::EndOfFile, "end of file"),
|
|
libc::ECONNREFUSED => (old_io::ConnectionRefused, "connection refused"),
|
|
libc::ECONNRESET => (old_io::ConnectionReset, "connection reset"),
|
|
libc::EPERM | libc::EACCES =>
|
|
(old_io::PermissionDenied, "permission denied"),
|
|
libc::EPIPE => (old_io::BrokenPipe, "broken pipe"),
|
|
libc::ENOTCONN => (old_io::NotConnected, "not connected"),
|
|
libc::ECONNABORTED => (old_io::ConnectionAborted, "connection aborted"),
|
|
libc::EADDRNOTAVAIL => (old_io::ConnectionRefused, "address not available"),
|
|
libc::EADDRINUSE => (old_io::ConnectionRefused, "address in use"),
|
|
libc::ENOENT => (old_io::FileNotFound, "no such file or directory"),
|
|
libc::EISDIR => (old_io::InvalidInput, "illegal operation on a directory"),
|
|
libc::ENOSYS => (old_io::IoUnavailable, "function not implemented"),
|
|
libc::EINVAL => (old_io::InvalidInput, "invalid argument"),
|
|
libc::ENOTTY =>
|
|
(old_io::MismatchedFileTypeForOperation,
|
|
"file descriptor is not a TTY"),
|
|
libc::ETIMEDOUT => (old_io::TimedOut, "operation timed out"),
|
|
libc::ECANCELED => (old_io::TimedOut, "operation aborted"),
|
|
libc::consts::os::posix88::EEXIST =>
|
|
(old_io::PathAlreadyExists, "path already exists"),
|
|
|
|
// These two constants can have the same value on some systems,
|
|
// but different values on others, so we can't use a match
|
|
// clause
|
|
x if x == libc::EAGAIN || x == libc::EWOULDBLOCK =>
|
|
(old_io::ResourceUnavailable, "resource temporarily unavailable"),
|
|
|
|
_ => (old_io::OtherIoError, "unknown error")
|
|
};
|
|
IoError { kind: kind, desc: desc, detail: None }
|
|
}
|
|
|
|
pub fn decode_error_detailed(errno: i32) -> IoError {
|
|
let mut err = decode_error(errno);
|
|
err.detail = Some(os::error_string(errno));
|
|
err
|
|
}
|
|
|
|
pub fn decode_error_kind(errno: i32) -> ErrorKind {
|
|
match errno as libc::c_int {
|
|
libc::ECONNREFUSED => ErrorKind::ConnectionRefused,
|
|
libc::ECONNRESET => ErrorKind::ConnectionReset,
|
|
libc::EPERM | libc::EACCES => ErrorKind::PermissionDenied,
|
|
libc::EPIPE => ErrorKind::BrokenPipe,
|
|
libc::ENOTCONN => ErrorKind::NotConnected,
|
|
libc::ECONNABORTED => ErrorKind::ConnectionAborted,
|
|
libc::EADDRNOTAVAIL => ErrorKind::ConnectionRefused,
|
|
libc::EADDRINUSE => ErrorKind::ConnectionRefused,
|
|
libc::ENOENT => ErrorKind::FileNotFound,
|
|
libc::EISDIR => ErrorKind::InvalidInput,
|
|
libc::EINTR => ErrorKind::Interrupted,
|
|
libc::EINVAL => ErrorKind::InvalidInput,
|
|
libc::ENOTTY => ErrorKind::MismatchedFileTypeForOperation,
|
|
libc::ETIMEDOUT => ErrorKind::TimedOut,
|
|
libc::ECANCELED => ErrorKind::TimedOut,
|
|
libc::consts::os::posix88::EEXIST => ErrorKind::PathAlreadyExists,
|
|
|
|
// These two constants can have the same value on some systems,
|
|
// but different values on others, so we can't use a match
|
|
// clause
|
|
x if x == libc::EAGAIN || x == libc::EWOULDBLOCK =>
|
|
ErrorKind::ResourceUnavailable,
|
|
|
|
_ => ErrorKind::Other,
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
pub fn retry<T, F> (mut f: F) -> T where
|
|
T: SignedInt,
|
|
F: FnMut() -> T,
|
|
{
|
|
let one: T = Int::one();
|
|
loop {
|
|
let n = f();
|
|
if n == -one && os::errno() == libc::EINTR as i32 { }
|
|
else { return n }
|
|
}
|
|
}
|
|
|
|
pub fn cvt<T: SignedInt>(t: T) -> io::Result<T> {
|
|
let one: T = Int::one();
|
|
if t == -one {
|
|
Err(io::Error::last_os_error())
|
|
} else {
|
|
Ok(t)
|
|
}
|
|
}
|
|
|
|
pub fn cvt_r<T, F>(mut f: F) -> io::Result<T>
|
|
where T: SignedInt, F: FnMut() -> T
|
|
{
|
|
loop {
|
|
match cvt(f()) {
|
|
Err(ref e) if e.kind() == ErrorKind::Interrupted => {}
|
|
other => return other,
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn ms_to_timeval(ms: u64) -> libc::timeval {
|
|
libc::timeval {
|
|
tv_sec: (ms / 1000) as libc::time_t,
|
|
tv_usec: ((ms % 1000) * 1000) as libc::suseconds_t,
|
|
}
|
|
}
|
|
|
|
pub fn wouldblock() -> bool {
|
|
let err = os::errno();
|
|
err == libc::EWOULDBLOCK as i32 || err == libc::EAGAIN as i32
|
|
}
|
|
|
|
pub fn set_nonblocking(fd: sock_t, nb: bool) -> IoResult<()> {
|
|
let set = nb as libc::c_int;
|
|
mkerr_libc(retry(|| unsafe { c::ioctl(fd, c::FIONBIO, &set) }))
|
|
}
|
|
|
|
// nothing needed on unix platforms
|
|
pub fn init_net() {}
|