1 | //! Convenient and efficient string argument passing. |
2 | //! |
3 | //! This module defines the `Arg` trait and implements it for several common |
4 | //! string types. This allows users to pass any of these string types directly |
5 | //! to rustix APIs with string arguments, and it allows rustix to implement |
6 | //! NUL-termination without the need for copying or dynamic allocation where |
7 | //! possible. |
8 | |
9 | use crate::ffi::CStr; |
10 | use crate::io; |
11 | #[cfg (feature = "itoa" )] |
12 | use crate::path::DecInt; |
13 | use crate::path::SMALL_PATH_BUFFER_SIZE; |
14 | #[cfg (all(feature = "alloc" , feature = "itoa" ))] |
15 | use alloc::borrow::ToOwned; |
16 | use core::mem::MaybeUninit; |
17 | use core::{ptr, slice, str}; |
18 | #[cfg (feature = "std" )] |
19 | use std::ffi::{OsStr, OsString}; |
20 | #[cfg (all(feature = "std" , target_os = "hermit" ))] |
21 | use std::os::hermit::ext::ffi::{OsStrExt, OsStringExt}; |
22 | #[cfg (all(feature = "std" , unix))] |
23 | use std::os::unix::ffi::{OsStrExt, OsStringExt}; |
24 | #[cfg (all(feature = "std" , target_os = "vxworks" ))] |
25 | use std::os::vxworks::ext::ffi::{OsStrExt, OsStringExt}; |
26 | #[cfg (all(feature = "std" , target_os = "wasi" ))] |
27 | use std::os::wasi::ffi::{OsStrExt, OsStringExt}; |
28 | #[cfg (feature = "std" )] |
29 | use std::path::{Component, Components, Iter, Path, PathBuf}; |
30 | #[cfg (feature = "alloc" )] |
31 | use {crate::ffi::CString, alloc::borrow::Cow}; |
32 | #[cfg (feature = "alloc" )] |
33 | use {alloc::string::String, alloc::vec::Vec}; |
34 | |
35 | /// A trait for passing path arguments. |
36 | /// |
37 | /// This is similar to [`AsRef`]`<`[`Path`]`>`, but is implemented for more |
38 | /// kinds of strings and can convert into more kinds of strings. |
39 | /// |
40 | /// # Examples |
41 | /// |
42 | /// ``` |
43 | /// # #[cfg (any(feature = "fs" , feature = "net" ))] |
44 | /// use rustix::ffi::CStr; |
45 | /// use rustix::io; |
46 | /// # #[cfg (any(feature = "fs" , feature = "net" ))] |
47 | /// use rustix::path::Arg; |
48 | /// |
49 | /// # #[cfg (any(feature = "fs" , feature = "net" ))] |
50 | /// pub fn touch<P: Arg>(path: P) -> io::Result<()> { |
51 | /// let path = path.into_c_str()?; |
52 | /// _touch(&path) |
53 | /// } |
54 | /// |
55 | /// # #[cfg (any(feature = "fs" , feature = "net" ))] |
56 | /// fn _touch(path: &CStr) -> io::Result<()> { |
57 | /// // implementation goes here |
58 | /// Ok(()) |
59 | /// } |
60 | /// ``` |
61 | /// |
62 | /// Users can then call `touch("foo")`, `touch(cstr!("foo"))`, |
63 | /// `touch(Path::new("foo"))`, or many other things. |
64 | /// |
65 | /// [`AsRef`]: std::convert::AsRef |
66 | pub trait Arg { |
67 | /// Returns a view of this string as a string slice. |
68 | fn as_str(&self) -> io::Result<&str>; |
69 | |
70 | /// Returns a potentially-lossy rendering of this string as a |
71 | /// `Cow<'_, str>`. |
72 | #[cfg (feature = "alloc" )] |
73 | fn to_string_lossy(&self) -> Cow<'_, str>; |
74 | |
75 | /// Returns a view of this string as a maybe-owned [`CStr`]. |
76 | #[cfg (feature = "alloc" )] |
77 | fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>>; |
78 | |
79 | /// Consumes `self` and returns a view of this string as a maybe-owned |
80 | /// [`CStr`]. |
81 | #[cfg (feature = "alloc" )] |
82 | fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> |
83 | where |
84 | Self: 'b; |
85 | |
86 | /// Runs a closure with `self` passed in as a `&CStr`. |
87 | fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> |
88 | where |
89 | Self: Sized, |
90 | F: FnOnce(&CStr) -> io::Result<T>; |
91 | } |
92 | |
93 | /// Runs a closure on `arg` where `A` is mapped to a `&CStr` |
94 | pub fn option_into_with_c_str<T, F, A>(arg: Option<A>, f: F) -> io::Result<T> |
95 | where |
96 | A: Arg + Sized, |
97 | F: FnOnce(Option<&CStr>) -> io::Result<T>, |
98 | { |
99 | if let Some(arg: A) = arg { |
100 | arg.into_with_c_str(|p: &CStr| f(Some(p))) |
101 | } else { |
102 | f(None) |
103 | } |
104 | } |
105 | |
106 | impl Arg for &str { |
107 | #[inline ] |
108 | fn as_str(&self) -> io::Result<&str> { |
109 | Ok(self) |
110 | } |
111 | |
112 | #[cfg (feature = "alloc" )] |
113 | #[inline ] |
114 | fn to_string_lossy(&self) -> Cow<'_, str> { |
115 | Cow::Borrowed(self) |
116 | } |
117 | |
118 | #[cfg (feature = "alloc" )] |
119 | #[inline ] |
120 | fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> { |
121 | Ok(Cow::Owned( |
122 | CString::new(*self).map_err(|_cstr_err| io::Errno::INVAL)?, |
123 | )) |
124 | } |
125 | |
126 | #[cfg (feature = "alloc" )] |
127 | #[inline ] |
128 | fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> |
129 | where |
130 | Self: 'b, |
131 | { |
132 | Ok(Cow::Owned( |
133 | CString::new(self).map_err(|_cstr_err| io::Errno::INVAL)?, |
134 | )) |
135 | } |
136 | |
137 | #[inline ] |
138 | fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> |
139 | where |
140 | Self: Sized, |
141 | F: FnOnce(&CStr) -> io::Result<T>, |
142 | { |
143 | with_c_str(self.as_bytes(), f) |
144 | } |
145 | } |
146 | |
147 | #[cfg (feature = "alloc" )] |
148 | impl Arg for &String { |
149 | #[inline ] |
150 | fn as_str(&self) -> io::Result<&str> { |
151 | Ok(self) |
152 | } |
153 | |
154 | #[cfg (feature = "alloc" )] |
155 | #[inline ] |
156 | fn to_string_lossy(&self) -> Cow<'_, str> { |
157 | Cow::Borrowed(self) |
158 | } |
159 | |
160 | #[cfg (feature = "alloc" )] |
161 | #[inline ] |
162 | fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> { |
163 | Ok(Cow::Owned( |
164 | CString::new(String::as_str(self)).map_err(|_cstr_err| io::Errno::INVAL)?, |
165 | )) |
166 | } |
167 | |
168 | #[cfg (feature = "alloc" )] |
169 | #[inline ] |
170 | fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> |
171 | where |
172 | Self: 'b, |
173 | { |
174 | self.as_str().into_c_str() |
175 | } |
176 | |
177 | #[inline ] |
178 | fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> |
179 | where |
180 | Self: Sized, |
181 | F: FnOnce(&CStr) -> io::Result<T>, |
182 | { |
183 | with_c_str(self.as_bytes(), f) |
184 | } |
185 | } |
186 | |
187 | #[cfg (feature = "alloc" )] |
188 | impl Arg for String { |
189 | #[inline ] |
190 | fn as_str(&self) -> io::Result<&str> { |
191 | Ok(self) |
192 | } |
193 | |
194 | #[cfg (feature = "alloc" )] |
195 | #[inline ] |
196 | fn to_string_lossy(&self) -> Cow<'_, str> { |
197 | Cow::Borrowed(self) |
198 | } |
199 | |
200 | #[cfg (feature = "alloc" )] |
201 | #[inline ] |
202 | fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> { |
203 | Ok(Cow::Owned( |
204 | CString::new(self.as_str()).map_err(|_cstr_err| io::Errno::INVAL)?, |
205 | )) |
206 | } |
207 | |
208 | #[cfg (feature = "alloc" )] |
209 | #[inline ] |
210 | fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> |
211 | where |
212 | Self: 'b, |
213 | { |
214 | Ok(Cow::Owned( |
215 | CString::new(self).map_err(|_cstr_err| io::Errno::INVAL)?, |
216 | )) |
217 | } |
218 | |
219 | #[inline ] |
220 | fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> |
221 | where |
222 | Self: Sized, |
223 | F: FnOnce(&CStr) -> io::Result<T>, |
224 | { |
225 | f(&CString::new(self).map_err(|_cstr_err| io::Errno::INVAL)?) |
226 | } |
227 | } |
228 | |
229 | #[cfg (feature = "std" )] |
230 | impl Arg for &OsStr { |
231 | #[inline ] |
232 | fn as_str(&self) -> io::Result<&str> { |
233 | self.to_str().ok_or(io::Errno::INVAL) |
234 | } |
235 | |
236 | #[cfg (feature = "alloc" )] |
237 | #[inline ] |
238 | fn to_string_lossy(&self) -> Cow<'_, str> { |
239 | OsStr::to_string_lossy(self) |
240 | } |
241 | |
242 | #[cfg (feature = "alloc" )] |
243 | #[inline ] |
244 | fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> { |
245 | Ok(Cow::Owned( |
246 | CString::new(self.as_bytes()).map_err(|_cstr_err| io::Errno::INVAL)?, |
247 | )) |
248 | } |
249 | |
250 | #[cfg (feature = "alloc" )] |
251 | #[inline ] |
252 | fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> |
253 | where |
254 | Self: 'b, |
255 | { |
256 | Ok(Cow::Owned( |
257 | CString::new(self.as_bytes()).map_err(|_cstr_err| io::Errno::INVAL)?, |
258 | )) |
259 | } |
260 | |
261 | #[inline ] |
262 | fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> |
263 | where |
264 | Self: Sized, |
265 | F: FnOnce(&CStr) -> io::Result<T>, |
266 | { |
267 | with_c_str(self.as_bytes(), f) |
268 | } |
269 | } |
270 | |
271 | #[cfg (feature = "std" )] |
272 | impl Arg for &OsString { |
273 | #[inline ] |
274 | fn as_str(&self) -> io::Result<&str> { |
275 | OsString::as_os_str(self).to_str().ok_or(io::Errno::INVAL) |
276 | } |
277 | |
278 | #[cfg (feature = "alloc" )] |
279 | #[inline ] |
280 | fn to_string_lossy(&self) -> Cow<'_, str> { |
281 | self.as_os_str().to_string_lossy() |
282 | } |
283 | |
284 | #[cfg (feature = "alloc" )] |
285 | #[inline ] |
286 | fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> { |
287 | Ok(Cow::Owned( |
288 | CString::new(OsString::as_os_str(self).as_bytes()) |
289 | .map_err(|_cstr_err| io::Errno::INVAL)?, |
290 | )) |
291 | } |
292 | |
293 | #[cfg (feature = "alloc" )] |
294 | #[inline ] |
295 | fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> |
296 | where |
297 | Self: 'b, |
298 | { |
299 | self.as_os_str().into_c_str() |
300 | } |
301 | |
302 | #[inline ] |
303 | fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> |
304 | where |
305 | Self: Sized, |
306 | F: FnOnce(&CStr) -> io::Result<T>, |
307 | { |
308 | with_c_str(self.as_bytes(), f) |
309 | } |
310 | } |
311 | |
312 | #[cfg (feature = "std" )] |
313 | impl Arg for OsString { |
314 | #[inline ] |
315 | fn as_str(&self) -> io::Result<&str> { |
316 | self.as_os_str().to_str().ok_or(io::Errno::INVAL) |
317 | } |
318 | |
319 | #[cfg (feature = "alloc" )] |
320 | #[inline ] |
321 | fn to_string_lossy(&self) -> Cow<'_, str> { |
322 | self.as_os_str().to_string_lossy() |
323 | } |
324 | |
325 | #[cfg (feature = "alloc" )] |
326 | #[inline ] |
327 | fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> { |
328 | Ok(Cow::Owned( |
329 | CString::new(self.as_bytes()).map_err(|_cstr_err| io::Errno::INVAL)?, |
330 | )) |
331 | } |
332 | |
333 | #[cfg (feature = "alloc" )] |
334 | #[inline ] |
335 | fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> |
336 | where |
337 | Self: 'b, |
338 | { |
339 | Ok(Cow::Owned( |
340 | CString::new(self.into_vec()).map_err(|_cstr_err| io::Errno::INVAL)?, |
341 | )) |
342 | } |
343 | |
344 | #[inline ] |
345 | fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> |
346 | where |
347 | Self: Sized, |
348 | F: FnOnce(&CStr) -> io::Result<T>, |
349 | { |
350 | f(&CString::new(self.into_vec()).map_err(|_cstr_err| io::Errno::INVAL)?) |
351 | } |
352 | } |
353 | |
354 | #[cfg (feature = "std" )] |
355 | impl Arg for &Path { |
356 | #[inline ] |
357 | fn as_str(&self) -> io::Result<&str> { |
358 | self.as_os_str().to_str().ok_or(io::Errno::INVAL) |
359 | } |
360 | |
361 | #[cfg (feature = "alloc" )] |
362 | #[inline ] |
363 | fn to_string_lossy(&self) -> Cow<'_, str> { |
364 | Path::to_string_lossy(self) |
365 | } |
366 | |
367 | #[cfg (feature = "alloc" )] |
368 | #[inline ] |
369 | fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> { |
370 | Ok(Cow::Owned( |
371 | CString::new(self.as_os_str().as_bytes()).map_err(|_cstr_err| io::Errno::INVAL)?, |
372 | )) |
373 | } |
374 | |
375 | #[cfg (feature = "alloc" )] |
376 | #[inline ] |
377 | fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> |
378 | where |
379 | Self: 'b, |
380 | { |
381 | Ok(Cow::Owned( |
382 | CString::new(self.as_os_str().as_bytes()).map_err(|_cstr_err| io::Errno::INVAL)?, |
383 | )) |
384 | } |
385 | |
386 | #[inline ] |
387 | fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> |
388 | where |
389 | Self: Sized, |
390 | F: FnOnce(&CStr) -> io::Result<T>, |
391 | { |
392 | with_c_str(self.as_os_str().as_bytes(), f) |
393 | } |
394 | } |
395 | |
396 | #[cfg (feature = "std" )] |
397 | impl Arg for &PathBuf { |
398 | #[inline ] |
399 | fn as_str(&self) -> io::Result<&str> { |
400 | PathBuf::as_path(self) |
401 | .as_os_str() |
402 | .to_str() |
403 | .ok_or(io::Errno::INVAL) |
404 | } |
405 | |
406 | #[cfg (feature = "alloc" )] |
407 | #[inline ] |
408 | fn to_string_lossy(&self) -> Cow<'_, str> { |
409 | self.as_path().to_string_lossy() |
410 | } |
411 | |
412 | #[cfg (feature = "alloc" )] |
413 | #[inline ] |
414 | fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> { |
415 | Ok(Cow::Owned( |
416 | CString::new(PathBuf::as_path(self).as_os_str().as_bytes()) |
417 | .map_err(|_cstr_err| io::Errno::INVAL)?, |
418 | )) |
419 | } |
420 | |
421 | #[cfg (feature = "alloc" )] |
422 | #[inline ] |
423 | fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> |
424 | where |
425 | Self: 'b, |
426 | { |
427 | self.as_path().into_c_str() |
428 | } |
429 | |
430 | #[inline ] |
431 | fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> |
432 | where |
433 | Self: Sized, |
434 | F: FnOnce(&CStr) -> io::Result<T>, |
435 | { |
436 | with_c_str(self.as_os_str().as_bytes(), f) |
437 | } |
438 | } |
439 | |
440 | #[cfg (feature = "std" )] |
441 | impl Arg for PathBuf { |
442 | #[inline ] |
443 | fn as_str(&self) -> io::Result<&str> { |
444 | self.as_os_str().to_str().ok_or(io::Errno::INVAL) |
445 | } |
446 | |
447 | #[cfg (feature = "alloc" )] |
448 | #[inline ] |
449 | fn to_string_lossy(&self) -> Cow<'_, str> { |
450 | self.as_os_str().to_string_lossy() |
451 | } |
452 | |
453 | #[cfg (feature = "alloc" )] |
454 | #[inline ] |
455 | fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> { |
456 | Ok(Cow::Owned( |
457 | CString::new(self.as_os_str().as_bytes()).map_err(|_cstr_err| io::Errno::INVAL)?, |
458 | )) |
459 | } |
460 | |
461 | #[cfg (feature = "alloc" )] |
462 | #[inline ] |
463 | fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> |
464 | where |
465 | Self: 'b, |
466 | { |
467 | Ok(Cow::Owned( |
468 | CString::new(self.into_os_string().into_vec()).map_err(|_cstr_err| io::Errno::INVAL)?, |
469 | )) |
470 | } |
471 | |
472 | #[inline ] |
473 | fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> |
474 | where |
475 | Self: Sized, |
476 | F: FnOnce(&CStr) -> io::Result<T>, |
477 | { |
478 | f( |
479 | &CString::new(self.into_os_string().into_vec()) |
480 | .map_err(|_cstr_err| io::Errno::INVAL)?, |
481 | ) |
482 | } |
483 | } |
484 | |
485 | impl Arg for &CStr { |
486 | #[inline ] |
487 | fn as_str(&self) -> io::Result<&str> { |
488 | self.to_str().map_err(|_utf8_err| io::Errno::INVAL) |
489 | } |
490 | |
491 | #[cfg (feature = "alloc" )] |
492 | #[inline ] |
493 | fn to_string_lossy(&self) -> Cow<'_, str> { |
494 | CStr::to_string_lossy(self) |
495 | } |
496 | |
497 | #[cfg (feature = "alloc" )] |
498 | #[inline ] |
499 | fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> { |
500 | Ok(Cow::Borrowed(self)) |
501 | } |
502 | |
503 | #[cfg (feature = "alloc" )] |
504 | #[inline ] |
505 | fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> |
506 | where |
507 | Self: 'b, |
508 | { |
509 | Ok(Cow::Borrowed(self)) |
510 | } |
511 | |
512 | #[inline ] |
513 | fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> |
514 | where |
515 | Self: Sized, |
516 | F: FnOnce(&CStr) -> io::Result<T>, |
517 | { |
518 | f(self) |
519 | } |
520 | } |
521 | |
522 | #[cfg (feature = "alloc" )] |
523 | impl Arg for &CString { |
524 | #[inline ] |
525 | fn as_str(&self) -> io::Result<&str> { |
526 | unimplemented!() |
527 | } |
528 | |
529 | #[cfg (feature = "alloc" )] |
530 | #[inline ] |
531 | fn to_string_lossy(&self) -> Cow<'_, str> { |
532 | unimplemented!() |
533 | } |
534 | |
535 | #[cfg (feature = "alloc" )] |
536 | #[inline ] |
537 | fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> { |
538 | Ok(Cow::Borrowed(self)) |
539 | } |
540 | |
541 | #[cfg (feature = "alloc" )] |
542 | #[inline ] |
543 | fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> |
544 | where |
545 | Self: 'b, |
546 | { |
547 | Ok(Cow::Borrowed(self)) |
548 | } |
549 | |
550 | #[inline ] |
551 | fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> |
552 | where |
553 | Self: Sized, |
554 | F: FnOnce(&CStr) -> io::Result<T>, |
555 | { |
556 | f(self) |
557 | } |
558 | } |
559 | |
560 | #[cfg (feature = "alloc" )] |
561 | impl Arg for CString { |
562 | #[inline ] |
563 | fn as_str(&self) -> io::Result<&str> { |
564 | self.to_str().map_err(|_utf8_err| io::Errno::INVAL) |
565 | } |
566 | |
567 | #[cfg (feature = "alloc" )] |
568 | #[inline ] |
569 | fn to_string_lossy(&self) -> Cow<'_, str> { |
570 | CStr::to_string_lossy(self) |
571 | } |
572 | |
573 | #[cfg (feature = "alloc" )] |
574 | #[inline ] |
575 | fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> { |
576 | Ok(Cow::Borrowed(self)) |
577 | } |
578 | |
579 | #[cfg (feature = "alloc" )] |
580 | #[inline ] |
581 | fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> |
582 | where |
583 | Self: 'b, |
584 | { |
585 | Ok(Cow::Owned(self)) |
586 | } |
587 | |
588 | #[inline ] |
589 | fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> |
590 | where |
591 | Self: Sized, |
592 | F: FnOnce(&CStr) -> io::Result<T>, |
593 | { |
594 | f(&self) |
595 | } |
596 | } |
597 | |
598 | #[cfg (feature = "alloc" )] |
599 | impl<'a> Arg for Cow<'a, str> { |
600 | #[inline ] |
601 | fn as_str(&self) -> io::Result<&str> { |
602 | Ok(self) |
603 | } |
604 | |
605 | #[cfg (feature = "alloc" )] |
606 | #[inline ] |
607 | fn to_string_lossy(&self) -> Cow<'_, str> { |
608 | Cow::Borrowed(self) |
609 | } |
610 | |
611 | #[cfg (feature = "alloc" )] |
612 | #[inline ] |
613 | fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> { |
614 | Ok(Cow::Owned( |
615 | CString::new(self.as_ref()).map_err(|_cstr_err| io::Errno::INVAL)?, |
616 | )) |
617 | } |
618 | |
619 | #[cfg (feature = "alloc" )] |
620 | #[inline ] |
621 | fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> |
622 | where |
623 | Self: 'b, |
624 | { |
625 | Ok(Cow::Owned( |
626 | match self { |
627 | Cow::Owned(s) => CString::new(s), |
628 | Cow::Borrowed(s) => CString::new(s), |
629 | } |
630 | .map_err(|_cstr_err| io::Errno::INVAL)?, |
631 | )) |
632 | } |
633 | |
634 | #[inline ] |
635 | fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> |
636 | where |
637 | Self: Sized, |
638 | F: FnOnce(&CStr) -> io::Result<T>, |
639 | { |
640 | with_c_str(self.as_bytes(), f) |
641 | } |
642 | } |
643 | |
644 | #[cfg (feature = "std" )] |
645 | #[cfg (feature = "alloc" )] |
646 | impl<'a> Arg for Cow<'a, OsStr> { |
647 | #[inline ] |
648 | fn as_str(&self) -> io::Result<&str> { |
649 | (**self).to_str().ok_or(io::Errno::INVAL) |
650 | } |
651 | |
652 | #[cfg (feature = "alloc" )] |
653 | #[inline ] |
654 | fn to_string_lossy(&self) -> Cow<'_, str> { |
655 | (**self).to_string_lossy() |
656 | } |
657 | |
658 | #[cfg (feature = "alloc" )] |
659 | #[inline ] |
660 | fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> { |
661 | Ok(Cow::Owned( |
662 | CString::new(self.as_bytes()).map_err(|_cstr_err| io::Errno::INVAL)?, |
663 | )) |
664 | } |
665 | |
666 | #[cfg (feature = "alloc" )] |
667 | #[inline ] |
668 | fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> |
669 | where |
670 | Self: 'b, |
671 | { |
672 | Ok(Cow::Owned( |
673 | match self { |
674 | Cow::Owned(os) => CString::new(os.into_vec()), |
675 | Cow::Borrowed(os) => CString::new(os.as_bytes()), |
676 | } |
677 | .map_err(|_cstr_err| io::Errno::INVAL)?, |
678 | )) |
679 | } |
680 | |
681 | #[inline ] |
682 | fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> |
683 | where |
684 | Self: Sized, |
685 | F: FnOnce(&CStr) -> io::Result<T>, |
686 | { |
687 | with_c_str(self.as_bytes(), f) |
688 | } |
689 | } |
690 | |
691 | #[cfg (feature = "alloc" )] |
692 | impl<'a> Arg for Cow<'a, CStr> { |
693 | #[inline ] |
694 | fn as_str(&self) -> io::Result<&str> { |
695 | self.to_str().map_err(|_utf8_err| io::Errno::INVAL) |
696 | } |
697 | |
698 | #[cfg (feature = "alloc" )] |
699 | #[inline ] |
700 | fn to_string_lossy(&self) -> Cow<'_, str> { |
701 | let borrow: &CStr = core::borrow::Borrow::borrow(self); |
702 | borrow.to_string_lossy() |
703 | } |
704 | |
705 | #[cfg (feature = "alloc" )] |
706 | #[inline ] |
707 | fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> { |
708 | Ok(Cow::Borrowed(self)) |
709 | } |
710 | |
711 | #[cfg (feature = "alloc" )] |
712 | #[inline ] |
713 | fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> |
714 | where |
715 | Self: 'b, |
716 | { |
717 | Ok(self) |
718 | } |
719 | |
720 | #[inline ] |
721 | fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> |
722 | where |
723 | Self: Sized, |
724 | F: FnOnce(&CStr) -> io::Result<T>, |
725 | { |
726 | f(&self) |
727 | } |
728 | } |
729 | |
730 | #[cfg (feature = "std" )] |
731 | impl<'a> Arg for Component<'a> { |
732 | #[inline ] |
733 | fn as_str(&self) -> io::Result<&str> { |
734 | self.as_os_str().to_str().ok_or(io::Errno::INVAL) |
735 | } |
736 | |
737 | #[cfg (feature = "alloc" )] |
738 | #[inline ] |
739 | fn to_string_lossy(&self) -> Cow<'_, str> { |
740 | self.as_os_str().to_string_lossy() |
741 | } |
742 | |
743 | #[cfg (feature = "alloc" )] |
744 | #[inline ] |
745 | fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> { |
746 | Ok(Cow::Owned( |
747 | CString::new(self.as_os_str().as_bytes()).map_err(|_cstr_err| io::Errno::INVAL)?, |
748 | )) |
749 | } |
750 | |
751 | #[cfg (feature = "alloc" )] |
752 | #[inline ] |
753 | fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> |
754 | where |
755 | Self: 'b, |
756 | { |
757 | Ok(Cow::Owned( |
758 | CString::new(self.as_os_str().as_bytes()).map_err(|_cstr_err| io::Errno::INVAL)?, |
759 | )) |
760 | } |
761 | |
762 | #[inline ] |
763 | fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> |
764 | where |
765 | Self: Sized, |
766 | F: FnOnce(&CStr) -> io::Result<T>, |
767 | { |
768 | with_c_str(self.as_os_str().as_bytes(), f) |
769 | } |
770 | } |
771 | |
772 | #[cfg (feature = "std" )] |
773 | impl<'a> Arg for Components<'a> { |
774 | #[inline ] |
775 | fn as_str(&self) -> io::Result<&str> { |
776 | self.as_path().to_str().ok_or(io::Errno::INVAL) |
777 | } |
778 | |
779 | #[cfg (feature = "alloc" )] |
780 | #[inline ] |
781 | fn to_string_lossy(&self) -> Cow<'_, str> { |
782 | self.as_path().to_string_lossy() |
783 | } |
784 | |
785 | #[cfg (feature = "alloc" )] |
786 | #[inline ] |
787 | fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> { |
788 | Ok(Cow::Owned( |
789 | CString::new(self.as_path().as_os_str().as_bytes()) |
790 | .map_err(|_cstr_err| io::Errno::INVAL)?, |
791 | )) |
792 | } |
793 | |
794 | #[cfg (feature = "alloc" )] |
795 | #[inline ] |
796 | fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> |
797 | where |
798 | Self: 'b, |
799 | { |
800 | Ok(Cow::Owned( |
801 | CString::new(self.as_path().as_os_str().as_bytes()) |
802 | .map_err(|_cstr_err| io::Errno::INVAL)?, |
803 | )) |
804 | } |
805 | |
806 | #[inline ] |
807 | fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> |
808 | where |
809 | Self: Sized, |
810 | F: FnOnce(&CStr) -> io::Result<T>, |
811 | { |
812 | with_c_str(self.as_path().as_os_str().as_bytes(), f) |
813 | } |
814 | } |
815 | |
816 | #[cfg (feature = "std" )] |
817 | impl<'a> Arg for Iter<'a> { |
818 | #[inline ] |
819 | fn as_str(&self) -> io::Result<&str> { |
820 | self.as_path().to_str().ok_or(io::Errno::INVAL) |
821 | } |
822 | |
823 | #[cfg (feature = "alloc" )] |
824 | #[inline ] |
825 | fn to_string_lossy(&self) -> Cow<'_, str> { |
826 | self.as_path().to_string_lossy() |
827 | } |
828 | |
829 | #[cfg (feature = "alloc" )] |
830 | #[inline ] |
831 | fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> { |
832 | Ok(Cow::Owned( |
833 | CString::new(self.as_path().as_os_str().as_bytes()) |
834 | .map_err(|_cstr_err| io::Errno::INVAL)?, |
835 | )) |
836 | } |
837 | |
838 | #[cfg (feature = "alloc" )] |
839 | #[inline ] |
840 | fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> |
841 | where |
842 | Self: 'b, |
843 | { |
844 | Ok(Cow::Owned( |
845 | CString::new(self.as_path().as_os_str().as_bytes()) |
846 | .map_err(|_cstr_err| io::Errno::INVAL)?, |
847 | )) |
848 | } |
849 | |
850 | #[inline ] |
851 | fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> |
852 | where |
853 | Self: Sized, |
854 | F: FnOnce(&CStr) -> io::Result<T>, |
855 | { |
856 | with_c_str(self.as_path().as_os_str().as_bytes(), f) |
857 | } |
858 | } |
859 | |
860 | impl Arg for &[u8] { |
861 | #[inline ] |
862 | fn as_str(&self) -> io::Result<&str> { |
863 | str::from_utf8(self).map_err(|_utf8_err| io::Errno::INVAL) |
864 | } |
865 | |
866 | #[cfg (feature = "alloc" )] |
867 | #[inline ] |
868 | fn to_string_lossy(&self) -> Cow<'_, str> { |
869 | String::from_utf8_lossy(self) |
870 | } |
871 | |
872 | #[cfg (feature = "alloc" )] |
873 | #[inline ] |
874 | fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> { |
875 | Ok(Cow::Owned( |
876 | CString::new(*self).map_err(|_cstr_err| io::Errno::INVAL)?, |
877 | )) |
878 | } |
879 | |
880 | #[cfg (feature = "alloc" )] |
881 | #[inline ] |
882 | fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> |
883 | where |
884 | Self: 'b, |
885 | { |
886 | Ok(Cow::Owned( |
887 | CString::new(self).map_err(|_cstr_err| io::Errno::INVAL)?, |
888 | )) |
889 | } |
890 | |
891 | #[inline ] |
892 | fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> |
893 | where |
894 | Self: Sized, |
895 | F: FnOnce(&CStr) -> io::Result<T>, |
896 | { |
897 | with_c_str(self, f) |
898 | } |
899 | } |
900 | |
901 | #[cfg (feature = "alloc" )] |
902 | impl Arg for &Vec<u8> { |
903 | #[inline ] |
904 | fn as_str(&self) -> io::Result<&str> { |
905 | str::from_utf8(self).map_err(|_utf8_err| io::Errno::INVAL) |
906 | } |
907 | |
908 | #[cfg (feature = "alloc" )] |
909 | #[inline ] |
910 | fn to_string_lossy(&self) -> Cow<'_, str> { |
911 | String::from_utf8_lossy(self) |
912 | } |
913 | |
914 | #[cfg (feature = "alloc" )] |
915 | #[inline ] |
916 | fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> { |
917 | Ok(Cow::Owned( |
918 | CString::new(self.as_slice()).map_err(|_cstr_err| io::Errno::INVAL)?, |
919 | )) |
920 | } |
921 | |
922 | #[cfg (feature = "alloc" )] |
923 | #[inline ] |
924 | fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> |
925 | where |
926 | Self: 'b, |
927 | { |
928 | Ok(Cow::Owned( |
929 | CString::new(self.as_slice()).map_err(|_cstr_err| io::Errno::INVAL)?, |
930 | )) |
931 | } |
932 | |
933 | #[inline ] |
934 | fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> |
935 | where |
936 | Self: Sized, |
937 | F: FnOnce(&CStr) -> io::Result<T>, |
938 | { |
939 | with_c_str(self, f) |
940 | } |
941 | } |
942 | |
943 | #[cfg (feature = "alloc" )] |
944 | impl Arg for Vec<u8> { |
945 | #[inline ] |
946 | fn as_str(&self) -> io::Result<&str> { |
947 | str::from_utf8(self).map_err(|_utf8_err| io::Errno::INVAL) |
948 | } |
949 | |
950 | #[cfg (feature = "alloc" )] |
951 | #[inline ] |
952 | fn to_string_lossy(&self) -> Cow<'_, str> { |
953 | String::from_utf8_lossy(self) |
954 | } |
955 | |
956 | #[cfg (feature = "alloc" )] |
957 | #[inline ] |
958 | fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> { |
959 | Ok(Cow::Owned( |
960 | CString::new(self.as_slice()).map_err(|_cstr_err| io::Errno::INVAL)?, |
961 | )) |
962 | } |
963 | |
964 | #[cfg (feature = "alloc" )] |
965 | #[inline ] |
966 | fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> |
967 | where |
968 | Self: 'b, |
969 | { |
970 | Ok(Cow::Owned( |
971 | CString::new(self).map_err(|_cstr_err| io::Errno::INVAL)?, |
972 | )) |
973 | } |
974 | |
975 | #[inline ] |
976 | fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> |
977 | where |
978 | Self: Sized, |
979 | F: FnOnce(&CStr) -> io::Result<T>, |
980 | { |
981 | f(&CString::new(self).map_err(|_cstr_err| io::Errno::INVAL)?) |
982 | } |
983 | } |
984 | |
985 | #[cfg (feature = "itoa" )] |
986 | impl Arg for DecInt { |
987 | #[inline ] |
988 | fn as_str(&self) -> io::Result<&str> { |
989 | Ok(self.as_str()) |
990 | } |
991 | |
992 | #[cfg (feature = "alloc" )] |
993 | #[inline ] |
994 | fn to_string_lossy(&self) -> Cow<'_, str> { |
995 | Cow::Borrowed(self.as_str()) |
996 | } |
997 | |
998 | #[cfg (feature = "alloc" )] |
999 | #[inline ] |
1000 | fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> { |
1001 | Ok(Cow::Borrowed(self.as_c_str())) |
1002 | } |
1003 | |
1004 | #[cfg (feature = "alloc" )] |
1005 | #[inline ] |
1006 | fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> |
1007 | where |
1008 | Self: 'b, |
1009 | { |
1010 | Ok(Cow::Owned(self.as_c_str().to_owned())) |
1011 | } |
1012 | |
1013 | #[inline ] |
1014 | fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> |
1015 | where |
1016 | Self: Sized, |
1017 | F: FnOnce(&CStr) -> io::Result<T>, |
1018 | { |
1019 | f(self.as_c_str()) |
1020 | } |
1021 | } |
1022 | |
1023 | /// Runs a closure with `bytes` passed in as a `&CStr`. |
1024 | #[allow (unsafe_code, clippy::int_plus_one)] |
1025 | #[inline ] |
1026 | fn with_c_str<T, F>(bytes: &[u8], f: F) -> io::Result<T> |
1027 | where |
1028 | F: FnOnce(&CStr) -> io::Result<T>, |
1029 | { |
1030 | // Most paths are less than `SMALL_PATH_BUFFER_SIZE` long. The rest can go |
1031 | // through the dynamic allocation path. If you're opening many files in a |
1032 | // directory with a long path, consider opening the directory and using |
1033 | // `openat` to open the files under it, which will avoid this, and is often |
1034 | // faster in the OS as well. |
1035 | |
1036 | // Test with `>=` so that we have room for the trailing NUL. |
1037 | if bytes.len() >= SMALL_PATH_BUFFER_SIZE { |
1038 | return with_c_str_slow_path(bytes, f); |
1039 | } |
1040 | |
1041 | // Taken from |
1042 | // <https://github.com/rust-lang/rust/blob/a00f8ba7fcac1b27341679c51bf5a3271fa82df3/library/std/src/sys/common/small_c_string.rs> |
1043 | let mut buf = MaybeUninit::<[u8; SMALL_PATH_BUFFER_SIZE]>::uninit(); |
1044 | let buf_ptr = buf.as_mut_ptr().cast::<u8>(); |
1045 | |
1046 | // This helps test our safety condition below. |
1047 | debug_assert!(bytes.len() + 1 <= SMALL_PATH_BUFFER_SIZE); |
1048 | |
1049 | // SAFETY: `bytes.len() < SMALL_PATH_BUFFER_SIZE` which means we have space |
1050 | // for `bytes.len() + 1` u8s: |
1051 | unsafe { |
1052 | ptr::copy_nonoverlapping(bytes.as_ptr(), buf_ptr, bytes.len()); |
1053 | buf_ptr.add(bytes.len()).write(0); |
1054 | } |
1055 | |
1056 | // SAFETY: We just wrote the bytes above and they will remain valid for the |
1057 | // duration of `f` b/c buf doesn't get dropped until the end of the |
1058 | // function. |
1059 | match CStr::from_bytes_with_nul(unsafe { slice::from_raw_parts(buf_ptr, bytes.len() + 1) }) { |
1060 | Ok(s) => f(s), |
1061 | Err(_) => Err(io::Errno::INVAL), |
1062 | } |
1063 | } |
1064 | |
1065 | /// The slow path which handles any length. In theory OS's only support up to |
1066 | /// `PATH_MAX`, but we let the OS enforce that. |
1067 | #[allow (unsafe_code, clippy::int_plus_one)] |
1068 | #[cold ] |
1069 | fn with_c_str_slow_path<T, F>(bytes: &[u8], f: F) -> io::Result<T> |
1070 | where |
1071 | F: FnOnce(&CStr) -> io::Result<T>, |
1072 | { |
1073 | #[cfg (feature = "alloc" )] |
1074 | { |
1075 | f(&CString::new(bytes).map_err(|_cstr_err| io::Errno::INVAL)?) |
1076 | } |
1077 | |
1078 | #[cfg (not(feature = "alloc" ))] |
1079 | { |
1080 | #[cfg (all(libc, not(any(target_os = "hurd" , target_os = "wasi" ))))] |
1081 | const LARGE_PATH_BUFFER_SIZE: usize = libc::PATH_MAX as usize; |
1082 | #[cfg (linux_raw)] |
1083 | const LARGE_PATH_BUFFER_SIZE: usize = linux_raw_sys::general::PATH_MAX as usize; |
1084 | #[cfg (any(target_os = "hurd" , target_os = "wasi" ))] |
1085 | const LARGE_PATH_BUFFER_SIZE: usize = 4096 as usize; // TODO: upstream this |
1086 | |
1087 | // Taken from |
1088 | // <https://github.com/rust-lang/rust/blob/a00f8ba7fcac1b27341679c51bf5a3271fa82df3/library/std/src/sys/common/small_c_string.rs> |
1089 | let mut buf = MaybeUninit::<[u8; LARGE_PATH_BUFFER_SIZE]>::uninit(); |
1090 | let buf_ptr = buf.as_mut_ptr().cast::<u8>(); |
1091 | |
1092 | // This helps test our safety condition below. |
1093 | if bytes.len() + 1 > LARGE_PATH_BUFFER_SIZE { |
1094 | return Err(io::Errno::NAMETOOLONG); |
1095 | } |
1096 | |
1097 | // SAFETY: `bytes.len() < LARGE_PATH_BUFFER_SIZE` which means we have |
1098 | // space for `bytes.len() + 1` u8s: |
1099 | unsafe { |
1100 | ptr::copy_nonoverlapping(bytes.as_ptr(), buf_ptr, bytes.len()); |
1101 | buf_ptr.add(bytes.len()).write(0); |
1102 | } |
1103 | |
1104 | // SAFETY: We just wrote the bytes above and they will remain valid for |
1105 | // the duration of `f` b/c buf doesn't get dropped until the end of the |
1106 | // function. |
1107 | match CStr::from_bytes_with_nul(unsafe { slice::from_raw_parts(buf_ptr, bytes.len() + 1) }) |
1108 | { |
1109 | Ok(s) => f(s), |
1110 | Err(_) => Err(io::Errno::INVAL), |
1111 | } |
1112 | } |
1113 | } |
1114 | |