• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2023, The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 //! Routines for parsing bootargs
16 
17 #[cfg(not(test))]
18 use alloc::format;
19 #[cfg(not(test))]
20 use alloc::string::String;
21 use core::ffi::CStr;
22 
23 /// A single boot argument ex: "panic", "init=", or "foo=1,2,3".
24 pub struct BootArg<'a> {
25     arg: &'a str,
26     equal_sign: Option<usize>,
27 }
28 
29 impl AsRef<str> for BootArg<'_> {
as_ref(&self) -> &str30     fn as_ref(&self) -> &str {
31         self.arg
32     }
33 }
34 
35 impl BootArg<'_> {
36     /// Name of the boot argument
name(&self) -> &str37     pub fn name(&self) -> &str {
38         if let Some(n) = self.equal_sign {
39             &self.arg[..n]
40         } else {
41             self.arg
42         }
43     }
44 
45     /// Optional value of the boot aragument. This includes the '=' prefix.
value(&self) -> Option<&str>46     pub fn value(&self) -> Option<&str> {
47         Some(&self.arg[self.equal_sign?..])
48     }
49 }
50 
51 /// Iterator that iteratos over bootargs
52 pub struct BootArgsIterator<'a> {
53     arg: &'a str,
54 }
55 
56 impl<'a> BootArgsIterator<'a> {
57     /// Creates a new iterator from the raw boot args. The input has to be encoded in ASCII
new(bootargs: &'a CStr) -> Result<Self, String>58     pub fn new(bootargs: &'a CStr) -> Result<Self, String> {
59         let arg = bootargs.to_str().map_err(|e| format!("{e}"))?;
60         if !arg.is_ascii() {
61             return Err(format!("{arg:?} is not ASCII"));
62         }
63 
64         Ok(Self { arg })
65     }
66 
67     // Finds the end of a value in the given string `s`, and returns the index of the end. A value
68     // can have spaces if quoted. The quote character can't be escaped.
find_value_end(s: &str) -> usize69     fn find_value_end(s: &str) -> usize {
70         let mut in_quote = false;
71         for (i, c) in s.char_indices() {
72             if c == '"' {
73                 in_quote = !in_quote;
74             } else if c.is_whitespace() && !in_quote {
75                 return i;
76             }
77         }
78         s.len()
79     }
80 }
81 
82 impl<'a> Iterator for BootArgsIterator<'a> {
83     type Item = BootArg<'a>;
84 
next(&mut self) -> Option<Self::Item>85     fn next(&mut self) -> Option<Self::Item> {
86         // Skip spaces to find the start of a name. If there's nothing left, that's the end of the
87         // iterator.
88         let arg = self.arg.trim_start();
89         self.arg = arg; // advance before returning
90         if arg.is_empty() {
91             return None;
92         }
93         // Name ends with either whitespace or =. If it ends with =, the value comes immediately
94         // after.
95         let name_end = arg.find(|c: char| c.is_whitespace() || c == '=').unwrap_or(arg.len());
96         let (arg, equal_sign) = match arg.chars().nth(name_end) {
97             Some(c) if c == '=' => {
98                 let value_end = name_end + Self::find_value_end(&arg[name_end..]);
99                 (&arg[..value_end], Some(name_end))
100             }
101             _ => (&arg[..name_end], None),
102         };
103         self.arg = &self.arg[arg.len()..]; // advance before returning
104         Some(BootArg { arg, equal_sign })
105     }
106 }
107 
108 #[cfg(test)]
109 #[allow(dead_code)]
110 mod helpers;
111 
112 #[cfg(test)]
113 mod tests {
114 
115     use super::*;
116     use crate::cstr;
117 
check(raw: &CStr, expected: Result<&[(&str, Option<&str>)], ()>)118     fn check(raw: &CStr, expected: Result<&[(&str, Option<&str>)], ()>) {
119         let actual = BootArgsIterator::new(raw);
120         assert_eq!(actual.is_err(), expected.is_err(), "Unexpected result with {raw:?}");
121         if actual.is_err() {
122             return;
123         }
124         let mut actual = actual.unwrap();
125 
126         for (name, value) in expected.unwrap() {
127             let actual = actual.next();
128             assert!(actual.is_some(), "Expected ({}, {:?}) from {raw:?}", name, value);
129             let actual = actual.unwrap();
130             assert_eq!(name, &actual.name(), "Unexpected name from {raw:?}");
131             assert_eq!(value, &actual.value(), "Unexpected value from {raw:?}");
132         }
133         let remaining = actual.next();
134         assert!(
135             remaining.is_none(),
136             "Unexpected extra item from {raw:?}. Got ({}, {:?})",
137             remaining.as_ref().unwrap().name(),
138             remaining.as_ref().unwrap().value()
139         );
140     }
141 
142     #[test]
empty()143     fn empty() {
144         check(cstr!(""), Ok(&[]));
145         check(cstr!("    "), Ok(&[]));
146         check(cstr!("  \n  "), Ok(&[]));
147     }
148 
149     #[test]
single()150     fn single() {
151         check(cstr!("foo"), Ok(&[("foo", None)]));
152         check(cstr!("   foo"), Ok(&[("foo", None)]));
153         check(cstr!("foo   "), Ok(&[("foo", None)]));
154         check(cstr!("   foo   "), Ok(&[("foo", None)]));
155     }
156 
157     #[test]
single_with_value()158     fn single_with_value() {
159         check(cstr!("foo=bar"), Ok(&[("foo", Some("=bar"))]));
160         check(cstr!("   foo=bar"), Ok(&[("foo", Some("=bar"))]));
161         check(cstr!("foo=bar   "), Ok(&[("foo", Some("=bar"))]));
162         check(cstr!("   foo=bar   "), Ok(&[("foo", Some("=bar"))]));
163 
164         check(cstr!("foo="), Ok(&[("foo", Some("="))]));
165         check(cstr!("   foo="), Ok(&[("foo", Some("="))]));
166         check(cstr!("foo=   "), Ok(&[("foo", Some("="))]));
167         check(cstr!("   foo=   "), Ok(&[("foo", Some("="))]));
168     }
169 
170     #[test]
single_with_quote()171     fn single_with_quote() {
172         check(cstr!("foo=hello\" \"world"), Ok(&[("foo", Some("=hello\" \"world"))]));
173     }
174 
175     #[test]
invalid_encoding()176     fn invalid_encoding() {
177         check(CStr::from_bytes_with_nul(&[255, 255, 255, 0]).unwrap(), Err(()));
178     }
179 
180     #[test]
multiple()181     fn multiple() {
182         check(
183             cstr!(" a=b   c=d   e=  f g  "),
184             Ok(&[("a", Some("=b")), ("c", Some("=d")), ("e", Some("=")), ("f", None), ("g", None)]),
185         );
186         check(
187             cstr!("   a=b  \n c=d      e=  f g"),
188             Ok(&[("a", Some("=b")), ("c", Some("=d")), ("e", Some("=")), ("f", None), ("g", None)]),
189         );
190     }
191 
192     #[test]
incomplete_quote()193     fn incomplete_quote() {
194         check(
195             cstr!("foo=incomplete\" quote bar=y"),
196             Ok(&[("foo", Some("=incomplete\" quote bar=y"))]),
197         );
198     }
199 
200     #[test]
complex()201     fn complex() {
202         check(cstr!("  a  a1=  b=c d=e,f,g x=\"value with quote\" y=val\"ue with \"multiple\" quo\"te  "), Ok(&[
203             ("a", None),
204             ("a1", Some("=")),
205             ("b", Some("=c")),
206             ("d", Some("=e,f,g")),
207             ("x", Some("=\"value with quote\"")),
208             ("y", Some("=val\"ue with \"multiple\" quo\"te")),
209         ]));
210     }
211 }
212