kconfig_represent/
config_registry.rs

1/*
2 Cargo KConfig - KConfig parser
3 Copyright (C) 2022  Sjoerd van Leent
4
5--------------------------------------------------------------------------------
6
7Copyright Notice: Apache
8
9Licensed under the Apache License, Version 2.0 (the "License"); you may not use
10this file except in compliance with the License. You may obtain a copy of the
11License at
12
13   https://www.apache.org/licenses/LICENSE-2.0
14
15Unless required by applicable law or agreed to in writing, software distributed
16under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
17CONDITIONS OF ANY KIND, either express or implied. See the License for the
18specific language governing permissions and limitations under the License.
19
20--------------------------------------------------------------------------------
21
22Copyright Notice: GPLv2
23
24This program is free software: you can redistribute it and/or modify
25it under the terms of the GNU General Public License as published by
26the Free Software Foundation, either version 2 of the License, or
27(at your option) any later version.
28
29This program is distributed in the hope that it will be useful,
30but WITHOUT ANY WARRANTY; without even the implied warranty of
31MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
32GNU General Public License for more details.
33
34You should have received a copy of the GNU General Public License
35along with this program.  If not, see <https://www.gnu.org/licenses/>.
36
37--------------------------------------------------------------------------------
38
39Copyright Notice: MIT
40
41Permission is hereby granted, free of charge, to any person obtaining a copy of
42this software and associated documentation files (the “Software”), to deal in
43the Software without restriction, including without limitation the rights to
44use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
45the Software, and to permit persons to whom the Software is furnished to do so,
46subject to the following conditions:
47
48The above copyright notice and this permission notice shall be included in all
49copies or substantial portions of the Software.
50
51THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
52IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
53FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
54COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
55IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
56CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
57*/
58
59//! This file implements the registry, which is the main component of the Kconfig
60//! representation module.
61
62use crate::dependencies::ResolvedExpr;
63use crate::error::LoadError;
64use crate::item::ConfigItem;
65use crate::Tristate;
66use std::io::BufRead;
67use std::io::BufReader;
68use std::io::Lines;
69use std::io::Read;
70
71use crate::{
72    dependencies::{DependencyResolver, SelectAndImplyDependencyResolver},
73    error::Error,
74    eval::Evaluator,
75    eval::ExpensiveEvaluator,
76    item::MaybeIncompleteItemReference,
77    Ast, AstExtra, Info, Item, ItemReference, MutationStart, Symbol, Variant,
78};
79use std::collections::{HashMap, HashSet};
80use std::fs::OpenOptions;
81use std::io::Write;
82
83/// The ConfigRegistry is used to create a registry, which allows reading and writing
84/// a configuration to and from a .config file as defined by a Kconfig file. To
85/// present the menu, a frontend is necessary to represent the state of the registry.
86#[derive(Debug)]
87pub struct ConfigRegistry {
88    // An item can either be a configuration item, a menu item, or both (in the case
89    // of a "menuconfig" situation). The different items can be retrieved by a
90    // menu item descriptor, and resolved as such.
91    items: HashMap<ItemReference, Item>,
92
93    // Name of the main menu
94    main_menu_name: String,
95}
96
97impl ConfigRegistry {
98    /// Constructs a new configuration registry, given the Abstract syntax tree
99    /// as loaded from a kconfig-parser's Ast instance, and the macro symbols
100    /// discoverd by the macro lexer (if used).
101    pub fn new(ast: &Ast, macro_symbols: &HashSet<Symbol>) -> Result<Self, Error> {
102        let mut forward_dependency_resolver = SelectAndImplyDependencyResolver::new();
103        let mut main_menu = Self {
104            items: HashMap::new(),
105            main_menu_name: ast.main_menu_name_with_default(),
106        };
107
108        let items = Item::new_from_ast(
109            &main_menu.main_menu_name,
110            macro_symbols,
111            &ast,
112            &mut forward_dependency_resolver,
113        );
114
115        main_menu.items.extend(items.into_iter());
116
117        DependencyResolver::new(forward_dependency_resolver, macro_symbols.clone())
118            .resolve(&mut main_menu.items);
119
120        main_menu.reset()?;
121        Ok(main_menu)
122    }
123
124    /// Return a reference to the map of the items available in the configuration registry
125    pub fn items<'a>(&'a self) -> &'a HashMap<ItemReference, Item> {
126        &self.items
127    }
128
129    /// Returns the name of the main menu
130    pub fn main_menu_name<'a>(&'a self) -> &'a str {
131        &self.main_menu_name
132    }
133
134    /// Returns the item belonging to the main menu
135    pub fn main_menu_item<'a>(&'a self) -> &'a Item {
136        let itemref = ItemReference::Menu(self.main_menu_name.clone());
137        // The menu should be in the item map, otherwise something very strange is going on
138        self.get(&itemref).unwrap()
139    }
140
141    /// Finds the item given the item reference, if available, otherwise returns None
142    pub fn get<'a>(&'a self, itemref: &ItemReference) -> Option<&'a Item> {
143        self.items.get(itemref)
144    }
145
146    /// Returns an item's information, if available, otherwise returns None
147    pub fn get_info(&self, itemref: &ItemReference) -> Option<Info> {
148        self.get(itemref).map(|item| item.info())
149    }
150
151    /// Given the item reference, determine whether the associated item is enabled
152    pub fn item_enabled(&self, itemref: &ItemReference) -> Result<bool, Error> {
153        match self.items.get(itemref) {
154            Some(item) => {
155                let evaluator = Evaluator::new(&self.items);
156                let enabled_variant = evaluator.enabled(item);
157                enabled_variant.map(Variant::into)
158            }
159            None => Err(Error::from(format!(
160                "Could not find item associated to itemreference {itemref}"
161            ))),
162        }
163    }
164
165    fn cloned_int_current_config_item(
166        item: Item,
167        expensive_evaluator: &ExpensiveEvaluator,
168    ) -> Option<ConfigItem> {
169        let mut restricted_config_item: Option<&ConfigItem> = None;
170        let mut unrestricted_config_item: Option<&ConfigItem> = None;
171
172        for (_, config_item) in item.config_items() {
173            if let Some(expr) = config_item.maybe_dependencies() {
174                if let Ok(variant) = expensive_evaluator.evalexpr(expr.item_expr()) {
175                    let enabled: bool = variant.into();
176                    if enabled {
177                        restricted_config_item = Some(config_item);
178                    }
179                }
180            } else {
181                unrestricted_config_item = Some(config_item);
182            }
183        }
184
185        match (restricted_config_item, unrestricted_config_item) {
186            (Some(config_item), _) => Some(config_item.clone()),
187            (_, Some(config_item)) => Some(config_item.clone()),
188            _ => None,
189        }
190    }
191
192    fn int_current_config_item<'a>(&self, item: &'a Item) -> Option<&'a ConfigItem> {
193        let mut restricted_config_item: Option<&ConfigItem> = None;
194        let mut unrestricted_config_item: Option<&ConfigItem> = None;
195
196        for (_, config_item) in item.config_items() {
197            if let Some(expr) = config_item.maybe_dependencies() {
198                let evaluator = Evaluator::new(&self.items);
199                if let Ok(variant) = evaluator.evalexpr(expr.item_expr()) {
200                    let enabled: bool = variant.into();
201                    if enabled {
202                        restricted_config_item = Some(config_item);
203                    }
204                }
205            } else {
206                unrestricted_config_item = Some(config_item);
207            }
208        }
209
210        match (restricted_config_item, unrestricted_config_item) {
211            (Some(config_item), _) => Some(&config_item),
212            (_, Some(config_item)) => Some(&config_item),
213            _ => None,
214        }
215    }
216
217    /// Returns a clone of the active configuration item, if available
218    pub fn current_config_item(&self, itemref: &ItemReference) -> Option<ConfigItem> {
219        self.get(itemref)
220            .map(|item| self.int_current_config_item(item))
221            .map(|c| c.map(ConfigItem::clone))
222            .flatten()
223    }
224
225    /// Returns how to start a mutation. Given the configuration type, this can be
226    /// a different start situation.
227    pub fn mutation_start(&self, itemref: &ItemReference) -> Option<MutationStart> {
228        self.current_value(itemref).map(MutationStart::from)
229    }
230
231    /// The current value of an item is resolved according to a number of rules:
232    ///
233    /// If the value has been selected through a select reverse dependency, then
234    /// this value will be used, with respect to it's default value.
235    ///
236    /// If the value has a "regular" value, then this value will be used
237    ///
238    /// If the value has been selected through an implied value, then this
239    /// value will be used, with respect to it's default value.
240    ///
241    /// Otherwise, the default value will be used.
242    pub fn current_value(&self, itemref: &ItemReference) -> Option<Variant> {
243        Evaluator::new(&self.items).current_value(itemref)
244    }
245
246    /// Determines whether the value if the configuration item internal to the item
247    /// is set to it's default value.
248    pub fn current_value_is_default(&self, itemref: &ItemReference) -> bool {
249        let maybe_config_item = self.current_config_item(itemref);
250        maybe_config_item
251            .map(|config_item| match config_item.value() {
252                Some(_) => false,
253                None => true,
254            })
255            .unwrap_or(false)
256    }
257
258    fn int_set_value(&mut self, itemref: &ItemReference, value: Variant) -> Result<(), Error> {
259        // If the itemreference is of type menu, this is a nonsense situation
260        if let &ItemReference::Menu(_) = itemref {
261            return Err(Error::from(format!(
262                "Item reference {itemref} indicates a menu, which does not have values"
263            )));
264        }
265
266        // Get the item, if available
267        let item = match self.items.get_mut(itemref) {
268            // If no item is available, then an error should be raised
269            None => {
270                return Err(Error::from(format!(
271                    "Item for reference not found: {itemref}"
272                )))
273            }
274            Some(item) => item,
275        };
276
277        // Force the value of each configuration item within the current item
278        for (_, config_item) in item.config_items_mut() {
279            config_item.set_config_value(value.clone());
280        }
281
282        Ok(())
283    }
284
285    fn int_discard_value(&mut self, itemref: &ItemReference) -> Result<(), Error> {
286        // If the itemreference is of type menu, this is a nonsense situation
287        if let &ItemReference::Menu(_) = itemref {
288            return Err(Error::from(format!(
289                "Item reference {itemref} indicates a menu, which does not have values"
290            )));
291        }
292
293        // Get the item, if available
294        let item = match self.items.get_mut(itemref) {
295            // If no item is available, then an error should be raised
296            None => {
297                return Err(Error::from(format!(
298                    "Item for reference not found: {itemref}"
299                )))
300            }
301            Some(item) => item,
302        };
303
304        // Force the value of each configuration item within the current item
305        for (_, config_item) in item.config_items_mut() {
306            config_item.reset_value();
307        }
308
309        Ok(())
310    }
311
312    /// Sets the value of a configuration item either by loading it from an
313    /// earlier stored session, or by entering the data programmatically/through
314    /// a configuration editor. Returns nothing if successful, or an error if
315    /// for some reason the value could not be set properly.
316    pub fn set_value(&mut self, itemref: &ItemReference, value: Variant) -> Result<(), Error> {
317        self.int_set_value(itemref, value)?;
318        self.reset()
319    }
320
321    /// Unsets the value of a configuation item
322    pub fn discard_value(&mut self, itemref: &ItemReference) -> Result<(), Error> {
323        self.int_discard_value(itemref)?;
324        self.reset()
325    }
326
327    /// Finds the appropriate item reference by the name of the itemreference
328    pub fn find_itemref(&self, name: &str) -> Option<ItemReference> {
329        let key1 = ItemReference::Config(name.to_string());
330        let key2 = ItemReference::MenuConfig(name.to_string());
331        let key3 = ItemReference::Menu(name.to_string());
332        if self.items.contains_key(&key1) {
333            Some(key1)
334        } else if self.items.contains_key(&key2) {
335            Some(key2)
336        } else if self.items.contains_key(&key3) {
337            Some(key3)
338        } else {
339            None
340        }
341    }
342
343    /// Evaluates all items and enforces the proper rules on all internal configuration
344    /// items and dependent configuration items for all the items evaluated.
345    pub fn reset(&mut self) -> Result<(), Error> {
346        // Items are reset by evaluating them, from the top-most item, to the item with the
347        // most dependencies.
348
349        // First, collect all the item references, and the dependencies of those item references
350        let mut snapshots: HashMap<ItemReference, HashSet<ItemReference>> = HashMap::new();
351        for (itemref, item) in &self.items {
352            let maybe_incomplete_snapshot = item.collect_all_dependencies();
353            let snapshot = maybe_incomplete_snapshot
354                .iter()
355                .map(|iiref| match iiref {
356                    MaybeIncompleteItemReference::ItemReference(itemref) => itemref.clone(),
357                    MaybeIncompleteItemReference::String(s) => self
358                        .find_itemref(&s)
359                        .unwrap_or(ItemReference::Config(s.to_string())),
360                })
361                .collect();
362            snapshots.insert(itemref.clone(), snapshot);
363        }
364
365        // Resolve the item references which have no further dependencies, then resolve the
366        // next items, etc. until the list of snapshots is depleted. If the list of snapshots
367        // does not deplete any more, then there is a cyclic dependency, which is to result
368        // into an error.
369        while snapshots.len() > 0 {
370            match Self::take_available_snapshot(&snapshots) {
371                Some(itemref) => {
372                    self.reset_item_by_ref(itemref.clone());
373                    snapshots.remove(&itemref);
374                }
375                None => {
376                    if snapshots.len() > 0 {
377                        let keys: HashSet<ItemReference> = snapshots.keys().cloned().collect();
378                        let mut error_string = String::new();
379                        for key in keys {
380                            if error_string.len() > 0 {
381                                error_string += " ";
382                            }
383                            error_string += &key.to_string();
384                        }
385                        return Err(Error::from(format!(
386                            "Dependency cycle(s) detected in items {error_string}"
387                        )));
388                    }
389                }
390            }
391        }
392
393        Ok(())
394    }
395
396    fn reset_item_by_ref(&mut self, itemref: ItemReference) {
397        let map: HashMap<ItemReference, Item> = self
398            .items
399            .iter()
400            .map(|(itemref, item)| (itemref.clone(), item.clone()))
401            .collect();
402
403        if let Some(item) = self.items.get_mut(&itemref) {
404            // For the item:
405            // Determine if the item is enabled, an item is only enabled if all the
406            // item dependencies evaluate true.
407            //
408            // Determine which current configuration item is active, all other configuration
409            // will be defaulted.
410            // - Determine if the value of the current configuration item is
411            //   selected and/or implied, and resolve the choices
412            // - Determine if the value has a default value matching the configured value,
413            //   if so, erase the configured value
414            // - If no default value matches the configured value, set the configured value
415
416            let evaluator = ExpensiveEvaluator::new(map);
417            let enabled_variant = evaluator.enabled(item);
418            let is_enabled = enabled_variant.map(Variant::into).unwrap_or(false);
419
420            // If the item is not enabled, all the configuration values set by externally
421            // are to be discarded.
422            if !is_enabled {
423                for value in item.config_items_mut().values_mut() {
424                    value.reset_all();
425                }
426            } else {
427                if let Some(current_config_item) =
428                    Self::cloned_int_current_config_item(item.clone(), &evaluator)
429                {
430                    let selects_me = item.selects_me();
431                    let imply_me = item.imply_me();
432
433                    for (_, config_item) in item.config_items_mut() {
434                        let is_current = config_item == &current_config_item;
435                        let default_value =
436                            evaluator.config_item_default_value(&current_config_item);
437                        match config_item.choice_mode() {
438                            None => {
439                                // If the choice mode is none, then the value can not be set by
440                                // the select and/or imply methods, therefore, it is only verified
441                                // against it's default value. If it is the same as the default
442                                // value, the value is to be reset, otherwise it is to be set.
443                                if is_current {
444                                    if let Some(value) = config_item.config_value() {
445                                        if value != default_value {
446                                            config_item.set_value(value);
447                                        } else {
448                                            config_item.reset_value();
449                                        }
450                                    } else {
451                                        config_item.reset_value();
452                                    }
453                                    config_item.reset_config_value()
454                                } else {
455                                    config_item.reset_all()
456                                }
457                            }
458                            Some(_) => {
459                                if is_current {
460                                    let maybe_selects_value =
461                                        Self::get_highest_reverse_dependency_value(
462                                            &evaluator,
463                                            &selects_me,
464                                        );
465                                    let maybe_implies_value =
466                                        Self::get_highest_reverse_dependency_value(
467                                            &evaluator, &imply_me,
468                                        );
469
470                                    // If a value is selected, then the selected value is the minimum value
471                                    // which can be used by the item. Thus, if the value of the selecting
472                                    // object is m, then the minimum value of the selected value is also m,
473                                    // any value lower than m, is in error.
474
475                                    // If a value is implied, then the same rules apply, just only to the
476                                    // default value. The value can however be overridden completely.
477
478                                    match (maybe_selects_value, maybe_implies_value) {
479                                        (Some(selects_value), _) => {
480                                            // Though the item is selected, the value is that of the implication,
481                                            // if the implication is higher.
482                                            config_item.set_choice_mode(
483                                                true,
484                                                selects_value.clone().into(),
485                                            );
486
487                                            // if the config value > the select value, then retain the
488                                            // choice value, otherwise, remove it.
489                                            if let Some(config_value) = config_item.config_value() {
490                                                if config_value < selects_value {
491                                                    config_item.reset_value();
492                                                } else {
493                                                    let default_value = evaluator
494                                                        .config_item_default_value(config_item);
495                                                    if config_value != default_value {
496                                                        config_item.set_value(config_value);
497                                                    } else {
498                                                        config_item.reset_value();
499                                                    }
500                                                }
501                                            }
502                                            config_item.reset_config_value();
503                                        }
504                                        (_, Some(implies_value)) => {
505                                            // If a value is explicitly set, and is not equal to the lower bound nor the
506                                            // default value, then set the value explicitly.
507                                            let default_value =
508                                                evaluator.config_item_default_value(config_item);
509
510                                            config_item.set_choice_mode(
511                                                false,
512                                                implies_value.clone().into(),
513                                            );
514
515                                            if let Some(config_value) = config_item.config_value() {
516                                                if config_value != default_value
517                                                    && config_value != implies_value
518                                                {
519                                                    config_item.set_value(config_value);
520                                                } else {
521                                                    config_item.reset_value();
522                                                }
523                                            }
524                                            config_item.reset_config_value();
525                                        }
526                                        (_, None) => {
527                                            let default_value =
528                                                evaluator.config_item_default_value(config_item);
529                                            config_item.set_choice_mode(false, Tristate::FALSE);
530                                            if let Some(config_value) = config_item.config_value() {
531                                                if config_value != default_value {
532                                                    config_item.set_value(config_value);
533                                                } else {
534                                                    config_item.reset_value();
535                                                }
536                                            }
537                                            config_item.reset_config_value();
538                                        }
539                                    }
540                                } else {
541                                    config_item.reset_all()
542                                }
543                            }
544                        }
545                    }
546                }
547            }
548        }
549    }
550
551    fn take_available_snapshot(
552        snapshots: &HashMap<ItemReference, HashSet<ItemReference>>,
553    ) -> Option<ItemReference> {
554        let keys: HashSet<ItemReference> = snapshots.keys().cloned().collect();
555        for (snapshot_itemref, snapshot_dependencies) in snapshots {
556            let mut resolved = true;
557            for dep in snapshot_dependencies {
558                if keys.contains(dep) {
559                    resolved = false;
560                    break;
561                }
562            }
563            if resolved {
564                return Some(snapshot_itemref.clone());
565            }
566        }
567        None
568    }
569
570    fn get_highest_reverse_dependency_value(
571        evaluator: &ExpensiveEvaluator,
572        revdeps: &HashMap<String, Option<ResolvedExpr>>,
573    ) -> Option<Variant> {
574        let itemrefs = evaluator.dependency_itemrefs(revdeps);
575
576        if itemrefs.len() > 0 {
577            let mut highest_value = Variant::from(false);
578            for itemref in itemrefs {
579                let value = evaluator.current_value(&itemref);
580                if let Some(value) = value {
581                    if value > highest_value {
582                        highest_value = value;
583                    }
584                }
585            }
586            Some(highest_value)
587        } else {
588            None
589        }
590    }
591
592    /// Writes the current configuration known to the configuration registry
593    /// into a file, typically this file has the file name ".config"
594    pub fn write_dotconfig_file(&self, dotconfig_filename: &str) -> Result<(), Error> {
595        let mut f = OpenOptions::new()
596            .read(true)
597            .write(true)
598            .create(true)
599            .truncate(true)
600            .open(dotconfig_filename)?;
601        f.write("#\n".as_bytes())?;
602        f.write("# Automatically generated file; DO NOT EDIT\n".as_bytes())?;
603        match self.write_dotconfig(&mut f) {
604            Ok(_) => Ok(()),
605            Err(e) => Err(Error::from(format!(
606                "Saving configuration to: {} failed - {}",
607                dotconfig_filename, e
608            ))),
609        }
610    }
611
612    /// Writes the current configuration known to the configuration registry
613    /// into a Write trait supporting type.
614    pub fn write_dotconfig(&self, output: &mut dyn Write) -> Result<(), Error> {
615        output.write("# ".as_bytes())?;
616        output.write(self.main_menu_name.to_string().as_bytes())?;
617        output.write("\n#\n\n".as_bytes())?;
618        for (itemref, _) in self.items() {
619            match itemref {
620                ItemReference::Config(name) | ItemReference::MenuConfig(name) => {
621                    match self.current_value(itemref) {
622                        Some(value) => {
623                            output.write(name.to_string().as_bytes())?;
624                            output.write("=".as_bytes())?;
625                            output.write(value.dot_config().as_bytes())?;
626                        }
627                        None => {
628                            output.write("#".as_bytes())?;
629                            output.write(name.as_bytes())?;
630                            output.write("=is not set".as_bytes())?;
631                        }
632                    };
633                    output.write("\n".as_bytes())?;
634                }
635                _ => (),
636            }
637        }
638
639        Ok(())
640    }
641
642    /// Reads the saved configuration from a file, typically this file has
643    /// the file name ".config", and mutates the configuration registry
644    /// accordingly.
645    pub fn read_dotconfig_file(&mut self, dotconfig_filename: &str) -> Result<(), LoadError> {
646        let mut f = match OpenOptions::new().read(true).open(dotconfig_filename) {
647            Ok(f) => f,
648            Err(e) => {
649                return Err(LoadError::from(Error::from(format!(
650                    "⛔ Can not read {} - {}",
651                    dotconfig_filename, e
652                ))));
653            }
654        };
655
656        self.read_dotconfig(&mut f)
657    }
658
659    /// Reads the saved configuration from a Read trait supporting type,
660    /// and mutates the configuration registry accordingly.
661    pub fn read_dotconfig(&mut self, input: &mut dyn Read) -> Result<(), LoadError> {
662        let reader = BufReader::new(input);
663        let lines = reader.lines();
664        for (_, item) in self.items.iter_mut() {
665            for (_, config_item) in item.config_items_mut() {
666                config_item.reset_value();
667            }
668        }
669        let result = self.read_dotconfig_by_line(lines);
670        result
671    }
672
673    fn read_dotconfig_by_line(
674        &mut self,
675        lines: Lines<BufReader<&mut dyn Read>>,
676    ) -> Result<(), LoadError> {
677        let mut invalid_lines: Vec<(String, String)> = Vec::new();
678
679        for line in lines {
680            // Get the actual content of the line or return an error
681            let line = line?;
682
683            // Trim the line such that it begins with a valid character
684            let line = line.trim();
685
686            // If the line is empty, skip it
687            if line.is_empty() {
688                continue;
689            }
690
691            // If the line starts with a pund/hash symbol, it is a comment
692            // skip it.
693            if line.starts_with('#') {
694                continue;
695            }
696
697            // LHS receives the name of the descriptor and RHS receives
698            // the actual value.
699            let mut lhs = String::new();
700            let mut rhs = String::new();
701            let mut invalid = false;
702            let mut finished = false;
703            let mut in_string = false;
704            let mut escape = false;
705
706            // Altough reading a line might fail, the process will continue
707            // and simply skip the current value.
708            #[derive(Debug)]
709            enum MachineState {
710                Start,
711                Lhs,
712                Assign,
713                StartRhs,
714                Rhs,
715                End,
716            }
717
718            let mut state = MachineState::Start;
719            for c in line.chars() {
720                if !invalid && !finished {
721                    match state {
722                        MachineState::Start => {
723                            if !c.is_whitespace() && c != '=' && c != '#' {
724                                state = MachineState::Lhs;
725                                lhs.push(c);
726                            } else if c.is_whitespace() {
727                                // Skip the current value
728                            } else {
729                                // This line is illegal
730                                invalid = true;
731                            }
732                        }
733                        MachineState::Lhs => {
734                            if !c.is_whitespace() && c != '=' {
735                                lhs.push(c);
736                            } else if c.is_whitespace() {
737                                state = MachineState::Assign;
738                            } else if c == '=' {
739                                state = MachineState::StartRhs;
740                            } else {
741                                invalid = true;
742                            }
743                        }
744                        MachineState::Assign => {
745                            if c.is_whitespace() {
746                                // Skip the current value
747                            } else if c == '=' {
748                                state = MachineState::StartRhs;
749                            } else {
750                                invalid = true;
751                            }
752                        }
753                        MachineState::StartRhs => {
754                            if !c.is_whitespace() && c != '#' {
755                                state = MachineState::Rhs;
756                                if c == '"' {
757                                    in_string = true;
758                                }
759                                rhs.push(c);
760                            } else if c.is_whitespace() {
761                                // Skip the current value
762                            } else {
763                                // The value is empty
764                                finished = true;
765                            }
766                        }
767                        MachineState::Rhs => {
768                            if c == '"' {
769                                if in_string && !escape {
770                                    in_string = false;
771                                    rhs.push(c);
772                                } else if in_string && escape {
773                                    rhs.push(c);
774                                } else if !in_string {
775                                    in_string = true;
776                                    rhs.push(c);
777                                } else {
778                                    invalid = true;
779                                }
780                            } else if c == '\\' {
781                                if escape && in_string {
782                                    rhs.push(c);
783                                    escape = false;
784                                } else if in_string {
785                                    escape = true;
786                                } else {
787                                    rhs.push(c);
788                                }
789                            } else if !c.is_whitespace() && !in_string && c != '#' || in_string {
790                                rhs.push(c);
791                            } else if c.is_whitespace() {
792                                state = MachineState::End;
793                            } else if c == '#' {
794                                finished = true;
795                            } else {
796                                invalid = true;
797                            }
798                        }
799                        MachineState::End => {
800                            if c.is_whitespace() {
801                                // Ignore
802                            } else if c == '#' {
803                                finished = true;
804                            } else {
805                                invalid = true;
806                            }
807                        }
808                    }
809                }
810            }
811
812            if !escape && !invalid {
813                let value = Variant::from_dot_config(&rhs);
814                match self.find_itemref(&lhs) {
815                    Some(itemref) => match self.int_set_value(&itemref, value) {
816                        Ok(_) => (),
817                        Err(e) => invalid_lines.push((e.to_string(), line.to_string())),
818                    },
819                    None => {
820                        invalid_lines
821                            .push((format!("Invalid or unknown item: {lhs}"), line.to_string()));
822                    }
823                }
824            } else {
825                invalid_lines.push(("Invalid syntax".to_owned(), line.to_string()));
826            }
827        }
828
829        self.reset()?;
830
831        match invalid_lines.is_empty() {
832            true => Ok(()),
833            false => Err(LoadError::from(Error::from(invalid_lines))),
834        }
835    }
836}