1 /*
2  * Copyright (C) 2002-2020 by the Widelands Development Team
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17  *
18  */
19 
20 #ifndef WL_IO_PROFILE_H
21 #define WL_IO_PROFILE_H
22 
23 #include <memory>
24 
25 #include "base/macros.h"
26 #include "base/vector.h"
27 #include "io/filesystem/layered_filesystem.h"
28 // TODO(unknown): as soon as g_fs is not needed anymore,
29 // include "filesystem.h" instead of layered_filesystem.h.
30 
31 class Profile;
32 class FileSystem;
33 
34 /**
35  * Represents one section inside the .ini-style file, basically as a list of
36  * key-value pairs.
37  *
38  * get_string:
39  * Returns the value of the first key with the given name. If the key isn't
40  * found, def is returned.
41  *
42  * get_int, get_bool, convert the value string to the desired type.
43  *
44  * get_safe_*:
45  * Like above, but throw an exception if the key doesn't exist.
46  *
47  * get_next_*:
48  * Returns the name of the next key of the given name. You can pass name == 0 to
49  * retrieve any unused key. Value::used is used to determine which key is next.
50  * The value of the key is stored in the second parameter.
51  */
52 class Section {
53 public:
54 	friend class Profile;
55 
56 	struct Value {
57 		Value(const std::string& name, const char* const value);
58 		Value(const Value&);
59 		Value(Value&& other);
60 
61 		// destructor would be empty
62 
63 		Value& operator=(Value);
64 		Value& operator=(Value&& other);
65 
get_nameValue66 		char const* get_name() const {
67 			return name_.c_str();
68 		}
69 
70 		bool is_used() const;
71 		void mark_used();
72 
73 		int32_t get_int() const;
74 		uint32_t get_natural() const;
75 		uint32_t get_positive() const;
76 		bool get_bool() const;
get_stringValue77 		char const* get_string() const {
78 			return value_.get();
79 		}
get_stringValue80 		char* get_string() {
81 			return value_.get();
82 		}
83 		Vector2i get_point() const;
84 
85 		void set_string(char const*);
86 
87 		friend void swap(Value& first, Value& second);
88 
89 	private:
90 		bool used_;
91 		std::string name_;
92 		std::unique_ptr<char[]> value_;
93 
94 		Value() = default;
95 	};
96 
97 	using ValueList = std::vector<Value>;
98 
99 	Section(Profile*, const std::string& name);
100 
101 	/// \returns whether a value with the given name exists.
102 	/// Does not mark the value as used.
103 	bool has_val(char const* name) const;
104 
105 	Value* get_val(char const* name);
106 	Value* get_next_val(char const* name = nullptr);
get_num_values()107 	uint32_t get_num_values() const {
108 		return values_.size();
109 	}
110 
111 	char const* get_name() const;
112 	void set_name(const std::string&);
113 
114 	bool is_used() const;
115 	void mark_used();
116 
117 	void check_used() const;
118 
119 	int32_t get_int(char const* name, int32_t def = 0);
120 	uint32_t get_natural(char const* name, uint32_t def = 0);
121 	uint32_t get_positive(char const* name, uint32_t def = 1);
122 	bool get_bool(char const* name, bool def = false);
123 	const char* get_string(char const* name, char const* def = nullptr);
124 	Vector2i get_point(char const* name, Vector2i def = Vector2i::zero());
125 
126 	int32_t get_safe_int(const char* name);
127 	uint32_t get_safe_natural(char const* name);
128 	uint32_t get_safe_positive(char const* name);
129 	bool get_safe_bool(const char* name);
130 	const char* get_safe_string(const char* name);
131 	const char* get_safe_string(const std::string& name);
132 
133 	char const* get_next_bool(char const* name, bool* value);
134 
135 	void set_int(char const* name, int32_t value);
136 	void set_natural(char const* name, uint32_t value);
set_bool(char const * const name,bool const value)137 	void set_bool(char const* const name, bool const value) {
138 		set_string(name, value ? "true" : "false");
139 	}
140 	void set_string(char const* name, char const* value);
141 	void set_string_duplicate(char const* name, char const* value);
set_string(char const * const name,const std::string & value)142 	void set_string(char const* const name, const std::string& value) {
143 		set_string(name, value.c_str());
144 	}
set_string_duplicate(char const * const name,const std::string & value)145 	void set_string_duplicate(char const* const name, const std::string& value) {
146 		set_string_duplicate(name, value.c_str());
147 	}
148 
149 	/// If a Value with this name already exists, update it with the given
150 	/// value. Otherwise create a new Value with the given name and value.
151 	Value& create_val(char const* name, char const* value);
152 
153 	/// Create a new Value with the given name and value.
154 	Value& create_val_duplicate(char const* name, char const* value);
155 
156 private:
157 	Profile* profile_;
158 	bool used_;
159 	std::string section_name_;
160 	ValueList values_;
161 };
162 
163 /**
164  * Parses an .ini-style file into sections and key-value pairs.
165  * The destructor prints out warnings if a section or value hasn't been used.
166  *
167  * get_section:
168  * Returns the first section of the given name (or 0 if none exist).
169  *
170  * get_safe_section:
171  * Like get_section, but throws an exception if the section doesn't exist.
172  *
173  * get_next_section:
174  * Returns the next unused section of the given name, or 0 if all sections
175  * have been used. name can be 0 to retrieve any remaining sections.
176  */
177 class Profile {
178 public:
179 	enum { err_ignore = 0, err_log, err_throw };
180 
181 	explicit Profile(int32_t error_level = err_throw);
182 	explicit Profile(char const* filename,
183 	                 char const* global_section = nullptr,
184 	                 int32_t error_level = err_throw);
185 	explicit Profile(char const* filename,
186 	                 char const* global_section,
187 	                 const std::string& textdomain,
188 	                 int32_t error_level = err_throw);
189 
190 	void error(char const*, ...) const __attribute__((format(printf, 2, 3)));
191 	void check_used() const;
192 
193 	void read(const char* const filename,
194 	          const char* const global_section = nullptr,
195 	          FileSystem& = *g_fs);
196 	void write(const char* const filename,
197 	           bool used_only = true,
198 	           FileSystem& = *g_fs,
199 	           const char* const comment = nullptr);
200 
201 	Section* get_section(const std::string& name);
202 	Section& get_safe_section(const std::string& name);
203 	Section& pull_section(char const* name);
204 	Section* get_next_section(char const* name = nullptr);
205 
206 	/// If a section with the given name already exists, return a reference to
207 	/// it. Otherwise create a new section with the given name and return a
208 	/// reference to it.
209 	Section& create_section(char const* name);
210 
211 	/// Create a new section with the given name and return a reference to it.
212 	Section& create_section_duplicate(char const* name);
213 
214 private:
215 	using SectionList = std::vector<Section>;
216 
217 	std::string filename_;
218 	SectionList sections_;
219 	int32_t error_level_;
220 
221 	DISALLOW_COPY_AND_ASSIGN(Profile);
222 };
223 
224 #endif  // end of include guard: WL_IO_PROFILE_H
225