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