The Battle for Wesnoth  1.17.21+dev
persist_context.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2010 - 2023
3  by Jody Northup
4  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5 
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY.
12 
13  See the COPYING file for more details.
14 */
15 
16 #include "filesystem.hpp"
17 #include "lexical_cast.hpp"
18 #include "log.hpp"
19 #include "persist_context.hpp"
20 #include "persist_manager.hpp"
22 #include "serialization/parser.hpp"
23 
24 config pack_scalar(const std::string &name, const t_string &val)
25 {
26  config cfg;
27  cfg[name] = val;
28  return cfg;
29 }
30 
31 static std::string get_persist_cfg_name(const std::string &name_space) {
32  return (filesystem::get_dir(filesystem::get_user_data_dir() + "/persist/") + name_space + ".cfg");
33 }
34 
36 {
37  std::string cfg_name = get_persist_cfg_name(namespace_.root_);
38  if (filesystem::file_exists(cfg_name) && !filesystem::is_directory(cfg_name)) {
40  if (!(file_stream->fail())) {
41  try {
42  read(cfg_,*file_stream);
43  } catch (const config::error &err) {
44  LOG_PERSIST << err.message;
45  }
46  }
47  }
48 }
49 
52 {
53  load();
54 }
55 
56 bool persist_file_context::clear_var(const std::string &global, bool immediate)
57 {
58  config bak;
59  config bactive;
60  if (immediate) {
61  bak = cfg_;
62  config *node = get_node(bak, namespace_);
63  if (node)
64  bactive = node->child_or_add("variables");
65  load();
66  }
67  config *active = get_node(cfg_, namespace_);
68  if (active == nullptr)
69  return false;
70 
71  bool ret = active->has_child("variables");
72  if (ret) {
73  config &cfg = active->mandatory_child("variables");
74  bool exists = cfg.has_attribute(global);
75  if (!exists) {
76  if (cfg.has_child(global)) {
77  exists = true;
78  std::string::const_iterator index_start = std::find(global.begin(),global.end(),'[');
79  if (index_start != global.end())
80  {
81  const std::string::const_iterator index_end = std::find(global.begin(),global.end(),']');
82  const std::string index_str(index_start+1,index_end);
83  std::size_t index = static_cast<std::size_t>(lexical_cast_default<int>(index_str));
84  cfg.remove_child(global,index);
85  if (immediate) bactive.remove_child(global,index);
86  } else {
87  cfg.clear_children(global);
88  if (immediate) bactive.clear_children(global);
89  }
90  }
91  }
92  if (exists) {
93  cfg.remove_attribute(global);
94  if (immediate) bactive.remove_attribute(global);
95  if (cfg.empty()) {
96  active->clear_children("variables");
97  active->remove_attribute("variables");
98  name_space working = namespace_;
99  while ((active->empty()) && (!working.lineage_.empty())) {
100  name_space prev = working.prev();
101  active = get_node(cfg_, prev);
102  active->clear_children(working.node_);
103  if (active->has_child("variables") && active->mandatory_child("variables").empty()) {
104  active->clear_children("variables");
105  active->remove_attribute("variables");
106  }
107  working = prev;
108  }
109  }
110 
111  if (!in_transaction_)
112  ret = save_context();
113  else if (immediate) {
114  ret = save_context();
115  cfg_ = bak;
116  active = get_node(cfg_, namespace_);
117  if (active != nullptr) {
118  active->clear_children("variables");
119  active->remove_attribute("variables");
120  if (!bactive.empty())
121  active->add_child("variables",bactive);
122  }
123  } else {
124  ret = true;
125  }
126  } else {
127  if (immediate) {
128  cfg_ = bak;
129  active = get_node(cfg_, namespace_);
130  if (active != nullptr) {
131  active->clear_children("variables");
132  active->remove_attribute("variables");
133  if (!bactive.empty())
134  active->add_child("variables", bactive);
135  }
136  }
137  ret = exists;
138  }
139  }
140 
141  // TODO: figure out when this is the case and adjust the next loop
142  // condition accordingly. -- shadowm
143  assert(active);
144 
145  while (active && active->empty() && !namespace_.lineage_.empty()) {
147  active = get_node(cfg_, prev);
148  if (active == nullptr) {
149  break;
150  }
152  if (active->has_child("variables") && active->mandatory_child("variables").empty()) {
153  active->clear_children("variables");
154  active->remove_attribute("variables");
155  }
156  namespace_ = prev;
157  }
158  return ret;
159 }
160 
161 config persist_file_context::get_var(const std::string &global) const
162 {
163  config ret;
164  const config *active = get_node(cfg_, namespace_);
165  if (active && (active->has_child("variables"))) {
166  const config &cfg = active->mandatory_child("variables");
167  std::size_t arrsize = cfg.child_count(global);
168  if (arrsize > 0) {
169  for (std::size_t i = 0; i < arrsize; i++)
170  ret.add_child(global, cfg.mandatory_child(global,i));
171  } else {
172  ret = pack_scalar(global,cfg[global]);
173  }
174  } else {
175  ret = pack_scalar(global,"");
176  }
177  return ret;
178 }
180  bool success = false;
181 
182  std::string cfg_name = get_persist_cfg_name(namespace_.root_);
183  if (!cfg_name.empty()) {
184  if (cfg_.empty()) {
185  success = filesystem::delete_file(cfg_name);
186  } else {
188  if (!out->fail())
189  {
190  config_writer writer(*out,false);
191  try {
192  writer.write(cfg_);
193  success = true;
194  } catch(config::error &err) {
195  LOG_PERSIST << err.message;
196  success = false;
197  }
198  }
199  }
200  }
201  return success;
202 }
203 bool persist_file_context::set_var(const std::string &global,const config &val, bool immediate)
204 {
205  config bak;
206  config bactive;
207  if (immediate) {
208  bak = cfg_;
209  bactive = get_node(bak, namespace_, true)->child_or_add("variables");
210  load();
211  }
212 
213  config *active = get_node(cfg_, namespace_, true);
214  config &cfg = active->child_or_add("variables");
215  if (val.has_attribute(global)) {
216  if (val[global].empty()) {
217  clear_var(global,immediate);
218  } else {
219  cfg[global] = val[global];
220  if (immediate) bactive[global] = val[global];
221  }
222  } else {
223  cfg.clear_children(global);
224  cfg.append(val);
225  if (immediate) {
226  bactive.clear_children(global);
227  bactive.append(val);
228  }
229  }
230 // dirty_ = true;
231  if (!in_transaction_)
232  return save_context();
233  else if (immediate) {
234  bool ret = save_context();
235  cfg_ = bak;
236  active = get_node(cfg_, namespace_, true);
237  active->clear_children("variables");
238  active->remove_attribute("variables");
239  active->add_child("variables",bactive);
240  return ret;
241  } else
242  return true;
243 }
244 
245 void persist_context::set_node(const std::string &name) {
246  std::string newspace = namespace_.root_;
247  if (!name.empty())
248  newspace += "." + name;
249  namespace_ = name_space(newspace);
250 }
251 
252 std::string persist_context::get_node() const
253 {
254  return namespace_.namespace_;
255 }
map_location prev
Definition: astarsearch.cpp:66
Class for writing a config out to a file in pieces.
void write(const config &cfg)
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:161
void append(const config &cfg)
Append data from another config object to this one.
Definition: config.cpp:208
config & mandatory_child(config_key_type key, int n=0)
Returns the nth child with the given key, or throws an error if there is none.
Definition: config.cpp:371
void remove_child(config_key_type key, std::size_t index)
Definition: config.cpp:649
std::size_t child_count(config_key_type key) const
Definition: config.cpp:301
void clear_children(T... keys)
Definition: config.hpp:644
bool has_child(config_key_type key) const
Determine whether a config has a child or not.
Definition: config.cpp:321
bool has_attribute(config_key_type key) const
Definition: config.cpp:159
config & child_or_add(config_key_type key)
Returns a reference to the first child with the given key.
Definition: config.cpp:410
void remove_attribute(config_key_type key)
Definition: config.cpp:164
bool empty() const
Definition: config.cpp:856
config & add_child(config_key_type key)
Definition: config.cpp:445
std::string get_node() const
void set_node(const std::string &)
config get_var(const std::string &) const
bool set_var(const std::string &, const config &, bool immediate=false)
persist_file_context(const std::string &name_space)
bool clear_var(const std::string &, bool immediate=false)
Declarations for File-IO.
std::size_t i
Definition: function.cpp:968
New lexcical_cast header.
Standard logging facilities (interface).
filesystem::scoped_istream istream_file(const std::string &fname, bool treat_failure_as_error)
static bfs::path get_dir(const bfs::path &dirpath)
Definition: filesystem.cpp:332
std::string get_user_data_dir()
Definition: filesystem.cpp:871
bool delete_file(const std::string &filename)
static bool file_exists(const bfs::path &fpath)
Definition: filesystem.cpp:321
bool is_directory(const std::string &fname)
Returns true if the given file is a directory.
filesystem::scoped_ostream ostream_file(const std::string &fname, std::ios_base::openmode mode, bool create_directory)
std::unique_ptr< std::istream > scoped_istream
Definition: filesystem.hpp:50
std::unique_ptr< std::ostream > scoped_ostream
Definition: filesystem.hpp:51
bool exists(const image::locator &i_locator)
Returns true if the given image actually exists, without loading it.
Definition: picture.cpp:879
logger & err()
Definition: log.cpp:226
std::size_t index(const std::string &str, const std::size_t index)
Codepoint index corresponding to the nth character in a UTF-8 string.
Definition: unicode.cpp:72
config pack_scalar(const std::string &name, const t_string &val)
static std::string get_persist_cfg_name(const std::string &name_space)
#define LOG_PERSIST
void read(config &cfg, std::istream &in, abstract_validator *validator)
Definition: parser.cpp:627