1// Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "brillo/key_value_store.h"
6
7#include <map>
8#include <string>
9#include <vector>
10
11#include <base/files/file_util.h>
12#include <base/files/important_file_writer.h>
13#include <base/strings/string_split.h>
14#include <base/strings/string_util.h>
15#include <brillo/strings/string_utils.h>
16#include <brillo/map_utils.h>
17
18using std::map;
19using std::string;
20using std::vector;
21
22namespace brillo {
23
24namespace {
25
26// Values used for booleans.
27const char kTrueValue[] = "true";
28const char kFalseValue[] = "false";
29
30// Returns a copy of |key| with leading and trailing whitespace removed.
31string TrimKey(const string& key) {
32  string trimmed_key;
33  base::TrimWhitespaceASCII(key, base::TRIM_ALL, &trimmed_key);
34  CHECK(!trimmed_key.empty());
35  return trimmed_key;
36}
37
38}  // namespace
39
40bool KeyValueStore::Load(const base::FilePath& path) {
41  string file_data;
42  if (!base::ReadFileToString(path, &file_data))
43    return false;
44  return LoadFromString(file_data);
45}
46
47bool KeyValueStore::LoadFromString(const std::string& data) {
48  // Split along '\n', then along '='.
49  vector<string> lines = base::SplitString(data, "\n", base::KEEP_WHITESPACE,
50                                           base::SPLIT_WANT_ALL);
51  for (auto it = lines.begin(); it != lines.end(); ++it) {
52    std::string line;
53    base::TrimWhitespaceASCII(*it, base::TRIM_LEADING, &line);
54    if (line.empty() || line.front() == '#')
55      continue;
56
57    std::string key;
58    std::string value;
59    if (!string_utils::SplitAtFirst(line, "=", &key, &value, false))
60      return false;
61
62    base::TrimWhitespaceASCII(key, base::TRIM_TRAILING, &key);
63    if (key.empty())
64      return false;
65
66    // Append additional lines to the value as long as we see trailing
67    // backslashes.
68    while (!value.empty() && value.back() == '\\') {
69      ++it;
70      if (it == lines.end() || it->empty())
71        return false;
72      value.pop_back();
73      value += *it;
74    }
75
76    store_[key] = value;
77  }
78  return true;
79}
80
81bool KeyValueStore::Save(const base::FilePath& path) const {
82  return base::ImportantFileWriter::WriteFileAtomically(path, SaveToString());
83}
84
85string KeyValueStore::SaveToString() const {
86  string data;
87  for (const auto& key_value : store_)
88    data += key_value.first + "=" + key_value.second + "\n";
89  return data;
90}
91
92bool KeyValueStore::GetString(const string& key, string* value) const {
93  const auto key_value = store_.find(TrimKey(key));
94  if (key_value == store_.end())
95    return false;
96  *value = key_value->second;
97  return true;
98}
99
100void KeyValueStore::SetString(const string& key, const string& value) {
101  store_[TrimKey(key)] = value;
102}
103
104bool KeyValueStore::GetBoolean(const string& key, bool* value) const {
105  string string_value;
106  if (!GetString(key, &string_value))
107    return false;
108
109  if (string_value == kTrueValue) {
110    *value = true;
111    return true;
112  } else if (string_value == kFalseValue) {
113    *value = false;
114    return true;
115  }
116  return false;
117}
118
119void KeyValueStore::SetBoolean(const string& key, bool value) {
120  SetString(key, value ? kTrueValue : kFalseValue);
121}
122
123std::vector<std::string> KeyValueStore::GetKeys() const {
124  return GetMapKeysAsVector(store_);
125}
126
127}  // namespace brillo
128