Welcome to mirror list, hosted at ThFree Co, Russian Federation.

Version.cpp « Config « slic3r « src « xs - github.com/prusa3d/PrusaSlicer.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: a85322ecaf9c375a6bd18f0947c2075a7061771a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
#include "Version.hpp"

#include <boost/algorithm/string/predicate.hpp>
#include <boost/algorithm/string/trim.hpp>
#include <boost/nowide/fstream.hpp>

#include "../../libslic3r/libslic3r.h"
#include "../../libslic3r/Config.hpp"
#include "../../libslic3r/FileParserError.hpp"
#include "../../libslic3r/Utils.hpp"

namespace Slic3r { 
namespace GUI {
namespace Config {

static const Semver s_current_slic3r_semver(SLIC3R_VERSION);

// Optimized lexicographic compare of two pre-release versions, ignoring the numeric suffix.
static int compare_prerelease(const char *p1, const char *p2)
{
	for (;;) {
		char c1 = *p1 ++;
		char c2 = *p2 ++;
		bool a1 = std::isalpha(c1) && c1 != 0;
		bool a2 = std::isalpha(c2) && c2 != 0;
		if (a1) {
			if (a2) {
				if (c1 != c2)
					return (c1 < c2) ? -1 : 1;
			} else
				return 1;
		} else {
			if (a2)
				return -1;
			else
				return 0;
		}
	}
	// This shall never happen.
	return 0;
}

bool Version::is_slic3r_supported(const Semver &slic3r_version) const
{ 
	if (! slic3r_version.in_range(min_slic3r_version, max_slic3r_version))
		return false;
	// Now verify, whether the configuration pre-release status is compatible with the Slic3r's pre-release status.
	// Alpha Slic3r will happily load any configuration, while beta Slic3r will ignore alpha configurations etc.
	const char *prerelease_slic3r = slic3r_version.prerelease();
	const char *prerelease_config = this->config_version.prerelease();
	if (prerelease_config == nullptr)
		// Released config is always supported.
		return true;
	else if (prerelease_slic3r == nullptr)
		// Released slic3r only supports released configs.
		return false;
	// Compare the pre-release status of Slic3r against the config.
	// If the prerelease status of slic3r is lexicographically lower or equal 
	// to the prerelease status of the config, accept it.
	return compare_prerelease(prerelease_slic3r, prerelease_config) != 1;
}

bool Version::is_current_slic3r_supported() const
{
	return this->is_slic3r_supported(s_current_slic3r_semver);
}

#if 0
//TODO: This test should be moved to a unit test, once we have C++ unit tests in place.
static int version_test()
{
	Version v;
	v.config_version 	 = *Semver::parse("1.1.2");
	v.min_slic3r_version = *Semver::parse("1.38.0");
	v.max_slic3r_version = Semver::inf();
	assert(v.is_slic3r_supported(*Semver::parse("1.38.0")));
	assert(! v.is_slic3r_supported(*Semver::parse("1.38.0-alpha")));
	assert(! v.is_slic3r_supported(*Semver::parse("1.37.0-alpha")));
	// Test the prerelease status.
	assert(v.is_slic3r_supported(*Semver::parse("1.39.0-alpha")));
	assert(v.is_slic3r_supported(*Semver::parse("1.39.0-alpha1")));
	assert(v.is_slic3r_supported(*Semver::parse("1.39.0-alpha1")));
	assert(v.is_slic3r_supported(*Semver::parse("1.39.0-beta")));
	assert(v.is_slic3r_supported(*Semver::parse("1.39.0-beta1")));
	assert(v.is_slic3r_supported(*Semver::parse("1.39.0-beta1")));
	assert(v.is_slic3r_supported(*Semver::parse("1.39.0-rc2")));
	assert(v.is_slic3r_supported(*Semver::parse("1.39.0")));
	v.config_version 	 = *Semver::parse("1.1.2-alpha");
	assert(v.is_slic3r_supported(*Semver::parse("1.39.0-alpha")));
	assert(v.is_slic3r_supported(*Semver::parse("1.39.0-alpha1")));
	assert(! v.is_slic3r_supported(*Semver::parse("1.39.0-beta")));
	assert(! v.is_slic3r_supported(*Semver::parse("1.39.0-beta1")));
	assert(! v.is_slic3r_supported(*Semver::parse("1.39.0-beta1")));
	assert(! v.is_slic3r_supported(*Semver::parse("1.39.0-rc2")));
	assert(! v.is_slic3r_supported(*Semver::parse("1.39.0")));
	v.config_version 	 = *Semver::parse("1.1.2-alpha1");
	assert(v.is_slic3r_supported(*Semver::parse("1.39.0-alpha")));
	assert(v.is_slic3r_supported(*Semver::parse("1.39.0-alpha1")));
	assert(! v.is_slic3r_supported(*Semver::parse("1.39.0-beta")));
	assert(! v.is_slic3r_supported(*Semver::parse("1.39.0-beta1")));
	assert(! v.is_slic3r_supported(*Semver::parse("1.39.0-beta1")));
	assert(! v.is_slic3r_supported(*Semver::parse("1.39.0-rc2")));
	assert(! v.is_slic3r_supported(*Semver::parse("1.39.0")));
	v.config_version 	 = *Semver::parse("1.1.2-beta");
	assert(v.is_slic3r_supported(*Semver::parse("1.39.0-alpha")));
	assert(v.is_slic3r_supported(*Semver::parse("1.39.0-alpha1")));
	assert(v.is_slic3r_supported(*Semver::parse("1.39.0-beta")));
	assert(v.is_slic3r_supported(*Semver::parse("1.39.0-beta1")));
	assert(v.is_slic3r_supported(*Semver::parse("1.39.0-beta1")));
	assert(! v.is_slic3r_supported(*Semver::parse("1.39.0-rc")));
	assert(! v.is_slic3r_supported(*Semver::parse("1.39.0-rc2")));
	assert(! v.is_slic3r_supported(*Semver::parse("1.39.0")));
	v.config_version 	 = *Semver::parse("1.1.2-rc");
	assert(v.is_slic3r_supported(*Semver::parse("1.39.0-alpha")));
	assert(v.is_slic3r_supported(*Semver::parse("1.39.0-alpha1")));
	assert(v.is_slic3r_supported(*Semver::parse("1.39.0-beta")));
	assert(v.is_slic3r_supported(*Semver::parse("1.39.0-beta1")));
	assert(v.is_slic3r_supported(*Semver::parse("1.39.0-beta1")));
	assert(v.is_slic3r_supported(*Semver::parse("1.39.0-rc")));
	assert(v.is_slic3r_supported(*Semver::parse("1.39.0-rc2")));
	assert(! v.is_slic3r_supported(*Semver::parse("1.39.0")));
	v.config_version 	 = *Semver::parse("1.1.2-rc2");
	assert(v.is_slic3r_supported(*Semver::parse("1.39.0-alpha")));
	assert(v.is_slic3r_supported(*Semver::parse("1.39.0-alpha1")));
	assert(v.is_slic3r_supported(*Semver::parse("1.39.0-beta")));
	assert(v.is_slic3r_supported(*Semver::parse("1.39.0-beta1")));
	assert(v.is_slic3r_supported(*Semver::parse("1.39.0-beta1")));
	assert(v.is_slic3r_supported(*Semver::parse("1.39.0-rc")));
	assert(v.is_slic3r_supported(*Semver::parse("1.39.0-rc2")));
	assert(! v.is_slic3r_supported(*Semver::parse("1.39.0")));
	// Test the upper boundary.
	v.config_version 	 = *Semver::parse("1.1.2");
	v.max_slic3r_version = *Semver::parse("1.39.3-beta1");
	assert(v.is_slic3r_supported(*Semver::parse("1.38.0")));
	assert(! v.is_slic3r_supported(*Semver::parse("1.38.0-alpha")));
	assert(! v.is_slic3r_supported(*Semver::parse("1.38.0-alpha1")));
	assert(! v.is_slic3r_supported(*Semver::parse("1.37.0-alpha")));
	return 0;
}
static int version_test_run = version_test();
#endif

inline char* left_trim(char *c)
{
	for (; *c == ' ' || *c == '\t'; ++ c);
	return c;
}

inline char* right_trim(char *start)
{
	char *end = start + strlen(start) - 1;
	for (; end >= start && (*end == ' ' || *end == '\t'); -- end);
	*(++ end) = 0;
	return end;
}

inline std::string unquote_value(char *value, char *end, const std::string &path, int idx_line)
{
	std::string svalue;
	if (value == end) {
		// Empty string is a valid string.
	} else if (*value == '"') {
		if (++ value > -- end || *end != '"')
			throw file_parser_error("String not enquoted correctly", path, idx_line);
		*end = 0;
		if (! unescape_string_cstyle(value, svalue))
			throw file_parser_error("Invalid escape sequence inside a quoted string", path, idx_line);
	} else
		svalue.assign(value, end);
	return svalue;
}

inline std::string unquote_version_comment(char *value, char *end, const std::string &path, int idx_line)
{
	std::string svalue;
	if (value == end) {
		// Empty string is a valid string.
	} else if (*value == '"') {
		if (++ value > -- end || *end != '"')
			throw file_parser_error("Version comment not enquoted correctly", path, idx_line);
		*end = 0;
		if (! unescape_string_cstyle(value, svalue))
			throw file_parser_error("Invalid escape sequence inside a quoted version comment", path, idx_line);
	} else
		svalue.assign(value, end);
	return svalue;
}

size_t Index::load(const boost::filesystem::path &path)
{
	m_configs.clear();
	m_vendor = path.stem().string();

    boost::nowide::ifstream ifs(path.string());
    std::string line;
    size_t idx_line = 0;
    Version ver;
    while (std::getline(ifs, line)) {
    	++ idx_line;
    	// Skip the initial white spaces.
    	char *key = left_trim(const_cast<char*>(line.data()));
		if (*key == '#')
			// Skip a comment line.
			continue;
		// Right trim the line.
		char *end = right_trim(key);
        if (key == end)
            // Skip an empty line.
            continue;
		// Keyword may only contain alphanumeric characters. Semantic version may in addition contain "+.-".
    	char *key_end = key;
    	bool  maybe_semver = true;
		for (; *key_end != 0; ++ key_end) {
			if (std::isalnum(*key_end) || strchr("+.-", *key_end) != nullptr) {
				// It may be a semver.
			} else if (*key_end == '_') {
				// Cannot be a semver, but it may be a key.
				maybe_semver = false;
			} else
				// End of semver or keyword.
				break;
    	}
    	if (*key_end != 0 && *key_end != ' ' && *key_end != '\t' && *key_end != '=')
    		throw file_parser_error("Invalid keyword or semantic version", path, idx_line);
		char *value = left_trim(key_end);
		bool  key_value_pair = *value == '=';
		if (key_value_pair)
			value = left_trim(value + 1);
		*key_end = 0;
    	boost::optional<Semver> semver;
    	if (maybe_semver)
    		semver = Semver::parse(key);
		if (key_value_pair) {
    		if (semver)
    			throw file_parser_error("Key cannot be a semantic version", path, idx_line);\
    		// Verify validity of the key / value pair.
			std::string svalue = unquote_value(value, end, path.string(), idx_line);
    		if (strcmp(key, "min_slic3r_version") == 0 || strcmp(key, "max_slic3r_version") == 0) {
    			if (! svalue.empty())
					semver = Semver::parse(svalue);
		    	if (! semver)
		    		throw file_parser_error(std::string(key) + " must referece a valid semantic version", path, idx_line);
				if (strcmp(key, "min_slic3r_version") == 0)
    				ver.min_slic3r_version = *semver;
    			else
    				ver.max_slic3r_version = *semver;
    		} else {
    			// Ignore unknown keys, as there may come new keys in the future.
    		}
			continue;
    	}
		if (! semver)
			throw file_parser_error("Invalid semantic version", path, idx_line);
		ver.config_version = *semver;
		ver.comment = (end <= key_end) ? "" : unquote_version_comment(value, end, path.string(), idx_line);
		m_configs.emplace_back(ver);
    }

    // Sort the configs by their version.
    std::sort(m_configs.begin(), m_configs.end(), [](const Version &v1, const Version &v2) { return v1.config_version < v2.config_version; });
    return m_configs.size();
}

Semver Index::version() const
{
    Semver ver = Semver::zero();
    for (const Version &cv : m_configs)
        if (cv.config_version >= ver)
            ver = cv.config_version;
    return ver;
}

Index::const_iterator Index::find(const Semver &ver) const
{ 
	Version key;
	key.config_version = ver;
	auto it = std::lower_bound(m_configs.begin(), m_configs.end(), key, 
		[](const Version &v1, const Version &v2) { return v1.config_version < v2.config_version; });
	return (it == m_configs.end() || it->config_version == ver) ? it : m_configs.end();
}

Index::const_iterator Index::recommended() const
{
	int idx = -1;
	const_iterator highest = this->end();
	for (const_iterator it = this->begin(); it != this->end(); ++ it)
		if (it->is_current_slic3r_supported() &&
			(highest == this->end() || highest->config_version < it->config_version))
			highest = it;
	return highest;
}

std::vector<Index> Index::load_db()
{
    boost::filesystem::path cache_dir = boost::filesystem::path(Slic3r::data_dir()) / "cache";

    std::vector<Index> index_db;
    std::string errors_cummulative;
	for (auto &dir_entry : boost::filesystem::directory_iterator(cache_dir))
        if (boost::filesystem::is_regular_file(dir_entry.status()) && boost::algorithm::iends_with(dir_entry.path().filename().string(), ".idx")) {
        	Index idx;
            try {
            	idx.load(dir_entry.path());
            } catch (const std::runtime_error &err) {
                errors_cummulative += err.what();
                errors_cummulative += "\n";
                continue;
			}
            index_db.emplace_back(std::move(idx));
        }

    if (! errors_cummulative.empty())
        throw std::runtime_error(errors_cummulative);
    return index_db;
}

} // namespace Config
} // namespace GUI
} // namespace Slic3r