@@ -128,6 +128,77 @@ 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 seen = false ;
140+ let mut modified = false ;
141+
142+ let params = self
143+ . iter ( )
144+ . filter_map ( |p| {
145+ if p. key == param. key {
146+ seen = true ;
147+
148+ if p == param {
149+ Some ( p. parameter )
150+ } else {
151+ modified = true ;
152+ Some ( param. parameter )
153+ }
154+ } else {
155+ Some ( p. parameter )
156+ }
157+ } )
158+ . collect :: < Vec < _ > > ( ) ;
159+
160+ if seen && !modified {
161+ // It was already correct
162+ return false ;
163+ }
164+
165+ if !seen {
166+ // We need to append it
167+ let self_mut = self . 0 . to_mut ( ) ;
168+ self_mut. push ( b' ' ) ;
169+ self_mut. extend_from_slice ( param. parameter ) ;
170+ return true ;
171+ }
172+
173+ // Otherwise, we modified it in-place, so re-"serialize" ourselves
174+ self . 0 = Cow :: Owned ( params. join ( b" " . as_slice ( ) ) . into_iter ( ) . collect ( ) ) ;
175+ true
176+ }
177+
178+ /// Remove parameter(s) with the given key from the command line
179+ ///
180+ /// Returns `true` if parameter(s) were removed.
181+ pub fn remove ( & mut self , key : ParameterKey ) -> bool {
182+ let mut removed = false ;
183+
184+ let params = self
185+ . iter ( )
186+ . filter_map ( |p| {
187+ if p. key == key {
188+ removed = true ;
189+ None
190+ } else {
191+ Some ( p. parameter )
192+ }
193+ } )
194+ . collect :: < Vec < _ > > ( ) ;
195+
196+ if removed {
197+ self . 0 = Cow :: Owned ( params. join ( b" " . as_slice ( ) ) . into_iter ( ) . collect ( ) ) ;
198+ }
199+
200+ removed
201+ }
131202}
132203
133204/// A single kernel command line parameter key
@@ -144,6 +215,12 @@ impl<'a> std::ops::Deref for ParameterKey<'a> {
144215 }
145216}
146217
218+ impl < ' a , T : AsRef < [ u8 ] > + ?Sized > From < & ' a T > for ParameterKey < ' a > {
219+ fn from ( s : & ' a T ) -> Self {
220+ Self ( s. as_ref ( ) )
221+ }
222+ }
223+
147224impl PartialEq for ParameterKey < ' _ > {
148225 /// Compares two parameter keys for equality.
149226 ///
@@ -171,6 +248,8 @@ impl PartialEq for ParameterKey<'_> {
171248/// A single kernel command line parameter.
172249#[ derive( Debug , Eq ) ]
173250pub struct Parameter < ' a > {
251+ /// The full original value
252+ parameter : & ' a [ u8 ] ,
174253 /// The parameter key as raw bytes
175254 key : ParameterKey < ' a > ,
176255 /// The parameter value as raw bytes, if present
@@ -214,6 +293,7 @@ impl<'a> Parameter<'a> {
214293
215294 let ret = match equals {
216295 None => Self {
296+ parameter : input,
217297 key : ParameterKey ( input) ,
218298 value : None ,
219299 } ,
@@ -232,6 +312,7 @@ impl<'a> Parameter<'a> {
232312 } ;
233313
234314 Self {
315+ parameter : input,
235316 key,
236317 value : Some ( value) ,
237318 }
@@ -537,4 +618,50 @@ mod tests {
537618 assert_eq ! ( rd_args[ 2 ] , param( "rd.foo=a" ) ) ;
538619 assert_eq ! ( rd_args[ 3 ] , param( "rd.qux=c" ) ) ;
539620 }
621+
622+ #[ test]
623+ fn test_add_or_modify ( ) {
624+ let mut kargs = Cmdline :: from ( b"foo=bar" ) ;
625+
626+ // add new
627+ assert ! ( kargs. add_or_modify( param( "baz" ) ) ) ;
628+ let mut iter = kargs. iter ( ) ;
629+ assert_eq ! ( iter. next( ) , Some ( param( "foo=bar" ) ) ) ;
630+ assert_eq ! ( iter. next( ) , Some ( param( "baz" ) ) ) ;
631+ assert_eq ! ( iter. next( ) , None ) ;
632+
633+ // modify existing
634+ assert ! ( kargs. add_or_modify( param( "foo=fuz" ) ) ) ;
635+ iter = kargs. iter ( ) ;
636+ assert_eq ! ( iter. next( ) , Some ( param( "foo=fuz" ) ) ) ;
637+ assert_eq ! ( iter. next( ) , Some ( param( "baz" ) ) ) ;
638+ assert_eq ! ( iter. next( ) , None ) ;
639+
640+ // already exists with same value returns false and doesn't
641+ // modify anything
642+ assert ! ( !kargs. add_or_modify( param( "foo=fuz" ) ) ) ;
643+ iter = kargs. iter ( ) ;
644+ assert_eq ! ( iter. next( ) , Some ( param( "foo=fuz" ) ) ) ;
645+ assert_eq ! ( iter. next( ) , Some ( param( "baz" ) ) ) ;
646+ assert_eq ! ( iter. next( ) , None ) ;
647+ }
648+
649+ #[ test]
650+ fn test_remove ( ) {
651+ let mut kargs = Cmdline :: from ( b"foo bar baz" ) ;
652+
653+ // remove existing
654+ assert ! ( kargs. remove( "bar" . into( ) ) ) ;
655+ let mut iter = kargs. iter ( ) ;
656+ assert_eq ! ( iter. next( ) , Some ( param( "foo" ) ) ) ;
657+ assert_eq ! ( iter. next( ) , Some ( param( "baz" ) ) ) ;
658+ assert_eq ! ( iter. next( ) , None ) ;
659+
660+ // doesn't exist? returns false and doesn't modify anything
661+ assert ! ( !kargs. remove( "missing" . into( ) ) ) ;
662+ iter = kargs. iter ( ) ;
663+ assert_eq ! ( iter. next( ) , Some ( param( "foo" ) ) ) ;
664+ assert_eq ! ( iter. next( ) , Some ( param( "baz" ) ) ) ;
665+ assert_eq ! ( iter. next( ) , None ) ;
666+ }
540667}
0 commit comments