44
55use crate :: proto:: unsafe_protocol;
66use crate :: { CStr16 , Char16 , Error , Result , Status , StatusExt } ;
7+
8+ use core:: marker:: PhantomData ;
79use core:: ptr;
810use uefi_raw:: protocol:: shell:: ShellProtocol ;
9- use alloc:: vec:: Vec ;
1011
1112/// Shell Protocol
1213#[ derive( Debug ) ]
1314#[ repr( transparent) ]
1415#[ unsafe_protocol( ShellProtocol :: GUID ) ]
1516pub struct Shell ( ShellProtocol ) ;
1617
18+ /// Trait for implementing the var function
19+ pub trait ShellVarProvider {
20+ /// Gets the value of the specified environment variable
21+ fn var ( & self , name : & CStr16 ) -> Option < & CStr16 > ;
22+ }
23+
24+ /// Iterator over the names of environmental variables obtained from the Shell protocol.
25+ #[ derive( Debug ) ]
26+ pub struct Vars < ' a , T : ShellVarProvider > {
27+ /// Char16 containing names of environment variables
28+ names : * const Char16 ,
29+ /// Reference to Shell Protocol
30+ protocol : * const T ,
31+ /// Marker to attach a lifetime to `Vars`
32+ _marker : PhantomData < & ' a CStr16 > ,
33+ }
34+
35+ impl < ' a , T : ShellVarProvider + ' a > Iterator for Vars < ' a , T > {
36+ type Item = ( & ' a CStr16 , Option < & ' a CStr16 > ) ;
37+ // We iterate a list of NUL terminated CStr16s.
38+ // The list is terminated with a double NUL.
39+ fn next ( & mut self ) -> Option < Self :: Item > {
40+ let s = unsafe { CStr16 :: from_ptr ( self . names ) } ;
41+ if s. is_empty ( ) {
42+ None
43+ } else {
44+ self . names = unsafe { self . names . add ( s. num_chars ( ) + 1 ) } ;
45+ Some ( ( s, unsafe { self . protocol . as_ref ( ) . unwrap ( ) . var ( s) } ) )
46+ }
47+ }
48+ }
49+
50+ impl ShellVarProvider for Shell {
51+ /// Gets the value of the specified environment variable
52+ fn var ( & self , name : & CStr16 ) -> Option < & CStr16 > {
53+ self . var ( name)
54+ }
55+ }
56+
1757impl Shell {
1858 /// Returns the current directory on the specified device.
1959 ///
@@ -56,64 +96,42 @@ impl Shell {
5696 unsafe { ( self . 0 . set_cur_dir ) ( fs_ptr. cast ( ) , dir_ptr. cast ( ) ) } . to_result ( )
5797 }
5898
59- /// Gets the environment variable or list of environment variables
99+ /// Gets the value of the specified environment variable
60100 ///
61101 /// # Arguments
62102 ///
63103 /// * `name` - The environment variable name of which to retrieve the
64- /// value
65- /// If None, will return all defined shell environment
66- /// variables
104+ /// value.
67105 ///
68106 /// # Returns
69107 ///
70- /// * `Some(Vec <env_value>)` - Value of the environment variable
71- /// * `Some(Vec<env_names>)` - Vector of environment variable names
72- /// * `None` - Environment variable doesn't exist
108+ /// * `Some(<env_value>)` - &CStr16 containing the value of the
109+ /// environment variable
110+ /// * `None` - If environment variable does not exist
73111 #[ must_use]
74- pub fn get_env < ' a > ( & ' a self , name : Option < & CStr16 > ) -> Option < Vec < & ' a CStr16 > > {
75- let mut env_vec = Vec :: new ( ) ;
76- match name {
77- Some ( n) => {
78- let name_ptr: * const Char16 = core:: ptr:: from_ref :: < CStr16 > ( n) . cast ( ) ;
79- let var_val = unsafe { ( self . 0 . get_env ) ( name_ptr. cast ( ) ) } ;
80- if var_val. is_null ( ) {
81- return None ;
82- } else {
83- unsafe { env_vec. push ( CStr16 :: from_ptr ( var_val. cast ( ) ) ) } ;
84- }
85- }
86- None => {
87- let cur_env_ptr = unsafe { ( self . 0 . get_env ) ( ptr:: null ( ) ) } ;
88-
89- let mut cur_start = cur_env_ptr;
90- let mut cur_len = 0 ;
91-
92- let mut i = 0 ;
93- let mut null_count = 0 ;
94- unsafe {
95- while null_count <= 1 {
96- if ( * ( cur_env_ptr. add ( i) ) ) == Char16 :: from_u16_unchecked ( 0 ) . into ( ) {
97- if cur_len > 0 {
98- env_vec. push ( CStr16 :: from_char16_with_nul_unchecked (
99- & ( * ptr:: slice_from_raw_parts ( cur_start. cast ( ) , cur_len + 1 ) ) ,
100- ) ) ;
101- }
102- cur_len = 0 ;
103- null_count += 1 ;
104- } else {
105- if null_count > 0 {
106- cur_start = cur_env_ptr. add ( i) ;
107- }
108- null_count = 0 ;
109- cur_len += 1 ;
110- }
111- i += 1 ;
112- }
113- }
114- }
112+ pub fn var ( & self , name : & CStr16 ) -> Option < & CStr16 > {
113+ let name_ptr: * const Char16 = name. as_ptr ( ) ;
114+ let var_val = unsafe { ( self . 0 . get_env ) ( name_ptr. cast ( ) ) } ;
115+ if var_val. is_null ( ) {
116+ None
117+ } else {
118+ unsafe { Some ( CStr16 :: from_ptr ( var_val. cast ( ) ) ) }
119+ }
120+ }
121+
122+ /// Gets an iterator over the names of all environment variables
123+ ///
124+ /// # Returns
125+ ///
126+ /// * `Vars` - Iterator over the names of the environment variables
127+ #[ must_use]
128+ pub fn vars ( & self ) -> Vars < ' _ , Self > {
129+ let env_ptr = unsafe { ( self . 0 . get_env ) ( ptr:: null ( ) ) } ;
130+ Vars {
131+ names : env_ptr. cast :: < Char16 > ( ) ,
132+ protocol : self ,
133+ _marker : PhantomData ,
115134 }
116- Some ( env_vec)
117135 }
118136
119137 /// Sets the environment variable
@@ -122,15 +140,114 @@ impl Shell {
122140 ///
123141 /// * `name` - The environment variable for which to set the value
124142 /// * `value` - The new value of the environment variable
125- /// * `volatile` - Indicates whether or not the variable is volatile or
143+ /// * `volatile` - Indicates whether the variable is volatile or
126144 /// not
127145 ///
128146 /// # Returns
129147 ///
130- /// * `Status::SUCCESS` The variable was successfully set
131- pub fn set_env ( & self , name : & CStr16 , value : & CStr16 , volatile : bool ) -> Status {
132- let name_ptr: * const Char16 = core:: ptr:: from_ref :: < CStr16 > ( name) . cast ( ) ;
133- let value_ptr: * const Char16 = core:: ptr:: from_ref :: < CStr16 > ( value) . cast ( ) ;
134- unsafe { ( self . 0 . set_env ) ( name_ptr. cast ( ) , value_ptr. cast ( ) , volatile) }
148+ /// * `Status::SUCCESS` - The variable was successfully set
149+ pub fn set_var ( & self , name : & CStr16 , value : & CStr16 , volatile : bool ) -> Result {
150+ let name_ptr: * const Char16 = name. as_ptr ( ) ;
151+ let value_ptr: * const Char16 = value. as_ptr ( ) ;
152+ unsafe { ( self . 0 . set_env ) ( name_ptr. cast ( ) , value_ptr. cast ( ) , volatile) } . to_result ( )
153+ }
154+ }
155+
156+ #[ cfg( test) ]
157+ mod tests {
158+ use super :: * ;
159+ use alloc:: collections:: BTreeMap ;
160+ use alloc:: vec:: Vec ;
161+ use uefi:: cstr16;
162+
163+ struct ShellMock < ' a > {
164+ inner : BTreeMap < & ' a CStr16 , & ' a CStr16 > ,
165+ }
166+
167+ impl < ' a > ShellMock < ' a > {
168+ fn new ( names : Vec < & ' a CStr16 > , values : Vec < & ' a CStr16 > ) -> ShellMock < ' a > {
169+ let mut inner_map = BTreeMap :: new ( ) ;
170+ for ( name, val) in names. iter ( ) . zip ( values. iter ( ) ) {
171+ inner_map. insert ( * name, * val) ;
172+ }
173+ ShellMock { inner : inner_map }
174+ }
175+ }
176+ impl < ' a > ShellVarProvider for ShellMock < ' a > {
177+ fn var ( & self , name : & CStr16 ) -> Option < & CStr16 > {
178+ if let Some ( val) = self . inner . get ( name) {
179+ Some ( * val)
180+ } else {
181+ None
182+ }
183+ }
184+ }
185+
186+ /// Testing Vars struct
187+ #[ test]
188+ fn test_vars ( ) {
189+ // Empty Vars
190+ let mut vars_mock = Vec :: < u16 > :: new ( ) ;
191+ vars_mock. push ( 0 ) ;
192+ vars_mock. push ( 0 ) ;
193+ let mut vars = Vars {
194+ names : vars_mock. as_ptr ( ) . cast ( ) ,
195+ protocol : & ShellMock :: new ( Vec :: new ( ) , Vec :: new ( ) ) ,
196+ _marker : PhantomData ,
197+ } ;
198+
199+ assert ! ( vars. next( ) . is_none( ) ) ;
200+
201+ // One environment variable in Vars
202+ let mut vars_mock = Vec :: < u16 > :: new ( ) ;
203+ vars_mock. push ( b'f' as u16 ) ;
204+ vars_mock. push ( b'o' as u16 ) ;
205+ vars_mock. push ( b'o' as u16 ) ;
206+ vars_mock. push ( 0 ) ;
207+ vars_mock. push ( 0 ) ;
208+ let vars = Vars {
209+ names : vars_mock. as_ptr ( ) . cast ( ) ,
210+ protocol : & ShellMock :: new ( Vec :: from ( [ cstr16 ! ( "foo" ) ] ) , Vec :: from ( [ cstr16 ! ( "value" ) ] ) ) ,
211+ _marker : PhantomData ,
212+ } ;
213+ assert_eq ! (
214+ vars. collect:: <Vec <_>>( ) ,
215+ Vec :: from( [ ( cstr16!( "foo" ) , Some ( cstr16!( "value" ) ) ) ] )
216+ ) ;
217+
218+ // Multiple environment variables in Vars
219+ let mut vars_mock = Vec :: < u16 > :: new ( ) ;
220+ vars_mock. push ( b'f' as u16 ) ;
221+ vars_mock. push ( b'o' as u16 ) ;
222+ vars_mock. push ( b'o' as u16 ) ;
223+ vars_mock. push ( b'1' as u16 ) ;
224+ vars_mock. push ( 0 ) ;
225+ vars_mock. push ( b'b' as u16 ) ;
226+ vars_mock. push ( b'a' as u16 ) ;
227+ vars_mock. push ( b'r' as u16 ) ;
228+ vars_mock. push ( 0 ) ;
229+ vars_mock. push ( b'b' as u16 ) ;
230+ vars_mock. push ( b'a' as u16 ) ;
231+ vars_mock. push ( b'z' as u16 ) ;
232+ vars_mock. push ( b'2' as u16 ) ;
233+ vars_mock. push ( 0 ) ;
234+ vars_mock. push ( 0 ) ;
235+
236+ let vars = Vars {
237+ names : vars_mock. as_ptr ( ) . cast ( ) ,
238+ protocol : & ShellMock :: new (
239+ Vec :: from ( [ cstr16 ! ( "foo1" ) , cstr16 ! ( "bar" ) , cstr16 ! ( "baz2" ) ] ) ,
240+ Vec :: from ( [ cstr16 ! ( "value" ) , cstr16 ! ( "one" ) , cstr16 ! ( "two" ) ] ) ,
241+ ) ,
242+ _marker : PhantomData ,
243+ } ;
244+ assert_eq ! (
245+ vars. collect:: <Vec <_>>( ) ,
246+ Vec :: from( [
247+ ( cstr16!( "foo1" ) , Some ( cstr16!( "value" ) ) ) ,
248+ ( cstr16!( "bar" ) , Some ( cstr16!( "one" ) ) ) ,
249+ ( cstr16!( "baz2" ) , Some ( cstr16!( "two" ) ) )
250+ ] )
251+ ) ;
135252 }
136253}
0 commit comments