@@ -128,6 +128,83 @@ impl<'a> Cmdline<'a> {
128128 anyhow:: anyhow!( "Failed to find kernel argument '{key}'" )
129129 } )
130130 }
131+
132+ /// Add or modify a parameter to the command line
133+ ///
134+ /// Returns `true` if the parameter was added or modified.
135+ ///
136+ /// Returns `false` if the parameter already existed with the same
137+ /// content.
138+ pub fn add_or_modify ( & mut self , param : Parameter ) -> bool {
139+ let mut new_params = Vec :: new ( ) ;
140+ let mut modified = false ;
141+ let mut seen_key = false ;
142+
143+ for p in self . iter ( ) {
144+ if p. key == param. key {
145+ if !seen_key {
146+ // This is the first time we've seen this key.
147+ // We will replace it with the new parameter.
148+ if p != param {
149+ modified = true ;
150+ }
151+ new_params. push ( param. parameter ) ;
152+ } else {
153+ // This is a subsequent parameter with the same key.
154+ // We will remove it, which constitutes a modification.
155+ modified = true ;
156+ }
157+ seen_key = true ;
158+ } else {
159+ new_params. push ( p. parameter ) ;
160+ }
161+ }
162+
163+ if !seen_key {
164+ // The parameter was not found, so we append it.
165+ let self_mut = self . 0 . to_mut ( ) ;
166+ if !self_mut. is_empty ( ) && !self_mut. last ( ) . unwrap ( ) . is_ascii_whitespace ( ) {
167+ self_mut. push ( b' ' ) ;
168+ }
169+ self_mut. extend_from_slice ( param. parameter ) ;
170+ return true ;
171+ }
172+ if modified {
173+ self . 0 = Cow :: Owned ( new_params. join ( b" " . as_slice ( ) ) ) ;
174+ true
175+ } else {
176+ // The parameter already existed with the same content, and there were no duplicates.
177+ false
178+ }
179+ }
180+
181+ /// Remove parameter(s) with the given key from the command line
182+ ///
183+ /// Returns `true` if parameter(s) were removed.
184+ pub fn remove ( & mut self , key : ParameterKey ) -> bool {
185+ let mut removed = false ;
186+ let mut new_params = Vec :: new ( ) ;
187+
188+ for p in self . iter ( ) {
189+ if p. key == key {
190+ removed = true ;
191+ } else {
192+ new_params. push ( p. parameter ) ;
193+ }
194+ }
195+
196+ if removed {
197+ self . 0 = Cow :: Owned ( new_params. join ( b" " . as_slice ( ) ) ) ;
198+ }
199+
200+ removed
201+ }
202+ }
203+
204+ impl < ' a > AsRef < [ u8 ] > for Cmdline < ' a > {
205+ fn as_ref ( & self ) -> & [ u8 ] {
206+ & self . 0
207+ }
131208}
132209
133210/// A single kernel command line parameter key
@@ -144,6 +221,12 @@ impl<'a> std::ops::Deref for ParameterKey<'a> {
144221 }
145222}
146223
224+ impl < ' a , T : AsRef < [ u8 ] > + ?Sized > From < & ' a T > for ParameterKey < ' a > {
225+ fn from ( s : & ' a T ) -> Self {
226+ Self ( s. as_ref ( ) )
227+ }
228+ }
229+
147230impl PartialEq for ParameterKey < ' _ > {
148231 /// Compares two parameter keys for equality.
149232 ///
@@ -171,6 +254,8 @@ impl PartialEq for ParameterKey<'_> {
171254/// A single kernel command line parameter.
172255#[ derive( Debug , Eq ) ]
173256pub struct Parameter < ' a > {
257+ /// The full original value
258+ parameter : & ' a [ u8 ] ,
174259 /// The parameter key as raw bytes
175260 key : ParameterKey < ' a > ,
176261 /// The parameter value as raw bytes, if present
@@ -214,6 +299,7 @@ impl<'a> Parameter<'a> {
214299
215300 let ret = match equals {
216301 None => Self {
302+ parameter : input,
217303 key : ParameterKey ( input) ,
218304 value : None ,
219305 } ,
@@ -232,6 +318,7 @@ impl<'a> Parameter<'a> {
232318 } ;
233319
234320 Self {
321+ parameter : input,
235322 key,
236323 value : Some ( value) ,
237324 }
@@ -537,4 +624,75 @@ mod tests {
537624 assert_eq ! ( rd_args[ 2 ] , param( "rd.foo=a" ) ) ;
538625 assert_eq ! ( rd_args[ 3 ] , param( "rd.qux=c" ) ) ;
539626 }
627+
628+ #[ test]
629+ fn test_add_or_modify ( ) {
630+ let mut kargs = Cmdline :: from ( b"foo=bar" ) ;
631+
632+ // add new
633+ assert ! ( kargs. add_or_modify( param( "baz" ) ) ) ;
634+ let mut iter = kargs. iter ( ) ;
635+ assert_eq ! ( iter. next( ) , Some ( param( "foo=bar" ) ) ) ;
636+ assert_eq ! ( iter. next( ) , Some ( param( "baz" ) ) ) ;
637+ assert_eq ! ( iter. next( ) , None ) ;
638+
639+ // modify existing
640+ assert ! ( kargs. add_or_modify( param( "foo=fuz" ) ) ) ;
641+ iter = kargs. iter ( ) ;
642+ assert_eq ! ( iter. next( ) , Some ( param( "foo=fuz" ) ) ) ;
643+ assert_eq ! ( iter. next( ) , Some ( param( "baz" ) ) ) ;
644+ assert_eq ! ( iter. next( ) , None ) ;
645+
646+ // already exists with same value returns false and doesn't
647+ // modify anything
648+ assert ! ( !kargs. add_or_modify( param( "foo=fuz" ) ) ) ;
649+ iter = kargs. iter ( ) ;
650+ assert_eq ! ( iter. next( ) , Some ( param( "foo=fuz" ) ) ) ;
651+ assert_eq ! ( iter. next( ) , Some ( param( "baz" ) ) ) ;
652+ assert_eq ! ( iter. next( ) , None ) ;
653+ }
654+
655+ #[ test]
656+ fn test_add_or_modify_empty_cmdline ( ) {
657+ let mut kargs = Cmdline :: from ( b"" ) ;
658+ assert ! ( kargs. add_or_modify( param( "foo" ) ) ) ;
659+ assert_eq ! ( kargs. 0 , b"foo" . as_slice( ) ) ;
660+ }
661+
662+ #[ test]
663+ fn test_add_or_modify_duplicate_parameters ( ) {
664+ let mut kargs = Cmdline :: from ( b"a=1 a=2" ) ;
665+ assert ! ( kargs. add_or_modify( param( "a=3" ) ) ) ;
666+ let mut iter = kargs. iter ( ) ;
667+ assert_eq ! ( iter. next( ) , Some ( param( "a=3" ) ) ) ;
668+ assert_eq ! ( iter. next( ) , None ) ;
669+ }
670+
671+ #[ test]
672+ fn test_remove ( ) {
673+ let mut kargs = Cmdline :: from ( b"foo bar baz" ) ;
674+
675+ // remove existing
676+ assert ! ( kargs. remove( "bar" . into( ) ) ) ;
677+ let mut iter = kargs. iter ( ) ;
678+ assert_eq ! ( iter. next( ) , Some ( param( "foo" ) ) ) ;
679+ assert_eq ! ( iter. next( ) , Some ( param( "baz" ) ) ) ;
680+ assert_eq ! ( iter. next( ) , None ) ;
681+
682+ // doesn't exist? returns false and doesn't modify anything
683+ assert ! ( !kargs. remove( "missing" . into( ) ) ) ;
684+ iter = kargs. iter ( ) ;
685+ assert_eq ! ( iter. next( ) , Some ( param( "foo" ) ) ) ;
686+ assert_eq ! ( iter. next( ) , Some ( param( "baz" ) ) ) ;
687+ assert_eq ! ( iter. next( ) , None ) ;
688+ }
689+
690+ #[ test]
691+ fn test_remove_duplicates ( ) {
692+ let mut kargs = Cmdline :: from ( b"a=1 b=2 a=3" ) ;
693+ assert ! ( kargs. remove( "a" . into( ) ) ) ;
694+ let mut iter = kargs. iter ( ) ;
695+ assert_eq ! ( iter. next( ) , Some ( param( "b=2" ) ) ) ;
696+ assert_eq ! ( iter. next( ) , None ) ;
697+ }
540698}
0 commit comments