1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 
5 use crate::preferences::{Pref, PrefValue, Preferences};
6 use std::borrow::Borrow;
7 use std::borrow::Cow;
8 use std::char;
9 use std::error::Error;
10 use std::fmt;
11 use std::io::{self, Write};
12 use std::iter::Iterator;
13 use std::mem;
14 use std::str;
15 
16 impl PrefReaderError {
new( message: &'static str, position: Position, parent: Option<Box<dyn Error>>, ) -> PrefReaderError17     fn new(
18         message: &'static str,
19         position: Position,
20         parent: Option<Box<dyn Error>>,
21     ) -> PrefReaderError {
22         PrefReaderError {
23             message,
24             position,
25             parent,
26         }
27     }
28 }
29 
30 impl fmt::Display for PrefReaderError {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result31     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
32         write!(
33             f,
34             "{} at line {}, column {}",
35             self.message, self.position.line, self.position.column
36         )
37     }
38 }
39 
40 impl Error for PrefReaderError {
description(&self) -> &str41     fn description(&self) -> &str {
42         self.message
43     }
44 
cause(&self) -> Option<&dyn Error>45     fn cause(&self) -> Option<&dyn Error> {
46         self.parent.as_deref()
47     }
48 }
49 
50 impl From<io::Error> for PrefReaderError {
from(err: io::Error) -> PrefReaderError51     fn from(err: io::Error) -> PrefReaderError {
52         PrefReaderError::new("IOError", Position::new(), Some(err.into()))
53     }
54 }
55 
56 #[derive(Copy, Clone, Debug, PartialEq)]
57 enum TokenizerState {
58     Junk,
59     CommentStart,
60     CommentLine,
61     CommentBlock,
62     FunctionName,
63     AfterFunctionName,
64     FunctionArgs,
65     FunctionArg,
66     DoubleQuotedString,
67     SingleQuotedString,
68     Number,
69     Bool,
70     AfterFunctionArg,
71     AfterFunction,
72     Error,
73 }
74 
75 #[derive(Copy, Clone, Debug, Default, PartialEq)]
76 pub struct Position {
77     line: u32,
78     column: u32,
79 }
80 
81 impl Position {
new() -> Position82     pub fn new() -> Position {
83         Position { line: 1, column: 0 }
84     }
85 }
86 
87 #[derive(Copy, Clone, Debug, PartialEq)]
88 pub enum TokenType {
89     None,
90     PrefFunction,
91     UserPrefFunction,
92     StickyPrefFunction,
93     CommentBlock,
94     CommentLine,
95     CommentBashLine,
96     Paren,
97     Semicolon,
98     Comma,
99     String,
100     Int,
101     Bool,
102     Error,
103 }
104 
105 #[derive(Debug, PartialEq)]
106 pub enum PrefToken<'a> {
107     PrefFunction(Position),
108     UserPrefFunction(Position),
109     StickyPrefFunction(Position),
110     CommentBlock(Cow<'a, str>, Position),
111     CommentLine(Cow<'a, str>, Position),
112     CommentBashLine(Cow<'a, str>, Position),
113     Paren(char, Position),
114     Semicolon(Position),
115     Comma(Position),
116     String(Cow<'a, str>, Position),
117     Int(i64, Position),
118     Bool(bool, Position),
119     Error(&'static str, Position),
120 }
121 
122 impl<'a> PrefToken<'a> {
position(&self) -> Position123     fn position(&self) -> Position {
124         match *self {
125             PrefToken::PrefFunction(position) => position,
126             PrefToken::UserPrefFunction(position) => position,
127             PrefToken::StickyPrefFunction(position) => position,
128             PrefToken::CommentBlock(_, position) => position,
129             PrefToken::CommentLine(_, position) => position,
130             PrefToken::CommentBashLine(_, position) => position,
131             PrefToken::Paren(_, position) => position,
132             PrefToken::Semicolon(position) => position,
133             PrefToken::Comma(position) => position,
134             PrefToken::String(_, position) => position,
135             PrefToken::Int(_, position) => position,
136             PrefToken::Bool(_, position) => position,
137             PrefToken::Error(_, position) => position,
138         }
139     }
140 }
141 
142 #[derive(Debug)]
143 pub struct PrefReaderError {
144     message: &'static str,
145     position: Position,
146     parent: Option<Box<dyn Error>>,
147 }
148 
149 struct TokenData<'a> {
150     token_type: TokenType,
151     complete: bool,
152     position: Position,
153     data: Cow<'a, str>,
154     start_pos: usize,
155 }
156 
157 impl<'a> TokenData<'a> {
new(token_type: TokenType, position: Position, start_pos: usize) -> TokenData<'a>158     fn new(token_type: TokenType, position: Position, start_pos: usize) -> TokenData<'a> {
159         TokenData {
160             token_type,
161             complete: false,
162             position,
163             data: Cow::Borrowed(""),
164             start_pos,
165         }
166     }
167 
start(&mut self, tokenizer: &PrefTokenizer, token_type: TokenType)168     fn start(&mut self, tokenizer: &PrefTokenizer, token_type: TokenType) {
169         self.token_type = token_type;
170         self.position = tokenizer.position;
171         self.start_pos = tokenizer.pos;
172     }
173 
end(&mut self, buf: &'a [u8], end_pos: usize) -> Result<(), PrefReaderError>174     fn end(&mut self, buf: &'a [u8], end_pos: usize) -> Result<(), PrefReaderError> {
175         self.complete = true;
176         self.add_slice_to_token(buf, end_pos)
177     }
178 
add_slice_to_token(&mut self, buf: &'a [u8], end_pos: usize) -> Result<(), PrefReaderError>179     fn add_slice_to_token(&mut self, buf: &'a [u8], end_pos: usize) -> Result<(), PrefReaderError> {
180         let data = match str::from_utf8(&buf[self.start_pos..end_pos]) {
181             Ok(x) => x,
182             Err(_) => {
183                 return Err(PrefReaderError::new(
184                     "Could not convert string to utf8",
185                     self.position,
186                     None,
187                 ));
188             }
189         };
190         if self.data != "" {
191             self.data.to_mut().push_str(&data)
192         } else {
193             self.data = Cow::Borrowed(&data)
194         };
195         Ok(())
196     }
197 
push_char(&mut self, tokenizer: &PrefTokenizer, data: char)198     fn push_char(&mut self, tokenizer: &PrefTokenizer, data: char) {
199         self.data.to_mut().push(data);
200         self.start_pos = tokenizer.pos + 1;
201     }
202 }
203 
204 pub struct PrefTokenizer<'a> {
205     data: &'a [u8],
206     pos: usize,
207     cur: Option<char>,
208     position: Position,
209     state: TokenizerState,
210     next_state: Option<TokenizerState>,
211 }
212 
213 impl<'a> PrefTokenizer<'a> {
new(data: &'a [u8]) -> PrefTokenizer<'a>214     pub fn new(data: &'a [u8]) -> PrefTokenizer<'a> {
215         PrefTokenizer {
216             data,
217             pos: 0,
218             cur: None,
219             position: Position::new(),
220             state: TokenizerState::Junk,
221             next_state: Some(TokenizerState::FunctionName),
222         }
223     }
224 
make_token(&mut self, token_data: TokenData<'a>) -> PrefToken<'a>225     fn make_token(&mut self, token_data: TokenData<'a>) -> PrefToken<'a> {
226         let buf = token_data.data;
227         let position = token_data.position;
228         match token_data.token_type {
229             TokenType::None => panic!("Got a token without a type"),
230             TokenType::PrefFunction => PrefToken::PrefFunction(position),
231             TokenType::UserPrefFunction => PrefToken::UserPrefFunction(position),
232             TokenType::StickyPrefFunction => PrefToken::StickyPrefFunction(position),
233             TokenType::CommentBlock => PrefToken::CommentBlock(buf, position),
234             TokenType::CommentLine => PrefToken::CommentLine(buf, position),
235             TokenType::CommentBashLine => PrefToken::CommentBashLine(buf, position),
236             TokenType::Paren => {
237                 if buf.len() != 1 {
238                     panic!("Expected a buffer of length one");
239                 }
240                 PrefToken::Paren(buf.chars().next().unwrap(), position)
241             }
242             TokenType::Semicolon => PrefToken::Semicolon(position),
243             TokenType::Comma => PrefToken::Comma(position),
244             TokenType::String => PrefToken::String(buf, position),
245             TokenType::Int => {
246                 let value = buf.parse::<i64>().expect("Integer wasn't parsed as an i64");
247                 PrefToken::Int(value, position)
248             }
249             TokenType::Bool => {
250                 let value = match buf.borrow() {
251                     "true" => true,
252                     "false" => false,
253                     x => panic!("Boolean wasn't 'true' or 'false' (was {})", x),
254                 };
255                 PrefToken::Bool(value, position)
256             }
257             TokenType::Error => panic!("make_token can't construct errors"),
258         }
259     }
260 
get_char(&mut self) -> Option<char>261     fn get_char(&mut self) -> Option<char> {
262         if self.pos >= self.data.len() - 1 {
263             self.cur = None;
264             return None;
265         };
266         if self.cur.is_some() {
267             self.pos += 1;
268         }
269         let c = self.data[self.pos] as char;
270         if self.cur == Some('\n') {
271             self.position.line += 1;
272             self.position.column = 0;
273         } else if self.cur.is_some() {
274             self.position.column += 1;
275         };
276         self.cur = Some(c);
277         self.cur
278     }
279 
unget_char(&mut self) -> Option<char>280     fn unget_char(&mut self) -> Option<char> {
281         if self.pos == 0 {
282             self.position.column = 0;
283             self.cur = None
284         } else {
285             self.pos -= 1;
286             let c = self.data[self.pos] as char;
287             if c == '\n' {
288                 self.position.line -= 1;
289                 let mut col_pos = self.pos - 1;
290                 while col_pos > 0 && self.data[col_pos] as char != '\n' {
291                     col_pos -= 1;
292                 }
293                 self.position.column = (self.pos - col_pos as usize - 1) as u32;
294             } else {
295                 self.position.column -= 1;
296             }
297             self.cur = Some(c);
298         }
299         self.cur
300     }
301 
is_space(c: char) -> bool302     fn is_space(c: char) -> bool {
303         matches!(c, ' ' | '\t' | '\r' | '\n')
304     }
305 
skip_whitespace(&mut self) -> Option<char>306     fn skip_whitespace(&mut self) -> Option<char> {
307         while let Some(c) = self.cur {
308             if PrefTokenizer::is_space(c) {
309                 self.get_char();
310             } else {
311                 break;
312             };
313         }
314         self.cur
315     }
316 
consume_escape(&mut self, token_data: &mut TokenData<'a>) -> Result<(), PrefReaderError>317     fn consume_escape(&mut self, token_data: &mut TokenData<'a>) -> Result<(), PrefReaderError> {
318         let pos = self.pos;
319         let escaped = self.read_escape()?;
320         if let Some(escape_char) = escaped {
321             token_data.add_slice_to_token(&self.data, pos)?;
322             token_data.push_char(&self, escape_char);
323         };
324         Ok(())
325     }
326 
read_escape(&mut self) -> Result<Option<char>, PrefReaderError>327     fn read_escape(&mut self) -> Result<Option<char>, PrefReaderError> {
328         let escape_char = match self.get_char() {
329             Some('u') => self.read_hex_escape(4, true)?,
330             Some('x') => self.read_hex_escape(2, true)?,
331             Some('\\') => '\\' as u32,
332             Some('"') => '"' as u32,
333             Some('\'') => '\'' as u32,
334             Some('r') => '\r' as u32,
335             Some('n') => '\n' as u32,
336             Some(_) => return Ok(None),
337             None => {
338                 return Err(PrefReaderError::new(
339                     "EOF in character escape",
340                     self.position,
341                     None,
342                 ))
343             }
344         };
345         Ok(Some(char::from_u32(escape_char).ok_or_else(|| {
346             PrefReaderError::new("Invalid codepoint decoded from escape", self.position, None)
347         })?))
348     }
349 
read_hex_escape(&mut self, hex_chars: isize, first: bool) -> Result<u32, PrefReaderError>350     fn read_hex_escape(&mut self, hex_chars: isize, first: bool) -> Result<u32, PrefReaderError> {
351         let mut value = 0;
352         for _ in 0..hex_chars {
353             match self.get_char() {
354                 Some(x) => {
355                     value <<= 4;
356                     match x {
357                         '0'..='9' => value += x as u32 - '0' as u32,
358                         'a'..='f' => value += x as u32 - 'a' as u32,
359                         'A'..='F' => value += x as u32 - 'A' as u32,
360                         _ => {
361                             return Err(PrefReaderError::new(
362                                 "Unexpected character in escape",
363                                 self.position,
364                                 None,
365                             ))
366                         }
367                     }
368                 }
369                 None => {
370                     return Err(PrefReaderError::new(
371                         "Unexpected EOF in escape",
372                         self.position,
373                         None,
374                     ))
375                 }
376             }
377         }
378         if first && (0xD800..=0xDBFF).contains(&value) {
379             // First part of a surrogate pair
380             if self.get_char() != Some('\\') || self.get_char() != Some('u') {
381                 return Err(PrefReaderError::new(
382                     "Lone high surrogate in surrogate pair",
383                     self.position,
384                     None,
385                 ));
386             }
387             self.unget_char();
388             let high_surrogate = value;
389             let low_surrogate = self.read_hex_escape(4, false)?;
390             let high_value = (high_surrogate - 0xD800) << 10;
391             let low_value = low_surrogate - 0xDC00;
392             value = high_value + low_value + 0x10000;
393         } else if first && (0xDC00..=0xDFFF).contains(&value) {
394             return Err(PrefReaderError::new(
395                 "Lone low surrogate",
396                 self.position,
397                 None,
398             ));
399         } else if !first && !(0xDC00..=0xDFFF).contains(&value) {
400             return Err(PrefReaderError::new(
401                 "Invalid low surrogate in surrogate pair",
402                 self.position,
403                 None,
404             ));
405         }
406         Ok(value)
407     }
408 
get_match(&mut self, target: &str, separators: &str) -> bool409     fn get_match(&mut self, target: &str, separators: &str) -> bool {
410         let initial_pos = self.pos;
411         let mut matched = true;
412         for c in target.chars() {
413             if self.cur == Some(c) {
414                 self.get_char();
415             } else {
416                 matched = false;
417                 break;
418             }
419         }
420 
421         if !matched {
422             for _ in 0..(self.pos - initial_pos) {
423                 self.unget_char();
424             }
425         } else {
426             // Check that the next character is whitespace or a separator
427             if let Some(c) = self.cur {
428                 if !(PrefTokenizer::is_space(c) || separators.contains(c) || c == '/') {
429                     matched = false;
430                 }
431             }
432             self.unget_char();
433         }
434 
435         matched
436     }
437 
next_token(&mut self) -> Result<Option<TokenData<'a>>, PrefReaderError>438     fn next_token(&mut self) -> Result<Option<TokenData<'a>>, PrefReaderError> {
439         let mut token_data = TokenData::new(TokenType::None, Position::new(), 0);
440 
441         loop {
442             let mut c = match self.get_char() {
443                 Some(x) => x,
444                 None => return Ok(None),
445             };
446 
447             self.state = match self.state {
448                 TokenizerState::Junk => {
449                     c = match self.skip_whitespace() {
450                         Some(x) => x,
451                         None => return Ok(None),
452                     };
453                     match c {
454                         '/' => TokenizerState::CommentStart,
455                         '#' => {
456                             token_data.start(&self, TokenType::CommentBashLine);
457                             token_data.start_pos = self.pos + 1;
458                             TokenizerState::CommentLine
459                         }
460                         _ => {
461                             self.unget_char();
462                             let next = match self.next_state {
463                                 Some(x) => x,
464                                 None => {
465                                     return Err(PrefReaderError::new(
466                                         "In Junk state without a next state defined",
467                                         self.position,
468                                         None,
469                                     ))
470                                 }
471                             };
472                             self.next_state = None;
473                             next
474                         }
475                     }
476                 }
477                 TokenizerState::CommentStart => match c {
478                     '*' => {
479                         token_data.start(&self, TokenType::CommentBlock);
480                         token_data.start_pos = self.pos + 1;
481                         TokenizerState::CommentBlock
482                     }
483                     '/' => {
484                         token_data.start(&self, TokenType::CommentLine);
485                         token_data.start_pos = self.pos + 1;
486                         TokenizerState::CommentLine
487                     }
488                     _ => {
489                         return Err(PrefReaderError::new(
490                             "Invalid character after /",
491                             self.position,
492                             None,
493                         ))
494                     }
495                 },
496                 TokenizerState::CommentLine => match c {
497                     '\n' => {
498                         token_data.end(&self.data, self.pos)?;
499                         TokenizerState::Junk
500                     }
501                     _ => TokenizerState::CommentLine,
502                 },
503                 TokenizerState::CommentBlock => match c {
504                     '*' => {
505                         if self.get_char() == Some('/') {
506                             token_data.end(&self.data, self.pos - 1)?;
507                             TokenizerState::Junk
508                         } else {
509                             TokenizerState::CommentBlock
510                         }
511                     }
512                     _ => TokenizerState::CommentBlock,
513                 },
514                 TokenizerState::FunctionName => {
515                     let position = self.position;
516                     let start_pos = self.pos;
517                     match c {
518                         'u' => {
519                             if self.get_match("user_pref", "(") {
520                                 token_data.start(&self, TokenType::UserPrefFunction);
521                             }
522                         }
523                         's' => {
524                             if self.get_match("sticky_pref", "(") {
525                                 token_data.start(&self, TokenType::StickyPrefFunction);
526                             }
527                         }
528                         'p' => {
529                             if self.get_match("pref", "(") {
530                                 token_data.start(&self, TokenType::PrefFunction);
531                             }
532                         }
533                         _ => {}
534                     };
535                     if token_data.token_type == TokenType::None {
536                         // We didn't match anything
537                         return Err(PrefReaderError::new(
538                             "Expected a pref function name",
539                             position,
540                             None,
541                         ));
542                     } else {
543                         token_data.start_pos = start_pos;
544                         token_data.position = position;
545                         token_data.end(&self.data, self.pos + 1)?;
546                         self.next_state = Some(TokenizerState::AfterFunctionName);
547                         TokenizerState::Junk
548                     }
549                 }
550                 TokenizerState::AfterFunctionName => match c {
551                     '(' => {
552                         self.next_state = Some(TokenizerState::FunctionArgs);
553                         token_data.start(&self, TokenType::Paren);
554                         token_data.end(&self.data, self.pos + 1)?;
555                         self.next_state = Some(TokenizerState::FunctionArgs);
556                         TokenizerState::Junk
557                     }
558                     _ => {
559                         return Err(PrefReaderError::new(
560                             "Expected an opening paren",
561                             self.position,
562                             None,
563                         ))
564                     }
565                 },
566                 TokenizerState::FunctionArgs => match c {
567                     ')' => {
568                         token_data.start(&self, TokenType::Paren);
569                         token_data.end(&self.data, self.pos + 1)?;
570                         self.next_state = Some(TokenizerState::AfterFunction);
571                         TokenizerState::Junk
572                     }
573                     _ => {
574                         self.unget_char();
575                         TokenizerState::FunctionArg
576                     }
577                 },
578                 TokenizerState::FunctionArg => match c {
579                     '"' => {
580                         token_data.start(&self, TokenType::String);
581                         token_data.start_pos = self.pos + 1;
582                         TokenizerState::DoubleQuotedString
583                     }
584                     '\'' => {
585                         token_data.start(&self, TokenType::String);
586                         token_data.start_pos = self.pos + 1;
587                         TokenizerState::SingleQuotedString
588                     }
589                     't' | 'f' => {
590                         self.unget_char();
591                         TokenizerState::Bool
592                     }
593                     '0'..='9' | '-' | '+' => {
594                         token_data.start(&self, TokenType::Int);
595                         TokenizerState::Number
596                     }
597                     _ => {
598                         return Err(PrefReaderError::new(
599                             "Invalid character at start of function argument",
600                             self.position,
601                             None,
602                         ))
603                     }
604                 },
605                 TokenizerState::DoubleQuotedString => match c {
606                     '"' => {
607                         token_data.end(&self.data, self.pos)?;
608                         self.next_state = Some(TokenizerState::AfterFunctionArg);
609                         TokenizerState::Junk
610                     }
611                     '\n' => {
612                         return Err(PrefReaderError::new(
613                             "EOL in double quoted string",
614                             self.position,
615                             None,
616                         ))
617                     }
618                     '\\' => {
619                         self.consume_escape(&mut token_data)?;
620                         TokenizerState::DoubleQuotedString
621                     }
622                     _ => TokenizerState::DoubleQuotedString,
623                 },
624                 TokenizerState::SingleQuotedString => match c {
625                     '\'' => {
626                         token_data.end(&self.data, self.pos)?;
627                         self.next_state = Some(TokenizerState::AfterFunctionArg);
628                         TokenizerState::Junk
629                     }
630                     '\n' => {
631                         return Err(PrefReaderError::new(
632                             "EOL in single quoted string",
633                             self.position,
634                             None,
635                         ))
636                     }
637                     '\\' => {
638                         self.consume_escape(&mut token_data)?;
639                         TokenizerState::SingleQuotedString
640                     }
641                     _ => TokenizerState::SingleQuotedString,
642                 },
643                 TokenizerState::Number => match c {
644                     '0'..='9' => TokenizerState::Number,
645                     ')' | ',' => {
646                         token_data.end(&self.data, self.pos)?;
647                         self.unget_char();
648                         self.next_state = Some(TokenizerState::AfterFunctionArg);
649                         TokenizerState::Junk
650                     }
651                     x if PrefTokenizer::is_space(x) => {
652                         token_data.end(&self.data, self.pos)?;
653                         self.next_state = Some(TokenizerState::AfterFunctionArg);
654                         TokenizerState::Junk
655                     }
656                     _ => {
657                         return Err(PrefReaderError::new(
658                             "Invalid character in number literal",
659                             self.position,
660                             None,
661                         ))
662                     }
663                 },
664                 TokenizerState::Bool => {
665                     let start_pos = self.pos;
666                     let position = self.position;
667                     match c {
668                         't' => {
669                             if self.get_match("true", ",)") {
670                                 token_data.start(&self, TokenType::Bool)
671                             }
672                         }
673                         'f' => {
674                             if self.get_match("false", ",)") {
675                                 token_data.start(&self, TokenType::Bool)
676                             }
677                         }
678                         _ => {}
679                     };
680                     if token_data.token_type == TokenType::None {
681                         return Err(PrefReaderError::new(
682                             "Unexpected characters in function argument",
683                             position,
684                             None,
685                         ));
686                     } else {
687                         token_data.start_pos = start_pos;
688                         token_data.position = position;
689                         token_data.end(&self.data, self.pos + 1)?;
690                         self.next_state = Some(TokenizerState::AfterFunctionArg);
691                         TokenizerState::Junk
692                     }
693                 }
694                 TokenizerState::AfterFunctionArg => match c {
695                     ',' => {
696                         token_data.start(&self, TokenType::Comma);
697                         token_data.end(&self.data, self.pos + 1)?;
698                         self.next_state = Some(TokenizerState::FunctionArg);
699                         TokenizerState::Junk
700                     }
701                     ')' => {
702                         token_data.start(&self, TokenType::Paren);
703                         token_data.end(&self.data, self.pos + 1)?;
704                         self.next_state = Some(TokenizerState::AfterFunction);
705                         TokenizerState::Junk
706                     }
707                     _ => {
708                         return Err(PrefReaderError::new(
709                             "Unexpected character after function argument",
710                             self.position,
711                             None,
712                         ))
713                     }
714                 },
715                 TokenizerState::AfterFunction => match c {
716                     ';' => {
717                         token_data.start(&self, TokenType::Semicolon);
718                         token_data.end(&self.data, self.pos)?;
719                         self.next_state = Some(TokenizerState::FunctionName);
720                         TokenizerState::Junk
721                     }
722                     _ => {
723                         return Err(PrefReaderError::new(
724                             "Unexpected character after function",
725                             self.position,
726                             None,
727                         ))
728                     }
729                 },
730                 TokenizerState::Error => TokenizerState::Error,
731             };
732             if token_data.complete {
733                 return Ok(Some(token_data));
734             }
735         }
736     }
737 }
738 
739 impl<'a> Iterator for PrefTokenizer<'a> {
740     type Item = PrefToken<'a>;
741 
next(&mut self) -> Option<PrefToken<'a>>742     fn next(&mut self) -> Option<PrefToken<'a>> {
743         if let TokenizerState::Error = self.state {
744             return None;
745         }
746         let token_data = match self.next_token() {
747             Err(e) => {
748                 self.state = TokenizerState::Error;
749                 return Some(PrefToken::Error(e.message, e.position));
750             }
751             Ok(Some(token_data)) => token_data,
752             Ok(None) => return None,
753         };
754         let token = self.make_token(token_data);
755         Some(token)
756     }
757 }
758 
tokenize(data: &[u8]) -> PrefTokenizer759 pub fn tokenize(data: &[u8]) -> PrefTokenizer {
760     PrefTokenizer::new(data)
761 }
762 
serialize_token<T: Write>(token: &PrefToken, output: &mut T) -> Result<(), PrefReaderError>763 pub fn serialize_token<T: Write>(token: &PrefToken, output: &mut T) -> Result<(), PrefReaderError> {
764     let mut data_buf = String::new();
765 
766     let data = match *token {
767         PrefToken::PrefFunction(_) => "pref",
768         PrefToken::UserPrefFunction(_) => "user_pref",
769         PrefToken::StickyPrefFunction(_) => "sticky_pref",
770         PrefToken::CommentBlock(ref data, _) => {
771             data_buf.reserve(data.len() + 4);
772             data_buf.push_str("/*");
773             data_buf.push_str(data.borrow());
774             data_buf.push('*');
775             &*data_buf
776         }
777         PrefToken::CommentLine(ref data, _) => {
778             data_buf.reserve(data.len() + 2);
779             data_buf.push_str("//");
780             data_buf.push_str(data.borrow());
781             &*data_buf
782         }
783         PrefToken::CommentBashLine(ref data, _) => {
784             data_buf.reserve(data.len() + 1);
785             data_buf.push('#');
786             data_buf.push_str(data.borrow());
787             &*data_buf
788         }
789         PrefToken::Paren(data, _) => {
790             data_buf.push(data);
791             &*data_buf
792         }
793         PrefToken::Comma(_) => ",",
794         PrefToken::Semicolon(_) => ";\n",
795         PrefToken::String(ref data, _) => {
796             data_buf.reserve(data.len() + 2);
797             data_buf.push('"');
798             data_buf.push_str(escape_quote(data.borrow()).borrow());
799             data_buf.push('"');
800             &*data_buf
801         }
802         PrefToken::Int(data, _) => {
803             data_buf.push_str(&*data.to_string());
804             &*data_buf
805         }
806         PrefToken::Bool(data, _) => {
807             if data {
808                 "true"
809             } else {
810                 "false"
811             }
812         }
813         PrefToken::Error(data, pos) => return Err(PrefReaderError::new(data, pos, None)),
814     };
815     output.write_all(data.as_bytes())?;
816     Ok(())
817 }
818 
serialize_tokens<'a, I, W>(tokens: I, output: &mut W) -> Result<(), PrefReaderError> where I: Iterator<Item = &'a PrefToken<'a>>, W: Write,819 pub fn serialize_tokens<'a, I, W>(tokens: I, output: &mut W) -> Result<(), PrefReaderError>
820 where
821     I: Iterator<Item = &'a PrefToken<'a>>,
822     W: Write,
823 {
824     for token in tokens {
825         serialize_token(token, output)?;
826     }
827     Ok(())
828 }
829 
escape_quote(data: &str) -> Cow<str>830 fn escape_quote(data: &str) -> Cow<str> {
831     // Not very efficient…
832     if data.contains('"') || data.contains('\\') {
833         Cow::Owned(data.replace(r#"\"#, r#"\\"#).replace(r#"""#, r#"\""#))
834     } else {
835         Cow::Borrowed(data)
836     }
837 }
838 
839 #[derive(Debug, PartialEq)]
840 enum ParserState {
841     Function,
842     Key,
843     Value,
844 }
845 
846 struct PrefBuilder {
847     key: Option<String>,
848     value: Option<PrefValue>,
849     sticky: bool,
850 }
851 
852 impl PrefBuilder {
new() -> PrefBuilder853     fn new() -> PrefBuilder {
854         PrefBuilder {
855             key: None,
856             value: None,
857             sticky: false,
858         }
859     }
860 }
861 
skip_comments<'a>(tokenizer: &mut PrefTokenizer<'a>) -> Option<PrefToken<'a>>862 fn skip_comments<'a>(tokenizer: &mut PrefTokenizer<'a>) -> Option<PrefToken<'a>> {
863     loop {
864         match tokenizer.next() {
865             Some(PrefToken::CommentBashLine(_, _))
866             | Some(PrefToken::CommentBlock(_, _))
867             | Some(PrefToken::CommentLine(_, _)) => {}
868             Some(x) => return Some(x),
869             None => return None,
870         }
871     }
872 }
873 
parse_tokens(tokenizer: &mut PrefTokenizer<'_>) -> Result<Preferences, PrefReaderError>874 pub fn parse_tokens(tokenizer: &mut PrefTokenizer<'_>) -> Result<Preferences, PrefReaderError> {
875     let mut state = ParserState::Function;
876     let mut current_pref = PrefBuilder::new();
877     let mut rv = Preferences::new();
878 
879     loop {
880         // Not just using a for loop here seems strange, but this restricts the
881         // scope of the borrow
882         let token = {
883             match tokenizer.next() {
884                 Some(x) => x,
885                 None => break,
886             }
887         };
888         // First deal with comments and errors
889         match token {
890             PrefToken::Error(msg, position) => {
891                 return Err(PrefReaderError::new(msg, position, None));
892             }
893             PrefToken::CommentBashLine(_, _)
894             | PrefToken::CommentLine(_, _)
895             | PrefToken::CommentBlock(_, _) => continue,
896             _ => {}
897         }
898         state = match state {
899             ParserState::Function => {
900                 match token {
901                     PrefToken::PrefFunction(_) => {
902                         current_pref.sticky = false;
903                     }
904                     PrefToken::UserPrefFunction(_) => {
905                         current_pref.sticky = false;
906                     }
907                     PrefToken::StickyPrefFunction(_) => {
908                         current_pref.sticky = true;
909                     }
910                     _ => {
911                         return Err(PrefReaderError::new(
912                             "Expected pref function",
913                             token.position(),
914                             None,
915                         ));
916                     }
917                 }
918                 let next = skip_comments(tokenizer);
919                 match next {
920                     Some(PrefToken::Paren('(', _)) => ParserState::Key,
921                     _ => {
922                         return Err(PrefReaderError::new(
923                             "Expected open paren",
924                             next.map(|x| x.position()).unwrap_or(tokenizer.position),
925                             None,
926                         ))
927                     }
928                 }
929             }
930             ParserState::Key => {
931                 match token {
932                     PrefToken::String(data, _) => current_pref.key = Some(data.into_owned()),
933                     _ => {
934                         return Err(PrefReaderError::new(
935                             "Expected string",
936                             token.position(),
937                             None,
938                         ));
939                     }
940                 }
941                 let next = skip_comments(tokenizer);
942                 match next {
943                     Some(PrefToken::Comma(_)) => ParserState::Value,
944                     _ => {
945                         return Err(PrefReaderError::new(
946                             "Expected comma",
947                             next.map(|x| x.position()).unwrap_or(tokenizer.position),
948                             None,
949                         ))
950                     }
951                 }
952             }
953             ParserState::Value => {
954                 match token {
955                     PrefToken::String(data, _) => {
956                         current_pref.value = Some(PrefValue::String(data.into_owned()))
957                     }
958                     PrefToken::Int(data, _) => current_pref.value = Some(PrefValue::Int(data)),
959                     PrefToken::Bool(data, _) => current_pref.value = Some(PrefValue::Bool(data)),
960                     _ => {
961                         return Err(PrefReaderError::new(
962                             "Expected value",
963                             token.position(),
964                             None,
965                         ))
966                     }
967                 }
968                 let next = skip_comments(tokenizer);
969                 match next {
970                     Some(PrefToken::Paren(')', _)) => {}
971                     _ => {
972                         return Err(PrefReaderError::new(
973                             "Expected close paren",
974                             next.map(|x| x.position()).unwrap_or(tokenizer.position),
975                             None,
976                         ))
977                     }
978                 }
979                 let next = skip_comments(tokenizer);
980                 match next {
981                     Some(PrefToken::Semicolon(_)) | None => {}
982                     _ => {
983                         return Err(PrefReaderError::new(
984                             "Expected semicolon",
985                             next.map(|x| x.position()).unwrap_or(tokenizer.position),
986                             None,
987                         ))
988                     }
989                 }
990                 let key = mem::replace(&mut current_pref.key, None);
991                 let value = mem::replace(&mut current_pref.value, None);
992                 let pref = if current_pref.sticky {
993                     Pref::new_sticky(value.unwrap())
994                 } else {
995                     Pref::new(value.unwrap())
996                 };
997                 rv.insert(key.unwrap(), pref);
998                 current_pref.sticky = false;
999                 ParserState::Function
1000             }
1001         }
1002     }
1003     match state {
1004         ParserState::Key | ParserState::Value => {
1005             return Err(PrefReaderError::new(
1006                 "EOF in middle of function",
1007                 tokenizer.position,
1008                 None,
1009             ));
1010         }
1011         _ => {}
1012     }
1013     Ok(rv)
1014 }
1015 
serialize<W: Write>(prefs: &Preferences, output: &mut W) -> io::Result<()>1016 pub fn serialize<W: Write>(prefs: &Preferences, output: &mut W) -> io::Result<()> {
1017     let mut p: Vec<_> = prefs.iter().collect();
1018     p.sort_by(|a, b| a.0.cmp(&b.0));
1019     for &(key, pref) in &p {
1020         let func = if pref.sticky {
1021             "sticky_pref("
1022         } else {
1023             "user_pref("
1024         }
1025         .as_bytes();
1026         output.write_all(func)?;
1027         output.write_all(b"\"")?;
1028         output.write_all(escape_quote(key).as_bytes())?;
1029         output.write_all(b"\"")?;
1030         output.write_all(b", ")?;
1031         match pref.value {
1032             PrefValue::Bool(x) => {
1033                 output.write_all(if x { b"true" } else { b"false" })?;
1034             }
1035             PrefValue::Int(x) => {
1036                 output.write_all(x.to_string().as_bytes())?;
1037             }
1038             PrefValue::String(ref x) => {
1039                 output.write_all(b"\"")?;
1040                 output.write_all(escape_quote(x).as_bytes())?;
1041                 output.write_all(b"\"")?;
1042             }
1043         };
1044         output.write_all(b");\n")?;
1045     }
1046     Ok(())
1047 }
1048 
parse(data: &[u8]) -> Result<Preferences, PrefReaderError>1049 pub fn parse(data: &[u8]) -> Result<Preferences, PrefReaderError> {
1050     let mut tokenizer = tokenize(data);
1051     parse_tokens(&mut tokenizer)
1052 }
1053