blob: f9c8278f4aa5864d493967c92844f73236753f06 [file] [log] [blame]
Jiyong Parkc5d2ef22023-04-11 01:23:46 +09001// 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))]
18use alloc::format;
19#[cfg(not(test))]
20use alloc::string::String;
21use core::ffi::CStr;
22
23/// A single boot argument ex: "panic", "init=", or "foo=1,2,3".
24pub struct BootArg<'a> {
25 arg: &'a str,
26 equal_sign: Option<usize>,
27}
28
29impl AsRef<str> for BootArg<'_> {
30 fn as_ref(&self) -> &str {
31 self.arg
32 }
33}
34
35impl BootArg<'_> {
36 /// Name of the boot argument
37 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.
46 pub fn value(&self) -> Option<&str> {
47 Some(&self.arg[self.equal_sign?..])
48 }
49}
50
51/// Iterator that iteratos over bootargs
52pub struct BootArgsIterator<'a> {
53 arg: &'a str,
54}
55
56impl<'a> BootArgsIterator<'a> {
57 /// Creates a new iterator from the raw boot args. The input has to be encoded in ASCII
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.
69 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
82impl<'a> Iterator for BootArgsIterator<'a> {
83 type Item = BootArg<'a>;
84
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)]
110mod helpers;
111
112#[cfg(test)]
113mod tests {
114
115 use super::*;
116 use crate::cstr;
117
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]
143 fn empty() {
144 check(cstr!(""), Ok(&[]));
145 check(cstr!(" "), Ok(&[]));
146 check(cstr!(" \n "), Ok(&[]));
147 }
148
149 #[test]
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]
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]
171 fn single_with_quote() {
172 check(cstr!("foo=hello\" \"world"), Ok(&[("foo", Some("=hello\" \"world"))]));
173 }
174
175 #[test]
176 fn invalid_encoding() {
177 check(CStr::from_bytes_with_nul(&[255, 255, 255, 0]).unwrap(), Err(()));
178 }
179
180 #[test]
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]
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]
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}