1 /*
2 * Copyright (C) 2011-2020 Daniel Scharrer
3 *
4 * This software is provided 'as-is', without any express or implied
5 * warranty. In no event will the author(s) be held liable for any damages
6 * arising from the use of this software.
7 *
8 * Permission is granted to anyone to use this software for any purpose,
9 * including commercial applications, and to alter it and redistribute it
10 * freely, subject to the following restrictions:
11 *
12 * 1. The origin of this software must not be misrepresented; you must not
13 * claim that you wrote the original software. If you use this software
14 * in a product, an acknowledgment in the product documentation would be
15 * appreciated but is not required.
16 * 2. Altered source versions must be plainly marked as such, and must not be
17 * misrepresented as being the original software.
18 * 3. This notice may not be removed or altered from any source distribution.
19 */
20
21 #include "setup/version.hpp"
22
23 #include <cstring>
24 #include <algorithm>
25 #include <istream>
26 #include <ostream>
27
28 #include <boost/version.hpp>
29 #include <boost/static_assert.hpp>
30 #include <boost/lexical_cast.hpp>
31 #include "boost/algorithm/string.hpp"
32 #include <boost/range/begin.hpp>
33 #include <boost/range/end.hpp>
34 #include <boost/range/size.hpp>
35
36 #include "util/load.hpp"
37 #include "util/log.hpp"
38
39 namespace setup {
40
41 namespace {
42
43 typedef char stored_legacy_version[12];
44
45 struct known_legacy_version {
46
47 char name[13]; // terminating 0 byte is ignored
48
49 version_constant version;
50 version::flags variant;
51
operator version_constantsetup::__anon124d1def0111::known_legacy_version52 operator version_constant() const { return version; }
53
54 };
55
56 const known_legacy_version legacy_versions[] = {
57 { "i1.2.10--16\x1a", INNO_VERSION(1, 2, 10), version::Bits16 },
58 { "i1.2.10--32\x1a", INNO_VERSION(1, 2, 10), 0 },
59 };
60
61 typedef char stored_version[64];
62
63 struct known_version {
64
65 stored_version name;
66
67 version_constant version;
68 version::flags variant;
69
operator version_constantsetup::__anon124d1def0111::known_version70 operator version_constant() const { return version; }
71
72 };
73
74 const known_version versions[] = {
75 { "Inno Setup Setup Data (1.3.3)", INNO_VERSION_EXT(1, 3, 3, 0), 0 },
76 { "Inno Setup Setup Data (1.3.9)", INNO_VERSION_EXT(1, 3, 9, 0), 0 },
77 { "Inno Setup Setup Data (1.3.10)", INNO_VERSION_EXT(1, 3, 10, 0), 0 },
78 { "Inno Setup Setup Data (1.3.10) with ISX (1.3.10)", INNO_VERSION_EXT(1, 3, 10, 0), version::ISX },
79 { "Inno Setup Setup Data (1.3.12) with ISX (1.3.12.1)", INNO_VERSION_EXT(1, 3, 12, 1), version::ISX },
80 { "Inno Setup Setup Data (1.3.21)", /* ambiguous */ INNO_VERSION_EXT(1, 3, 21, 0), 0 },
81 { "Inno Setup Setup Data (1.3.21) with ISX (1.3.17)", INNO_VERSION_EXT(1, 3, 21, 0), version::ISX },
82 { "Inno Setup Setup Data (1.3.24)", INNO_VERSION_EXT(1, 3, 24, 0), 0 },
83 { "Inno Setup Setup Data (1.3.21) with ISX (1.3.24)", INNO_VERSION_EXT(1, 3, 24, 0), version::ISX },
84 { "Inno Setup Setup Data (1.3.25)", INNO_VERSION_EXT(1, 3, 25, 0), 0 },
85 { "Inno Setup Setup Data (1.3.25) with ISX (1.3.25)", INNO_VERSION_EXT(1, 3, 25, 0), version::ISX },
86 { "Inno Setup Setup Data (2.0.0)", INNO_VERSION_EXT(2, 0, 0, 0), 0 },
87 { "Inno Setup Setup Data (2.0.1)", /* ambiguous */ INNO_VERSION_EXT(2, 0, 1, 0), 0 },
88 { "Inno Setup Setup Data (2.0.2)", INNO_VERSION_EXT(2, 0, 2, 0), 0 },
89 { "Inno Setup Setup Data (2.0.5)", INNO_VERSION_EXT(2, 0, 5, 0), 0 },
90 { "Inno Setup Setup Data (2.0.6a)", INNO_VERSION_EXT(2, 0, 6, 0), 0 },
91 { "Inno Setup Setup Data (2.0.6a) with ISX (2.0.3)", INNO_VERSION_EXT(2, 0, 6, 0), version::ISX },
92 { "Inno Setup Setup Data (2.0.7)", INNO_VERSION_EXT(2, 0, 7, 0), 0 },
93 { "Inno Setup Setup Data (2.0.8)", INNO_VERSION_EXT(2, 0, 8, 0), 0 },
94 { "Inno Setup Setup Data (2.0.8) with ISX (2.0.3)", INNO_VERSION_EXT(2, 0, 8, 0), version::ISX },
95 { "Inno Setup Setup Data (2.0.8) with ISX (2.0.10)", INNO_VERSION_EXT(2, 0, 10, 0), version::ISX },
96 { "Inno Setup Setup Data (2.0.11)", INNO_VERSION_EXT(2, 0, 11, 0), 0 },
97 { "Inno Setup Setup Data (2.0.11) with ISX (2.0.11)", INNO_VERSION_EXT(2, 0, 11, 0), version::ISX },
98 { "Inno Setup Setup Data (2.0.17)", INNO_VERSION_EXT(2, 0, 17, 0), 0 },
99 { "Inno Setup Setup Data (2.0.17) with ISX (2.0.11)", INNO_VERSION_EXT(2, 0, 17, 0), version::ISX },
100 { "Inno Setup Setup Data (2.0.18)", INNO_VERSION_EXT(2, 0, 18, 0), 0 },
101 { "Inno Setup Setup Data (2.0.18) with ISX (2.0.11)", INNO_VERSION_EXT(2, 0, 18, 0), version::ISX },
102 { "Inno Setup Setup Data (3.0.0a)", INNO_VERSION_EXT(3, 0, 0, 0), 0 },
103 { "Inno Setup Setup Data (3.0.1)", INNO_VERSION_EXT(3, 0, 1, 0), 0 },
104 { "Inno Setup Setup Data (3.0.1) with ISX (3.0.0)", INNO_VERSION_EXT(3, 0, 1, 0), version::ISX },
105 { "Inno Setup Setup Data (3.0.3)", /* ambiguous */ INNO_VERSION_EXT(3, 0, 3, 0), 0 },
106 { "Inno Setup Setup Data (3.0.3) with ISX (3.0.3)", INNO_VERSION_EXT(3, 0, 3, 0), version::ISX },
107 { "Inno Setup Setup Data (3.0.4)", INNO_VERSION_EXT(3, 0, 4, 0), 0 },
108 { "My Inno Setup Extensions Setup Data (3.0.4)", INNO_VERSION_EXT(3, 0, 4, 0), version::ISX },
109 { "Inno Setup Setup Data (3.0.5)", INNO_VERSION_EXT(3, 0, 5, 0), 0 },
110 { "My Inno Setup Extensions Setup Data (3.0.6.1)", INNO_VERSION_EXT(3, 0, 6, 1), version::ISX },
111 { "Inno Setup Setup Data (4.0.0a)", INNO_VERSION_EXT(4, 0, 0, 0), 0 },
112 { "Inno Setup Setup Data (4.0.1)", INNO_VERSION_EXT(4, 0, 1, 0), 0 },
113 { "Inno Setup Setup Data (4.0.3)", INNO_VERSION_EXT(4, 0, 3, 0), 0 },
114 { "Inno Setup Setup Data (4.0.5)", INNO_VERSION_EXT(4, 0, 5, 0), 0 },
115 { "Inno Setup Setup Data (4.0.9)", INNO_VERSION_EXT(4, 0, 9, 0), 0 },
116 { "Inno Setup Setup Data (4.0.10)", INNO_VERSION_EXT(4, 0, 10, 0), 0 },
117 { "Inno Setup Setup Data (4.0.11)", INNO_VERSION_EXT(4, 0, 11, 0), 0 },
118 { "Inno Setup Setup Data (4.1.0)", INNO_VERSION_EXT(4, 1, 0, 0), 0 },
119 { "Inno Setup Setup Data (4.1.2)", INNO_VERSION_EXT(4, 1, 2, 0), 0 },
120 { "Inno Setup Setup Data (4.1.3)", INNO_VERSION_EXT(4, 1, 3, 0), 0 },
121 { "Inno Setup Setup Data (4.1.4)", INNO_VERSION_EXT(4, 1, 4, 0), 0 },
122 { "Inno Setup Setup Data (4.1.5)", INNO_VERSION_EXT(4, 1, 5, 0), 0 },
123 { "Inno Setup Setup Data (4.1.6)", INNO_VERSION_EXT(4, 1, 6, 0), 0 },
124 { "Inno Setup Setup Data (4.1.8)", INNO_VERSION_EXT(4, 1, 8, 0), 0 },
125 { "Inno Setup Setup Data (4.2.0)", INNO_VERSION_EXT(4, 2, 0, 0), 0 },
126 { "Inno Setup Setup Data (4.2.1)", INNO_VERSION_EXT(4, 2, 1, 0), 0 },
127 { "Inno Setup Setup Data (4.2.2)", INNO_VERSION_EXT(4, 2, 2, 0), 0 },
128 { "Inno Setup Setup Data (4.2.3)", /* ambiguous */ INNO_VERSION_EXT(4, 2, 3, 0), 0 },
129 { "Inno Setup Setup Data (4.2.4)", INNO_VERSION_EXT(4, 2, 4, 0), 0 },
130 { "Inno Setup Setup Data (4.2.5)", INNO_VERSION_EXT(4, 2, 5, 0), 0 },
131 { "Inno Setup Setup Data (4.2.6)", INNO_VERSION_EXT(4, 2, 6, 0), 0 },
132 { "Inno Setup Setup Data (5.0.0)", INNO_VERSION_EXT(5, 0, 0, 0), 0 },
133 { "Inno Setup Setup Data (5.0.1)", INNO_VERSION_EXT(5, 0, 1, 0), 0 },
134 { "Inno Setup Setup Data (5.0.3)", INNO_VERSION_EXT(5, 0, 3, 0), 0 },
135 { "Inno Setup Setup Data (5.0.4)", INNO_VERSION_EXT(5, 0, 4, 0), 0 },
136 { "Inno Setup Setup Data (5.1.0)", INNO_VERSION_EXT(5, 1, 0, 0), 0 },
137 { "Inno Setup Setup Data (5.1.2)", INNO_VERSION_EXT(5, 1, 2, 0), 0 },
138 { "Inno Setup Setup Data (5.1.7)", INNO_VERSION_EXT(5, 1, 7, 0), 0 },
139 { "Inno Setup Setup Data (5.1.10)", INNO_VERSION_EXT(5, 1, 10, 0), 0 },
140 { "Inno Setup Setup Data (5.1.13)", INNO_VERSION_EXT(5, 1, 13, 0), 0 },
141 { "Inno Setup Setup Data (5.2.0)", INNO_VERSION_EXT(5, 2, 0, 0), 0 },
142 { "Inno Setup Setup Data (5.2.1)", INNO_VERSION_EXT(5, 2, 1, 0), 0 },
143 { "Inno Setup Setup Data (5.2.3)", INNO_VERSION_EXT(5, 2, 3, 0), 0 },
144 { "Inno Setup Setup Data (5.2.5)", INNO_VERSION_EXT(5, 2, 5, 0), 0 },
145 { "Inno Setup Setup Data (5.2.5) (u)", INNO_VERSION_EXT(5, 2, 5, 0), version::Unicode },
146 { "Inno Setup Setup Data (5.3.0)", INNO_VERSION_EXT(5, 3, 0, 0), 0 },
147 { "Inno Setup Setup Data (5.3.0) (u)", INNO_VERSION_EXT(5, 3, 0, 0), version::Unicode },
148 { "Inno Setup Setup Data (5.3.3)", INNO_VERSION_EXT(5, 3, 3, 0), 0 },
149 { "Inno Setup Setup Data (5.3.3) (u)", INNO_VERSION_EXT(5, 3, 3, 0), version::Unicode },
150 { "Inno Setup Setup Data (5.3.5)", INNO_VERSION_EXT(5, 3, 5, 0), 0 },
151 { "Inno Setup Setup Data (5.3.5) (u)", INNO_VERSION_EXT(5, 3, 5, 0), version::Unicode },
152 { "Inno Setup Setup Data (5.3.6)", INNO_VERSION_EXT(5, 3, 6, 0), 0 },
153 { "Inno Setup Setup Data (5.3.6) (u)", INNO_VERSION_EXT(5, 3, 6, 0), version::Unicode },
154 { "Inno Setup Setup Data (5.3.7)", INNO_VERSION_EXT(5, 3, 7, 0), 0 },
155 { "Inno Setup Setup Data (5.3.7) (u)", INNO_VERSION_EXT(5, 3, 7, 0), version::Unicode },
156 { "Inno Setup Setup Data (5.3.8)", INNO_VERSION_EXT(5, 3, 8, 0), 0 },
157 { "Inno Setup Setup Data (5.3.8) (u)", INNO_VERSION_EXT(5, 3, 8, 0), version::Unicode },
158 { "Inno Setup Setup Data (5.3.9)", INNO_VERSION_EXT(5, 3, 9, 0), 0 },
159 { "Inno Setup Setup Data (5.3.9) (u)", INNO_VERSION_EXT(5, 3, 9, 0), version::Unicode },
160 { "Inno Setup Setup Data (5.3.10)", INNO_VERSION_EXT(5, 3, 10, 0), 0 },
161 { "Inno Setup Setup Data (5.3.10) (u)", INNO_VERSION_EXT(5, 3, 10, 0), version::Unicode },
162 { "Inno Setup Setup Data (5.4.2)", INNO_VERSION_EXT(5, 4, 2, 0), 0 },
163 { "Inno Setup Setup Data (5.4.2) (u)", /* ambiguous */ INNO_VERSION_EXT(5, 4, 2, 0), version::Unicode },
164 { "" /* BlackBox v1? */, INNO_VERSION_EXT(5, 4, 2, 1), 0 },
165 { "" /* BlackBox v1? */, INNO_VERSION_EXT(5, 4, 2, 1), version::Unicode },
166 { "Inno Setup Setup Data (5.5.0)", INNO_VERSION_EXT(5, 5, 0, 0), 0 },
167 { "Inno Setup Setup Data (5.5.0) (u)", /* ambiguous */ INNO_VERSION_EXT(5, 5, 0, 0), version::Unicode },
168 { "" /* BlackBox v2 / Inno Setup Ultra */, INNO_VERSION_EXT(5, 5, 0, 1), 0 },
169 { "" /* BlackBox v2 / Inno Setup Ultra */, INNO_VERSION_EXT(5, 5, 0, 1), version::Unicode },
170 { "Inno Setup Setup Data (5.5.6)", INNO_VERSION_EXT(5, 5, 6, 0), 0 },
171 { "Inno Setup Setup Data (5.5.6) (u)", INNO_VERSION_EXT(5, 5, 6, 0), version::Unicode },
172 { "Inno Setup Setup Data (5.5.7)", /* ambiguous */ INNO_VERSION_EXT(5, 5, 7, 0), 0 },
173 { "Inno Setup Setup Data (5.5.7) (u)", /* ambiguous */ INNO_VERSION_EXT(5, 5, 7, 0), version::Unicode },
174 { "Inno Setup Setup Data (5.5.7) (U)", /* ambiguous */ INNO_VERSION_EXT(5, 5, 7, 0), version::Unicode },
175 { "Inno Setup Setup Data (5.5.8) (u)", /* unofficial */ INNO_VERSION_EXT(5, 5, 7, 0), version::Unicode },
176 { "" /* unknown 5.5.7 (u) variant */, /* ambiguous */ INNO_VERSION_EXT(5, 5, 7, 1), 0 },
177 { "" /* unknown 5.5.7 (u) variant */, /* ambiguous */ INNO_VERSION_EXT(5, 5, 7, 1), version::Unicode },
178 { "Inno Setup Setup Data (5.6.0)", INNO_VERSION_EXT(5, 6, 0, 0), 0 },
179 { "Inno Setup Setup Data (5.6.0) (u)", INNO_VERSION_EXT(5, 6, 0, 0), version::Unicode },
180 { "Inno Setup Setup Data (5.6.2)", /* prerelease */ INNO_VERSION_EXT(5, 6, 2, 0), 0 },
181 { "Inno Setup Setup Data (5.6.2) (u)", /* prerelease */ INNO_VERSION_EXT(5, 6, 2, 0), version::Unicode },
182 { "Inno Setup Setup Data (6.0.0) (u)", INNO_VERSION_EXT(6, 0, 0, 0), version::Unicode },
183 { "Inno Setup Setup Data (6.1.0) (u)", INNO_VERSION_EXT(6, 1, 0, 0), version::Unicode },
184 };
185
186 } // anonymous namespace
187
operator <<(std::ostream & os,const version & version)188 std::ostream & operator<<(std::ostream & os, const version & version) {
189
190 os << version.a() << '.' << version.b() << '.' << version.c();
191 if(version.d()) {
192 os << '.' << version.d();
193 }
194
195 if(version.is_unicode()) {
196 os << " (unicode)";
197 }
198
199 if(version.bits() != 32) {
200 os << " (" << int(version.bits()) << "-bit)";
201 }
202
203 if(version.is_isx()) {
204 os << " (isx)";
205 }
206
207 return os;
208 }
209
load(std::istream & is)210 void version::load(std::istream & is) {
211
212 static const char digits[] = "0123456789";
213
214 BOOST_STATIC_ASSERT(sizeof(stored_legacy_version) <= sizeof(stored_version));
215
216 stored_legacy_version legacy_version;
217 is.read(legacy_version, std::streamsize(sizeof(legacy_version)));
218
219 if(legacy_version[0] == 'i' && legacy_version[sizeof(legacy_version) - 1] == '\x1a') {
220
221 for(size_t i = 0; i < size_t(boost::size(legacy_versions)); i++) {
222 if(!memcmp(legacy_version, legacy_versions[i].name, sizeof(legacy_version))) {
223 value = legacy_versions[i].version;
224 variant = legacy_versions[i].variant;
225 known = true;
226 debug("known legacy version: \"" << versions[i].name << '"');
227 return;
228 }
229 }
230
231 debug("unknown legacy version: \""
232 << std::string(legacy_version, sizeof(legacy_version)) << '"');
233
234 if(legacy_version[0] != 'i' || legacy_version[2] != '.' || legacy_version[4] != '.'
235 || legacy_version[7] != '-' || legacy_version[8] != '-') {
236 throw version_error();
237 }
238
239 if(legacy_version[9] == '1' && legacy_version[10] == '6') {
240 variant = Bits16;
241 } else if(legacy_version[9] == '3' && legacy_version[10] == '2') {
242 variant = 0;
243 } else {
244 throw version_error();
245 }
246
247 std::string version_str(legacy_version, sizeof(legacy_version));
248
249 try {
250 unsigned a = util::to_unsigned(version_str.data() + 1, 1);
251 unsigned b = util::to_unsigned(version_str.data() + 3, 1);
252 unsigned c = util::to_unsigned(version_str.data() + 5, 2);
253 value = INNO_VERSION(a, b, c);
254 } catch(const boost::bad_lexical_cast &) {
255 throw version_error();
256 }
257
258 known = false;
259
260 return;
261 }
262
263 stored_version version_string;
264 BOOST_STATIC_ASSERT(sizeof(legacy_version) <= sizeof(version_string));
265 memcpy(version_string, legacy_version, sizeof(legacy_version));
266 is.read(version_string + sizeof(legacy_version),
267 std::streamsize(sizeof(version_string) - sizeof(legacy_version)));
268
269
270 for(size_t i = 0; i < size_t(boost::size(versions)); i++) {
271 if(versions[i].name[0] != '\0' && !memcmp(version_string, versions[i].name, sizeof(version_string))) {
272 value = versions[i].version;
273 variant = versions[i].variant;
274 known = true;
275 debug("known version: \"" << versions[i].name << '"');
276 return;
277 }
278 }
279
280 char * end = std::find(version_string, version_string + boost::size(version_string), '\0');
281 std::string version_str(version_string, end);
282 debug("unknown version: \"" << version_str << '"');
283 if(!boost::contains(version_str, "Inno Setup")) {
284 throw version_error();
285 }
286
287 value = 0;
288 size_t bracket = version_str.find('(');
289 for(; bracket != std::string::npos; bracket = version_str.find('(', bracket + 1)) {
290
291 if(version_str.length() - bracket < 6) {
292 continue;
293 }
294
295 try {
296
297 size_t a_start = bracket + 1;
298 size_t a_end = version_str.find_first_not_of(digits, a_start);
299 if(a_end == std::string::npos || version_str[a_end] != '.') {
300 continue;
301 }
302 unsigned a = util::to_unsigned(version_str.data() + a_start, a_end - a_start);
303
304 size_t b_start = a_end + 1;
305 size_t b_end = version_str.find_first_not_of(digits, b_start);
306 if(b_end == std::string::npos || version_str[b_end] != '.') {
307 continue;
308 }
309 unsigned b = util::to_unsigned(version_str.data() + b_start, b_end - b_start);
310
311 size_t c_start = b_end + 1;
312 size_t c_end = version_str.find_first_not_of(digits, c_start);
313 if(c_end == std::string::npos) {
314 continue;
315 }
316 unsigned c = util::to_unsigned(version_str.data() + c_start, c_end - c_start);
317
318 size_t d_start = c_end;
319 if(version_str[d_start] == 'a') {
320 if(d_start + 1 >= version_str.length()) {
321 continue;
322 }
323 d_start++;
324 }
325
326 unsigned d = 0;
327 if(version_str[d_start] == '.') {
328 d_start++;
329 size_t d_end = version_str.find_first_not_of(digits, d_start);
330 if(d_end != std::string::npos && d_end != d_start) {
331 d = util::to_unsigned(version_str.data() + d_start, d_end - d_start);
332 }
333 }
334
335 value = std::max(value, INNO_VERSION_EXT(a, b, c, d));
336
337 } catch(const boost::bad_lexical_cast &) {
338 continue;
339 }
340 }
341 if(!value) {
342 throw version_error();
343 }
344
345 variant = 0;
346 if(boost::contains(version_str, "(u)") || boost::contains(version_str, "(U)")) {
347 variant |= Unicode;
348 }
349 if(boost::contains(version_str, "My Inno Setup Extensions") || boost::contains(version_str, "with ISX")) {
350 variant |= ISX;
351 }
352
353 known = false;
354 }
355
is_ambiguous() const356 bool version::is_ambiguous() const {
357
358 if(value == INNO_VERSION(1, 3, 21)) {
359 // might be either 1.3.21 or 1.3.24
360 return true;
361 }
362
363 if(value == INNO_VERSION(2, 0, 1)) {
364 // might be either 2.0.1 or 2.0.2
365 return true;
366 }
367
368 if(value == INNO_VERSION(3, 0, 3)) {
369 // might be either 3.0.3 or 3.0.4
370 return true;
371 }
372
373 if(value == INNO_VERSION(4, 2, 3)) {
374 // might be either 4.2.3 or 4.2.4
375 return true;
376 }
377
378 if(value == INNO_VERSION(5, 4, 2)) {
379 // might be either 5.4.2 or 5.4.2.1
380 return true;
381 }
382
383 if(value == INNO_VERSION(5, 5, 0)) {
384 // might be either 5.5.0 or 5.5.0.1
385 return true;
386 }
387
388 if(value == INNO_VERSION(5, 5, 7) || value == INNO_VERSION_EXT(5, 5, 7, 1)) {
389 // might be either 5.5.7, an unknown modification of 5.5.7, or 5.6.0
390 return true;
391 }
392
393 return false;
394 }
395
next()396 version_constant version::next() {
397
398 const known_legacy_version * legacy_end = boost::end(legacy_versions);
399 const known_legacy_version * legacy_result;
400 legacy_result = std::upper_bound(boost::begin(legacy_versions), legacy_end, value);
401 while(legacy_result != legacy_end && legacy_result->variant != variant) {
402 legacy_result++;
403 }
404 if(legacy_result != legacy_end) {
405 return legacy_result->version;
406 }
407
408 const known_version * end = boost::end(versions);
409 const known_version * result = std::upper_bound(boost::begin(versions), end, value);
410 while(result != end && result->variant != variant) {
411 result++;
412 }
413 if(result != end) {
414 return result->version;
415 }
416
417 return 0;
418 }
419
420 } // namespace setup
421