@@ -128,6 +128,78 @@ 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+ if modified {
165+ self . 0 = Cow :: Owned ( new_params. join ( b" " . as_slice ( ) ) ) ;
166+ true
167+ } else {
168+ // The parameter already existed with the same content, and there were no duplicates.
169+ false
170+ }
171+ } else {
172+ // The parameter was not found, so we append it.
173+ let self_mut = self . 0 . to_mut ( ) ;
174+ if !self_mut. is_empty ( ) && !self_mut. ends_with ( b" " ) {
175+ self_mut. push ( b' ' ) ;
176+ }
177+ self_mut. extend_from_slice ( param. parameter ) ;
178+ true
179+ }
180+ }
181+
182+ /// Remove parameter(s) with the given key from the command line
183+ ///
184+ /// Returns `true` if parameter(s) were removed.
185+ pub fn remove ( & mut self , key : ParameterKey ) -> bool {
186+ let mut removed = false ;
187+ let mut new_params = Vec :: new ( ) ;
188+
189+ for p in self . iter ( ) {
190+ if p. key == key {
191+ removed = true ;
192+ } else {
193+ new_params. push ( p. parameter ) ;
194+ }
195+ }
196+
197+ if removed {
198+ self . 0 = Cow :: Owned ( new_params. join ( b" " . as_slice ( ) ) ) ;
199+ }
200+
201+ removed
202+ }
131203}
132204
133205/// A single kernel command line parameter key
@@ -144,6 +216,12 @@ impl<'a> std::ops::Deref for ParameterKey<'a> {
144216 }
145217}
146218
219+ impl < ' a , T : AsRef < [ u8 ] > + ?Sized > From < & ' a T > for ParameterKey < ' a > {
220+ fn from ( s : & ' a T ) -> Self {
221+ Self ( s. as_ref ( ) )
222+ }
223+ }
224+
147225impl PartialEq for ParameterKey < ' _ > {
148226 /// Compares two parameter keys for equality.
149227 ///
@@ -171,6 +249,8 @@ impl PartialEq for ParameterKey<'_> {
171249/// A single kernel command line parameter.
172250#[ derive( Debug , Eq ) ]
173251pub struct Parameter < ' a > {
252+ /// The full original value
253+ parameter : & ' a [ u8 ] ,
174254 /// The parameter key as raw bytes
175255 key : ParameterKey < ' a > ,
176256 /// The parameter value as raw bytes, if present
@@ -214,6 +294,7 @@ impl<'a> Parameter<'a> {
214294
215295 let ret = match equals {
216296 None => Self {
297+ parameter : input,
217298 key : ParameterKey ( input) ,
218299 value : None ,
219300 } ,
@@ -232,6 +313,7 @@ impl<'a> Parameter<'a> {
232313 } ;
233314
234315 Self {
316+ parameter : input,
235317 key,
236318 value : Some ( value) ,
237319 }
@@ -537,4 +619,50 @@ mod tests {
537619 assert_eq ! ( rd_args[ 2 ] , param( "rd.foo=a" ) ) ;
538620 assert_eq ! ( rd_args[ 3 ] , param( "rd.qux=c" ) ) ;
539621 }
622+
623+ #[ test]
624+ fn test_add_or_modify ( ) {
625+ let mut kargs = Cmdline :: from ( b"foo=bar" ) ;
626+
627+ // add new
628+ assert ! ( kargs. add_or_modify( param( "baz" ) ) ) ;
629+ let mut iter = kargs. iter ( ) ;
630+ assert_eq ! ( iter. next( ) , Some ( param( "foo=bar" ) ) ) ;
631+ assert_eq ! ( iter. next( ) , Some ( param( "baz" ) ) ) ;
632+ assert_eq ! ( iter. next( ) , None ) ;
633+
634+ // modify existing
635+ assert ! ( kargs. add_or_modify( param( "foo=fuz" ) ) ) ;
636+ iter = kargs. iter ( ) ;
637+ assert_eq ! ( iter. next( ) , Some ( param( "foo=fuz" ) ) ) ;
638+ assert_eq ! ( iter. next( ) , Some ( param( "baz" ) ) ) ;
639+ assert_eq ! ( iter. next( ) , None ) ;
640+
641+ // already exists with same value returns false and doesn't
642+ // modify anything
643+ assert ! ( !kargs. add_or_modify( param( "foo=fuz" ) ) ) ;
644+ iter = kargs. iter ( ) ;
645+ assert_eq ! ( iter. next( ) , Some ( param( "foo=fuz" ) ) ) ;
646+ assert_eq ! ( iter. next( ) , Some ( param( "baz" ) ) ) ;
647+ assert_eq ! ( iter. next( ) , None ) ;
648+ }
649+
650+ #[ test]
651+ fn test_remove ( ) {
652+ let mut kargs = Cmdline :: from ( b"foo bar baz" ) ;
653+
654+ // remove existing
655+ assert ! ( kargs. remove( "bar" . into( ) ) ) ;
656+ let mut iter = kargs. iter ( ) ;
657+ assert_eq ! ( iter. next( ) , Some ( param( "foo" ) ) ) ;
658+ assert_eq ! ( iter. next( ) , Some ( param( "baz" ) ) ) ;
659+ assert_eq ! ( iter. next( ) , None ) ;
660+
661+ // doesn't exist? returns false and doesn't modify anything
662+ assert ! ( !kargs. remove( "missing" . into( ) ) ) ;
663+ iter = kargs. iter ( ) ;
664+ assert_eq ! ( iter. next( ) , Some ( param( "foo" ) ) ) ;
665+ assert_eq ! ( iter. next( ) , Some ( param( "baz" ) ) ) ;
666+ assert_eq ! ( iter. next( ) , None ) ;
667+ }
540668}
0 commit comments