1use crate::ffi::{CStr, CString, c_char};
2use crate::ops::Index;
3use crate::{fmt, mem, ptr};
45/// Helper type to manage ownership of the strings within a C-style array.
6///
7/// This type manages an array of C-string pointers terminated by a null
8/// pointer. The pointer to the array (as returned by `as_ptr`) can be used as
9/// a value of `argv` or `environ`.
10pub struct CStringArray {
11 ptrs: Vec<*const c_char>,
12}
1314impl CStringArray {
15/// Creates a new `CStringArray` with enough capacity to hold `capacity`
16 /// strings.
17pub fn with_capacity(capacity: usize) -> Self {
18let mut result = CStringArray { ptrs: Vec::with_capacity(capacity + 1) };
19result.ptrs.push(ptr::null());
20result21 }
2223/// Replace the string at position `index`.
24pub fn write(&mut self, index: usize, item: CString) {
25let argc = self.ptrs.len() - 1;
26let ptr = &mut self.ptrs[..argc][index];
27let old = mem::replace(ptr, item.into_raw());
28// SAFETY:
29 // `CStringArray` owns all of its strings, and they were all transformed
30 // into pointers using `CString::into_raw`. Also, this is not the null
31 // pointer since the indexing above would have failed.
32drop(unsafe { CString::from_raw(old.cast_mut()) });
33 }
3435/// Push an additional string to the array.
36pub fn push(&mut self, item: CString) {
37let argc = self.ptrs.len() - 1;
38// Amend the array by another null pointer first, to ensure that the
39 // array is null-terminated even when the `push` panics, in which case
40 // the array will be left undisturbed (see #155748).
41self.ptrs.push(ptr::null());
42// Now, replace the previous null pointer.
43self.ptrs[argc] = item.into_raw();
44 }
4546/// Returns a pointer to the C-string array managed by this type.
47pub fn as_ptr(&self) -> *const *const c_char {
48self.ptrs.as_ptr()
49 }
5051/// Returns an iterator over all `CStr`s contained in this array.
52pub fn iter(&self) -> CStringIter<'_> {
53CStringIter { iter: self.ptrs[..self.ptrs.len() - 1].iter() }
54 }
55}
5657impl Index<usize> for CStringArray {
58type Output = CStr;
59fn index(&self, index: usize) -> &CStr {
60let ptr = self.ptrs[..self.ptrs.len() - 1][index];
61// SAFETY:
62 // `CStringArray` owns all of its strings. Also, this is not the null
63 // pointer since the indexing above would have failed.
64unsafe { CStr::from_ptr(ptr) }
65 }
66}
6768impl fmt::Debugfor CStringArray {
69fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
70f.debug_list().entries(self.iter()).finish()
71 }
72}
7374// SAFETY: `CStringArray` is basically just a `Vec<CString>`
75unsafe impl Sendfor CStringArray {}
76// SAFETY: `CStringArray` is basically just a `Vec<CString>`
77unsafe impl Syncfor CStringArray {}
7879impl Dropfor CStringArray {
80fn drop(&mut self) {
81// SAFETY:
82 // `CStringArray` owns all of its strings, and they were all transformed
83 // into pointers using `CString::into_raw`.
84self.ptrs[..self.ptrs.len() - 1]
85 .iter()
86 .for_each(|&p| drop(unsafe { CString::from_raw(p.cast_mut()) }))
87 }
88}
8990/// An iterator over all `CStr`s contained in a `CStringArray`.
91#[derive(#[automatically_derived]
impl<'a> ::core::clone::Clone for CStringIter<'a> {
#[inline]
fn clone(&self) -> CStringIter<'a> {
CStringIter { iter: ::core::clone::Clone::clone(&self.iter) }
}
}Clone)]
92pub struct CStringIter<'a> {
93 iter: crate::slice::Iter<'a, *const c_char>,
94}
9596impl<'a> Iteratorfor CStringIter<'a> {
97type Item = &'a CStr;
98fn next(&mut self) -> Option<&'a CStr> {
99// SAFETY:
100 // `CStringArray` owns all of its strings. Also, this is not the null
101 // pointer since the last element is excluded when creating `iter`.
102self.iter.next().map(|&p| unsafe { CStr::from_ptr(p) })
103 }
104105fn size_hint(&self) -> (usize, Option<usize>) {
106self.iter.size_hint()
107 }
108}
109110impl<'a> ExactSizeIteratorfor CStringIter<'a> {
111fn len(&self) -> usize {
112self.iter.len()
113 }
114fn is_empty(&self) -> bool {
115self.iter.is_empty()
116 }
117}