@@ -128,6 +128,83 @@ impl<'a> Cmdline<'a> {
128
128
anyhow:: anyhow!( "Failed to find kernel argument '{key}'" )
129
129
} )
130
130
}
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
+ }
131
208
}
132
209
133
210
/// A single kernel command line parameter key
@@ -144,6 +221,12 @@ impl<'a> std::ops::Deref for ParameterKey<'a> {
144
221
}
145
222
}
146
223
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
+
147
230
impl PartialEq for ParameterKey < ' _ > {
148
231
/// Compares two parameter keys for equality.
149
232
///
@@ -171,6 +254,8 @@ impl PartialEq for ParameterKey<'_> {
171
254
/// A single kernel command line parameter.
172
255
#[ derive( Debug , Eq ) ]
173
256
pub struct Parameter < ' a > {
257
+ /// The full original value
258
+ parameter : & ' a [ u8 ] ,
174
259
/// The parameter key as raw bytes
175
260
key : ParameterKey < ' a > ,
176
261
/// The parameter value as raw bytes, if present
@@ -214,6 +299,7 @@ impl<'a> Parameter<'a> {
214
299
215
300
let ret = match equals {
216
301
None => Self {
302
+ parameter : input,
217
303
key : ParameterKey ( input) ,
218
304
value : None ,
219
305
} ,
@@ -232,6 +318,7 @@ impl<'a> Parameter<'a> {
232
318
} ;
233
319
234
320
Self {
321
+ parameter : input,
235
322
key,
236
323
value : Some ( value) ,
237
324
}
@@ -537,4 +624,75 @@ mod tests {
537
624
assert_eq ! ( rd_args[ 2 ] , param( "rd.foo=a" ) ) ;
538
625
assert_eq ! ( rd_args[ 3 ] , param( "rd.qux=c" ) ) ;
539
626
}
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
+ }
540
698
}
0 commit comments