The Battle for Wesnoth  1.17.0-dev
persist_context.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2010 - 2021
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 << std::endl;
45  }
46  }
47  }
48 }
49 
51  : persist_context(name_space)
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->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->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->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->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.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 << std::endl;
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 }
config & child(config_key_type key, int n=0)
Returns the nth child with the given key, or a reference to an invalid config if there is none...
Definition: config.cpp:402
void write(const config &cfg)
void clear_children(T... keys)
Definition: config.hpp:557
void append(const config &cfg)
Append data from another config object to this one.
Definition: config.cpp:269
bool delete_file(const std::string &filename)
Definition: filesystem.cpp:987
New lexcical_cast header.
bool has_attribute(config_key_type key) const
Definition: config.cpp:211
bool has_child(config_key_type key) const
Determine whether a config has a child or not.
Definition: config.cpp:394
unsigned child_count(config_key_type key) const
Definition: config.cpp:372
static bool file_exists(const bfs::path &fpath)
Definition: filesystem.cpp:263
filesystem::scoped_istream istream_file(const std::string &fname, bool treat_failure_as_error)
config & child_or_add(config_key_type key)
Returns a reference to the first child with the given key.
Definition: config.cpp:478
config pack_scalar(const std::string &name, const t_string &val)
void remove_attribute(config_key_type key)
Definition: config.cpp:217
filesystem::scoped_ostream ostream_file(const std::string &fname, std::ios_base::openmode mode, bool create_directory)
static bfs::path get_dir(const bfs::path &dirpath)
Definition: filesystem.cpp:274
#define LOG_PERSIST
bool exists(const image::locator &i_locator)
Returns true if the given image actually exists, without loading it.
Definition: picture.cpp:1011
void read(config &cfg, std::istream &in, abstract_validator *validator)
Definition: parser.cpp:627
Class for writing a config out to a file in pieces.
std::string get_user_data_dir()
Definition: filesystem.cpp:792
bool set_var(const std::string &, const config &, bool immediate=false)
bool is_directory(const std::string &fname)
Returns true if the given file is a directory.
std::unique_ptr< std::istream > scoped_istream
Definition: filesystem.hpp:39
static std::string get_persist_cfg_name(const std::string &name_space)
static int writer(lua_State *L, const void *b, size_t size, void *ud)
Definition: lstrlib.cpp:221
std::unique_ptr< std::ostream > scoped_ostream
Definition: filesystem.hpp:40
persist_file_context(const std::string &name_space)
std::size_t i
Definition: function.cpp:967
logger & err()
Definition: log.cpp:77
Declarations for File-IO.
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 & add_child(config_key_type key)
Definition: config.cpp:514
void set_node(const std::string &)
std::string get_node() const
config get_var(const std::string &) const
map_location prev
Definition: astarsearch.cpp:66
Standard logging facilities (interface).
std::string message
Definition: exceptions.hpp:30
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:61
void remove_child(config_key_type key, unsigned index)
Definition: config.cpp:732
bool empty() const
Definition: config.cpp:941
bool clear_var(const std::string &, bool immediate=false)