Skip to content

Commit a21932f

Browse files
RenTrieuphip1611
andcommitted
uefi: Changing get_envs() to return an Iterator
Co-authored-by: Philipp Schuster <[email protected]>
1 parent 5a27fb8 commit a21932f

File tree

2 files changed

+150
-76
lines changed

2 files changed

+150
-76
lines changed

uefi-test-runner/src/proto/shell.rs

Lines changed: 54 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -2,30 +2,23 @@
22

33
use uefi::boot::ScopedProtocol;
44
use uefi::proto::shell::Shell;
5-
use uefi::{CStr16, boot};
5+
use uefi::{boot, cstr16};
66
use uefi_raw::Status;
77

8-
/// Test ``get_env()``, ``get_envs()``, and ``set_env()``
8+
/// Test `get_env()`, `get_envs()`, and `set_env()`
99
pub fn test_env(shell: &ScopedProtocol<Shell>) {
10-
let mut test_buf = [0u16; 128];
11-
1210
/* Test retrieving list of environment variable names */
11+
let mut cur_env_vec = shell.get_envs();
12+
assert_eq!(cur_env_vec.next().unwrap(), cstr16!("path"),);
13+
// check pre-defined shell variables; see UEFI Shell spec
14+
assert_eq!(cur_env_vec.next().unwrap(), cstr16!("nonesting"),);
1315
let cur_env_vec = shell.get_envs();
14-
assert_eq!(
15-
*cur_env_vec.first().unwrap(),
16-
CStr16::from_str_with_buf("path", &mut test_buf).unwrap()
17-
);
18-
assert_eq!(
19-
*cur_env_vec.get(1).unwrap(),
20-
CStr16::from_str_with_buf("nonesting", &mut test_buf).unwrap()
21-
);
22-
let default_len = cur_env_vec.len();
16+
let default_len = cur_env_vec.count();
2317

2418
/* Test setting and getting a specific environment variable */
25-
let mut test_env_buf = [0u16; 32];
26-
let test_var = CStr16::from_str_with_buf("test_var", &mut test_env_buf).unwrap();
27-
let mut test_val_buf = [0u16; 32];
28-
let test_val = CStr16::from_str_with_buf("test_val", &mut test_val_buf).unwrap();
19+
let cur_env_vec = shell.get_envs();
20+
let test_var = cstr16!("test_var");
21+
let test_val = cstr16!("test_val");
2922
assert!(shell.get_env(test_var).is_none());
3023
let status = shell.set_env(test_var, test_val, false);
3124
assert_eq!(status, Status::SUCCESS);
@@ -34,64 +27,81 @@ pub fn test_env(shell: &ScopedProtocol<Shell>) {
3427
.expect("Could not get environment variable");
3528
assert_eq!(cur_env_str, test_val);
3629

37-
assert!(!cur_env_vec.contains(&test_var));
30+
let mut found_var = false;
31+
for env_var in cur_env_vec {
32+
if env_var == test_var {
33+
found_var = true;
34+
}
35+
}
36+
assert!(!found_var);
37+
let cur_env_vec = shell.get_envs();
38+
let mut found_var = false;
39+
for env_var in cur_env_vec {
40+
if env_var == test_var {
41+
found_var = true;
42+
}
43+
}
44+
assert!(found_var);
45+
3846
let cur_env_vec = shell.get_envs();
39-
assert!(cur_env_vec.contains(&test_var));
40-
assert_eq!(cur_env_vec.len(), default_len + 1);
47+
assert_eq!(cur_env_vec.count(), default_len + 1);
4148

4249
/* Test deleting environment variable */
43-
let test_val = CStr16::from_str_with_buf("", &mut test_val_buf).unwrap();
50+
let test_val = cstr16!("");
4451
let status = shell.set_env(test_var, test_val, false);
4552
assert_eq!(status, Status::SUCCESS);
4653
assert!(shell.get_env(test_var).is_none());
4754

4855
let cur_env_vec = shell.get_envs();
49-
assert!(!cur_env_vec.contains(&test_var));
50-
assert_eq!(cur_env_vec.len(), default_len);
56+
let mut found_var = false;
57+
for env_var in cur_env_vec {
58+
if env_var == test_var {
59+
found_var = true;
60+
}
61+
}
62+
assert!(!found_var);
63+
let cur_env_vec = shell.get_envs();
64+
assert_eq!(cur_env_vec.count(), default_len);
5165
}
5266

53-
/// Test ``get_cur_dir()`` and ``set_cur_dir()``
67+
/// Test `get_cur_dir()` and `set_cur_dir()`
5468
pub fn test_cur_dir(shell: &ScopedProtocol<Shell>) {
55-
let mut test_buf = [0u16; 128];
56-
5769
/* Test setting and getting current file system and current directory */
58-
let mut fs_buf = [0u16; 16];
59-
let fs_var = CStr16::from_str_with_buf("fs0:", &mut fs_buf).unwrap();
60-
let mut dir_buf = [0u16; 32];
61-
let dir_var = CStr16::from_str_with_buf("/", &mut dir_buf).unwrap();
70+
let fs_var = cstr16!("fs0:");
71+
let dir_var = cstr16!("/");
6272
let status = shell.set_cur_dir(Some(fs_var), Some(dir_var));
6373
assert_eq!(status, Status::SUCCESS);
6474

6575
let cur_fs_str = shell
6676
.get_cur_dir(Some(fs_var))
6777
.expect("Could not get the current file system mapping");
68-
let expected_fs_str = CStr16::from_str_with_buf("FS0:\\", &mut test_buf).unwrap();
78+
let expected_fs_str = cstr16!("FS0:\\");
6979
assert_eq!(cur_fs_str, expected_fs_str);
7080

7181
// Changing current file system
72-
let fs_var = CStr16::from_str_with_buf("fs1:", &mut fs_buf).unwrap();
73-
let dir_var = CStr16::from_str_with_buf("/", &mut dir_buf).unwrap();
82+
let fs_var = cstr16!("fs1:");
83+
let dir_var = cstr16!("/");
7484
let status = shell.set_cur_dir(Some(fs_var), Some(dir_var));
7585
assert_eq!(status, Status::SUCCESS);
7686

7787
let cur_fs_str = shell
7888
.get_cur_dir(Some(fs_var))
7989
.expect("Could not get the current file system mapping");
8090
assert_ne!(cur_fs_str, expected_fs_str);
81-
let expected_fs_str = CStr16::from_str_with_buf("FS1:\\", &mut test_buf).unwrap();
91+
let expected_fs_str = cstr16!("FS1:\\");
8292
assert_eq!(cur_fs_str, expected_fs_str);
8393

8494
// Changing current file system and current directory
85-
let fs_var = CStr16::from_str_with_buf("fs0:", &mut fs_buf).unwrap();
86-
let dir_var = CStr16::from_str_with_buf("efi/", &mut dir_buf).unwrap();
95+
let fs_var = cstr16!("fs0:");
96+
let dir_var = cstr16!("efi/");
8797
let status = shell.set_cur_dir(Some(fs_var), Some(dir_var));
8898
assert_eq!(status, Status::SUCCESS);
8999

90100
let cur_fs_str = shell
91101
.get_cur_dir(Some(fs_var))
92102
.expect("Could not get the current file system mapping");
93103
assert_ne!(cur_fs_str, expected_fs_str);
94-
let expected_fs_str = CStr16::from_str_with_buf("FS0:\\efi", &mut test_buf).unwrap();
104+
let expected_fs_str = cstr16!("FS0:\\efi");
95105
assert_eq!(cur_fs_str, expected_fs_str);
96106

97107
/* Test current working directory cases */
@@ -101,13 +111,13 @@ pub fn test_cur_dir(shell: &ScopedProtocol<Shell>) {
101111
assert!(shell.get_cur_dir(None).is_none());
102112

103113
// Setting the current working file system and current working directory
104-
let dir_var = CStr16::from_str_with_buf("fs0:/", &mut dir_buf).unwrap();
114+
let dir_var = cstr16!("fs0:/");
105115
let status = shell.set_cur_dir(None, Some(dir_var));
106116
assert_eq!(status, Status::SUCCESS);
107117
let cur_fs_str = shell
108118
.get_cur_dir(Some(fs_var))
109119
.expect("Could not get the current file system mapping");
110-
let expected_fs_str = CStr16::from_str_with_buf("FS0:", &mut test_buf).unwrap();
120+
let expected_fs_str = cstr16!("FS0:");
111121
assert_eq!(cur_fs_str, expected_fs_str);
112122

113123
let cur_fs_str = shell
@@ -116,30 +126,30 @@ pub fn test_cur_dir(shell: &ScopedProtocol<Shell>) {
116126
assert_eq!(cur_fs_str, expected_fs_str);
117127

118128
// Changing current working directory
119-
let dir_var = CStr16::from_str_with_buf("/efi", &mut dir_buf).unwrap();
129+
let dir_var = cstr16!("/efi");
120130
let status = shell.set_cur_dir(None, Some(dir_var));
121131
assert_eq!(status, Status::SUCCESS);
122132
let cur_fs_str = shell
123133
.get_cur_dir(Some(fs_var))
124134
.expect("Could not get the current file system mapping");
125-
let expected_fs_str = CStr16::from_str_with_buf("FS0:\\efi", &mut test_buf).unwrap();
135+
let expected_fs_str = cstr16!("FS0:\\efi");
126136
assert_eq!(cur_fs_str, expected_fs_str);
127137
let cur_fs_str = shell
128138
.get_cur_dir(None)
129139
.expect("Could not get the current file system mapping");
130140
assert_eq!(cur_fs_str, expected_fs_str);
131141

132142
// Changing current directory in a non-current working file system
133-
let fs_var = CStr16::from_str_with_buf("fs0:", &mut fs_buf).unwrap();
134-
let dir_var = CStr16::from_str_with_buf("efi/tools", &mut dir_buf).unwrap();
143+
let fs_var = cstr16!("fs0:");
144+
let dir_var = cstr16!("efi/tools");
135145
let status = shell.set_cur_dir(Some(fs_var), Some(dir_var));
136146
assert_eq!(status, Status::SUCCESS);
137147
let cur_fs_str = shell
138148
.get_cur_dir(None)
139149
.expect("Could not get the current file system mapping");
140150
assert_ne!(cur_fs_str, expected_fs_str);
141151

142-
let expected_fs_str = CStr16::from_str_with_buf("FS0:\\efi\\tools", &mut test_buf).unwrap();
152+
let expected_fs_str = cstr16!("FS0:\\efi\\tools");
143153
let cur_fs_str = shell
144154
.get_cur_dir(Some(fs_var))
145155
.expect("Could not get the current file system mapping");

uefi/src/proto/shell/mod.rs

Lines changed: 96 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,10 @@
22

33
//! EFI Shell Protocol v2.2
44
5-
#![cfg(feature = "alloc")]
6-
7-
use alloc::vec::Vec;
85
use uefi_macros::unsafe_protocol;
96
use uefi_raw::Status;
107

8+
use core::marker::PhantomData;
119
use core::ptr;
1210

1311
use uefi_raw::protocol::shell::ShellProtocol;
@@ -20,6 +18,35 @@ use crate::{CStr16, Char16};
2018
#[unsafe_protocol(ShellProtocol::GUID)]
2119
pub struct Shell(ShellProtocol);
2220

21+
/// Iterator over the names of environmental variables obtained from the Shell protocol.
22+
#[derive(Debug)]
23+
pub struct Vars<'a> {
24+
/// Char16 containing names of environment variables
25+
inner: *const Char16,
26+
/// Placeholder to attach a lifetime to `Vars`
27+
placeholder: PhantomData<&'a CStr16>,
28+
}
29+
30+
impl<'a> Iterator for Vars<'a> {
31+
type Item = &'a CStr16;
32+
// We iterate a list of NUL terminated CStr16s.
33+
// The list is terminated with a double NUL.
34+
fn next(&mut self) -> Option<Self::Item> {
35+
let cur_start = self.inner;
36+
let mut cur_len = 0;
37+
unsafe {
38+
if *(cur_start) == Char16::from_u16_unchecked(0) {
39+
return None;
40+
}
41+
while *(cur_start.add(cur_len)) != Char16::from_u16_unchecked(0) {
42+
cur_len += 1;
43+
}
44+
self.inner = self.inner.add(cur_len + 1);
45+
Some(CStr16::from_ptr(cur_start))
46+
}
47+
}
48+
}
49+
2350
impl Shell {
2451
/// Gets the value of the specified environment variable
2552
///
@@ -50,36 +77,12 @@ impl Shell {
5077
///
5178
/// * `Vec<env_names>` - Vector of environment variable names
5279
#[must_use]
53-
pub fn get_envs(&self) -> Vec<&CStr16> {
54-
let mut env_vec: Vec<&CStr16> = Vec::new();
55-
let cur_env_ptr = unsafe { (self.0.get_env)(ptr::null()) };
56-
57-
let mut cur_start = cur_env_ptr;
58-
let mut cur_len = 0;
59-
60-
let mut i = 0;
61-
let mut null_count = 0;
62-
unsafe {
63-
while null_count <= 1 {
64-
if (*(cur_env_ptr.add(i))) == Char16::from_u16_unchecked(0).into() {
65-
if cur_len > 0 {
66-
env_vec.push(CStr16::from_char16_with_nul(
67-
&(*ptr::slice_from_raw_parts(cur_start.cast(), cur_len + 1)),
68-
).unwrap());
69-
}
70-
cur_len = 0;
71-
null_count += 1;
72-
} else {
73-
if null_count > 0 {
74-
cur_start = cur_env_ptr.add(i);
75-
}
76-
null_count = 0;
77-
cur_len += 1;
78-
}
79-
i += 1;
80-
}
80+
pub fn get_envs(&self) -> Vars {
81+
let env_ptr = unsafe { (self.0.get_env)(ptr::null()) };
82+
Vars {
83+
inner: env_ptr.cast::<Char16>(),
84+
placeholder: PhantomData,
8185
}
82-
env_vec
8386
}
8487

8588
/// Sets the environment variable
@@ -143,3 +146,64 @@ impl Shell {
143146
unsafe { (self.0.set_cur_dir)(fs_ptr.cast(), dir_ptr.cast()) }
144147
}
145148
}
149+
150+
#[cfg(test)]
151+
mod tests {
152+
use super::*;
153+
use alloc::vec::Vec;
154+
use uefi::cstr16;
155+
156+
/// Testing Vars struct
157+
#[test]
158+
fn test_vars() {
159+
// Empty Vars
160+
let mut vars_mock = Vec::<u16>::new();
161+
vars_mock.push(0);
162+
vars_mock.push(0);
163+
let mut vars = Vars {
164+
inner: vars_mock.as_ptr().cast(),
165+
placeholder: PhantomData,
166+
};
167+
assert!(vars.next().is_none());
168+
169+
// One environment variable in Vars
170+
let mut vars_mock = Vec::<u16>::new();
171+
vars_mock.push(b'f' as u16);
172+
vars_mock.push(b'o' as u16);
173+
vars_mock.push(b'o' as u16);
174+
vars_mock.push(0);
175+
vars_mock.push(0);
176+
let vars = Vars {
177+
inner: vars_mock.as_ptr().cast(),
178+
placeholder: PhantomData,
179+
};
180+
assert_eq!(vars.collect::<Vec<_>>(), Vec::from([cstr16!("foo")]));
181+
182+
// Multiple environment variables in Vars
183+
let mut vars_mock = Vec::<u16>::new();
184+
vars_mock.push(b'f' as u16);
185+
vars_mock.push(b'o' as u16);
186+
vars_mock.push(b'o' as u16);
187+
vars_mock.push(b'1' as u16);
188+
vars_mock.push(0);
189+
vars_mock.push(b'b' as u16);
190+
vars_mock.push(b'a' as u16);
191+
vars_mock.push(b'r' as u16);
192+
vars_mock.push(0);
193+
vars_mock.push(b'b' as u16);
194+
vars_mock.push(b'a' as u16);
195+
vars_mock.push(b'z' as u16);
196+
vars_mock.push(b'2' as u16);
197+
vars_mock.push(0);
198+
vars_mock.push(0);
199+
200+
let vars = Vars {
201+
inner: vars_mock.as_ptr().cast(),
202+
placeholder: PhantomData,
203+
};
204+
assert_eq!(
205+
vars.collect::<Vec<_>>(),
206+
Vec::from([cstr16!("foo1"), cstr16!("bar"), cstr16!("baz2")])
207+
);
208+
}
209+
}

0 commit comments

Comments
 (0)