@@ -128,6 +128,73 @@ 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+ let mut new_params = Vec :: new ( ) ;
142+
143+ for p in self . iter ( ) {
144+ if p. key == param. key {
145+ seen = true ;
146+ if p == param {
147+ new_params. push ( p. parameter ) ;
148+ } else {
149+ modified = true ;
150+ new_params. push ( param. parameter ) ;
151+ }
152+ } else {
153+ new_params. push ( p. parameter ) ;
154+ }
155+ }
156+
157+ if seen {
158+ if modified {
159+ // re-"serialize" ourselves since we modified in-place
160+ self . 0 = Cow :: Owned ( new_params. join ( b" " . as_slice ( ) ) ) ;
161+ true
162+ } else {
163+ // It was already correct
164+ false
165+ }
166+ } else {
167+ // We need to append it
168+ let self_mut = self . 0 . to_mut ( ) ;
169+ if !self_mut. ends_with ( b" " ) {
170+ self_mut. push ( b' ' ) ;
171+ }
172+ self_mut. extend_from_slice ( param. parameter ) ;
173+ true
174+ }
175+ }
176+
177+ /// Remove parameter(s) with the given key from the command line
178+ ///
179+ /// Returns `true` if parameter(s) were removed.
180+ pub fn remove ( & mut self , key : ParameterKey ) -> bool {
181+ let mut removed = false ;
182+ let mut new_params = Vec :: new ( ) ;
183+
184+ for p in self . iter ( ) {
185+ if p. key == key {
186+ removed = true ;
187+ } else {
188+ new_params. push ( p. parameter ) ;
189+ }
190+ }
191+
192+ if removed {
193+ self . 0 = Cow :: Owned ( new_params. join ( b" " . as_slice ( ) ) ) ;
194+ }
195+
196+ removed
197+ }
131198}
132199
133200/// A single kernel command line parameter key
@@ -144,6 +211,12 @@ impl<'a> std::ops::Deref for ParameterKey<'a> {
144211 }
145212}
146213
214+ impl < ' a , T : AsRef < [ u8 ] > + ?Sized > From < & ' a T > for ParameterKey < ' a > {
215+ fn from ( s : & ' a T ) -> Self {
216+ Self ( s. as_ref ( ) )
217+ }
218+ }
219+
147220impl PartialEq for ParameterKey < ' _ > {
148221 /// Compares two parameter keys for equality.
149222 ///
@@ -171,6 +244,8 @@ impl PartialEq for ParameterKey<'_> {
171244/// A single kernel command line parameter.
172245#[ derive( Debug , Eq ) ]
173246pub struct Parameter < ' a > {
247+ /// The full original value
248+ parameter : & ' a [ u8 ] ,
174249 /// The parameter key as raw bytes
175250 key : ParameterKey < ' a > ,
176251 /// The parameter value as raw bytes, if present
@@ -214,6 +289,7 @@ impl<'a> Parameter<'a> {
214289
215290 let ret = match equals {
216291 None => Self {
292+ parameter : input,
217293 key : ParameterKey ( input) ,
218294 value : None ,
219295 } ,
@@ -232,6 +308,7 @@ impl<'a> Parameter<'a> {
232308 } ;
233309
234310 Self {
311+ parameter : input,
235312 key,
236313 value : Some ( value) ,
237314 }
@@ -537,4 +614,50 @@ mod tests {
537614 assert_eq ! ( rd_args[ 2 ] , param( "rd.foo=a" ) ) ;
538615 assert_eq ! ( rd_args[ 3 ] , param( "rd.qux=c" ) ) ;
539616 }
617+
618+ #[ test]
619+ fn test_add_or_modify ( ) {
620+ let mut kargs = Cmdline :: from ( b"foo=bar" ) ;
621+
622+ // add new
623+ assert ! ( kargs. add_or_modify( param( "baz" ) ) ) ;
624+ let mut iter = kargs. iter ( ) ;
625+ assert_eq ! ( iter. next( ) , Some ( param( "foo=bar" ) ) ) ;
626+ assert_eq ! ( iter. next( ) , Some ( param( "baz" ) ) ) ;
627+ assert_eq ! ( iter. next( ) , None ) ;
628+
629+ // modify existing
630+ assert ! ( kargs. add_or_modify( param( "foo=fuz" ) ) ) ;
631+ iter = kargs. iter ( ) ;
632+ assert_eq ! ( iter. next( ) , Some ( param( "foo=fuz" ) ) ) ;
633+ assert_eq ! ( iter. next( ) , Some ( param( "baz" ) ) ) ;
634+ assert_eq ! ( iter. next( ) , None ) ;
635+
636+ // already exists with same value returns false and doesn't
637+ // modify anything
638+ assert ! ( !kargs. add_or_modify( param( "foo=fuz" ) ) ) ;
639+ iter = kargs. iter ( ) ;
640+ assert_eq ! ( iter. next( ) , Some ( param( "foo=fuz" ) ) ) ;
641+ assert_eq ! ( iter. next( ) , Some ( param( "baz" ) ) ) ;
642+ assert_eq ! ( iter. next( ) , None ) ;
643+ }
644+
645+ #[ test]
646+ fn test_remove ( ) {
647+ let mut kargs = Cmdline :: from ( b"foo bar baz" ) ;
648+
649+ // remove existing
650+ assert ! ( kargs. remove( "bar" . into( ) ) ) ;
651+ let mut iter = kargs. iter ( ) ;
652+ assert_eq ! ( iter. next( ) , Some ( param( "foo" ) ) ) ;
653+ assert_eq ! ( iter. next( ) , Some ( param( "baz" ) ) ) ;
654+ assert_eq ! ( iter. next( ) , None ) ;
655+
656+ // doesn't exist? returns false and doesn't modify anything
657+ assert ! ( !kargs. remove( "missing" . into( ) ) ) ;
658+ iter = kargs. iter ( ) ;
659+ assert_eq ! ( iter. next( ) , Some ( param( "foo" ) ) ) ;
660+ assert_eq ! ( iter. next( ) , Some ( param( "baz" ) ) ) ;
661+ assert_eq ! ( iter. next( ) , None ) ;
662+ }
540663}
0 commit comments