The Battle for Wesnoth  1.17.0-dev
game_version.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2008 - 2021
3  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
15 #include "game_version.hpp"
16 
17 #include "utils/math.hpp"
18 #include "lexical_cast.hpp"
20 #include "wesconfig.h"
21 
22 #ifdef LOAD_REVISION
23 #include "revision.h"
24 #endif
25 
26 #include <cassert>
27 #include <functional>
28 #include <locale>
29 
30 #include <boost/algorithm/string.hpp>
31 
32 namespace game_config
33 {
36 const version_info test_version("test");
37 
38 #ifdef REVISION
39 const std::string revision = VERSION " (" REVISION ")";
40 #elif defined(VCS_SHORT_HASH) && defined(VCS_WC_MODIFIED)
41 const std::string revision = std::string(VERSION) + " (" + VCS_SHORT_HASH + (VCS_WC_MODIFIED ? "-Modified" : "-Clean") + ")";
42 #else
43 const std::string revision = VERSION;
44 #endif
45 
46 } // namespace game_config
47 
49  : nums_(3,0), special_(""), special_separator_('\0')
50 {
51 }
52 
54  : version_info(std::string(str))
55 {
56 }
57 
58 version_info::version_info(unsigned int major, unsigned int minor, unsigned int revision_level,
59  char special_separator, const std::string& special)
60  : nums_(3,0), special_(special), special_separator_(special_separator)
61 {
62  nums_[0] = major;
63  nums_[1] = minor;
64  nums_[2] = revision_level;
65 }
66 
67 version_info::version_info(const std::string& str)
68  : nums_(3,0)
69  , special_("")
70  , special_separator_('\0')
71 {
72  std::string v = str;
73  boost::trim(v);
74 
75  if(v.empty())
76  return;
77 
78  //
79  // The breakpoint is where the "special" version component begins.
80  // For 1.1.2a it would at the index of the char 'a'. For 1.1.4+dev it is at '+'.
81  //
82  // For 1.5.2 it is at npos.
83  //
84  const std::string::size_type breakpoint_pos = v.find_first_not_of(".0123456789");
85  std::string left_side;
86  if(breakpoint_pos != std::string::npos) {
87  const std::string right_side = v.substr(breakpoint_pos);
88  assert(right_side.empty() == false);
89 
90  if(std::isalpha(right_side[0], std::locale::classic())) {
91  special_separator_ = '\0';
92  special_ = right_side;
93  }
94  else {
95  special_separator_ = right_side[0];
96  if(right_side.size() > 1) {
97  special_ = right_side.substr(1);
98  }
99  }
100 
101  left_side = v.substr(0, breakpoint_pos);
102  }
103  else {
104  left_side = v;
105  }
106 
107  const std::vector<std::string> components = utils::split(left_side, '.');
108  const std::size_t s = components.size();
109  if(s == 0) {
110  return;
111  }
112  else if(s > 3) {
113  nums_.resize(s, 0);
114  }
115 
116  for(std::size_t i = 0; (i < s); ++i) {
117  nums_[i] = lexical_cast_default<unsigned int>(components[i]);
118  }
119 }
120 
121 std::string version_info::str() const
122 {
123  const std::size_t s = nums_.size();
124 
125  std::ostringstream o;
126  for(std::size_t k = 0; k < s; ++k) {
127  o << nums_[k];
128 
129  if(s != 1+k) {
130  o << '.';
131  }
132  }
133 
134  if(! special_.empty()) {
135  if(special_separator_ != '\0') {
136  o << special_separator_;
137  }
138 
139  o << special_;
140  }
141 
142  return o.str();
143 }
144 
145 void version_info::set_major_version(unsigned int v) {
146  nums_[0] = v;
147 }
148 
149 void version_info::set_minor_version(unsigned int v) {
150  nums_[1] = v;
151 }
152 
153 void version_info::set_revision_level(unsigned int v) {
154  nums_[2] = v;
155 }
156 
157 unsigned int version_info::major_version() const {
158  return nums_[0];
159 }
160 
161 unsigned int version_info::minor_version() const {
162  return nums_[1];
163 }
164 
165 unsigned int version_info::revision_level() const {
166  return nums_[2];
167 }
168 
170  return nums_.size() <= 3;
171 }
172 
174  return is_canonical() && is_odd(minor_version());
175 }
176 
177 namespace {
178  template<template<typename> class Fcn>
179  bool version_comparison_internal(const version_info& l, const version_info& r)
180  {
181  std::vector<unsigned int> lc = l.components();
182  std::vector<unsigned int> rc = r.components();
183 
184  const std::size_t lsize = lc.size();
185  const std::size_t rsize = rc.size();
186  const std::size_t csize = std::max(lsize, rsize);
187 
188  // make compatible, missing items default to zero
189  if(lsize < csize) lc.resize(csize, 0);
190  if(rsize < csize) rc.resize(csize, 0);
191 
192  using comp_list = std::vector<unsigned int>;
193  using comp_pair = std::tuple<const comp_list&, const std::string&>;
194  Fcn<comp_pair> comp;
195 
196  const comp_pair& lp = std::tie(lc, l.special_version());
197  const comp_pair& rp = std::tie(rc, r.special_version());
198  return comp(lp, rp);
199  }
200 } // end unnamed namespace
201 
202 bool operator==(const version_info& l, const version_info& r)
203 {
204  return version_comparison_internal<std::equal_to>(l, r);
205 }
206 
207 bool operator!=(const version_info& l, const version_info& r)
208 {
209  return version_comparison_internal<std::not_equal_to>(l, r);
210 }
211 
212 bool operator<(const version_info& l, const version_info& r)
213 {
214  return version_comparison_internal<std::less>(l, r);
215 }
216 
217 bool operator>(const version_info& l, const version_info& r)
218 {
219  return version_comparison_internal<std::greater>(l, r);
220 }
221 
222 bool operator<=(const version_info& l, const version_info& r)
223 {
224  return version_comparison_internal<std::less_equal>(l, r);
225 }
226 
227 bool operator>=(const version_info& l, const version_info& r)
228 {
229  return version_comparison_internal<std::greater_equal>(l, r);
230 }
231 
232 VERSION_COMP_OP parse_version_op(const std::string& op_str)
233 {
234  if(op_str == "==") {
235  return OP_EQUAL;
236  } else if(op_str == "!=") {
237  return OP_NOT_EQUAL;
238  } else if(op_str == "<") {
239  return OP_LESS;
240  } else if(op_str == "<=") {
241  return OP_LESS_OR_EQUAL;
242  } else if(op_str == ">") {
243  return OP_GREATER;
244  } else if(op_str == ">=") {
245  return OP_GREATER_OR_EQUAL;
246  }
247 
248  return OP_INVALID;
249 }
250 
252 {
253  switch(op) {
254  case OP_EQUAL:
255  return a == b;
256  case OP_NOT_EQUAL:
257  return a != b;
258  case OP_LESS:
259  return a < b;
260  case OP_LESS_OR_EQUAL:
261  return a <= b;
262  case OP_GREATER:
263  return a > b;
264  case OP_GREATER_OR_EQUAL:
265  return a >= b;
266  default:
267  ;
268  }
269 
270  return false;
271 }
std::string special_
VERSION_COMP_OP
bool is_odd(T num)
Definition: math.hpp:35
Interfaces for manipulating version numbers of engine, add-ons, etc.
VERSION_COMP_OP parse_version_op(const std::string &op_str)
New lexcical_cast header.
#define a
void set_major_version(unsigned int)
Sets the major version number.
unsigned int revision_level() const
Retrieves the revision level (x3 in "x1.x2.x3").
void set_revision_level(unsigned int)
Sets the revision level.
STL namespace.
bool do_version_check(const version_info &a, VERSION_COMP_OP op, const version_info &b)
char special_separator_
#define b
const std::string & special_version() const
Retrieves the special version suffix (e.g.
unsigned int major_version() const
Retrieves the major version number (x1 in "x1.x2.x3").
bool operator>=(const version_info &l, const version_info &r)
Greater-than-or-equal operator for version_info.
bool operator<=(const version_info &l, const version_info &r)
Less-than-or-equal operator for version_info.
const version_info test_version("test")
const std::vector< unsigned int > & components() const
Read-only access to all numeric components.
bool operator==(const version_info &l, const version_info &r)
Equality operator for version_info.
bool operator<(const version_info &l, const version_info &r)
Less-than operator for version_info.
unsigned int minor_version() const
Retrieves the minor version number (x2 in "x1.x2.x3").
General math utility functions.
void set_minor_version(unsigned int)
Sets the minor version number.
bool operator!=(const version_info &l, const version_info &r)
Inequality operator for version_info.
bool is_canonical() const
Whether the version number is considered canonical for mainline Wesnoth.
#define MIN_SAVEGAME_VERSION
Some older savegames of Wesnoth cannot be loaded anymore, this variable defines the minimum required ...
Definition: wesconfig.h:33
Some defines: VERSION, PACKAGE, MIN_SAVEGAME_VERSION.
std::size_t i
Definition: function.cpp:940
const std::string revision
#define VERSION
Definition: wesconfig.h:43
Game configuration data as global variables.
Definition: build_info.cpp:58
bool is_dev_version() const
Whether this version represents a development version of Wesnoth aka whether the minor version odd...
static map_location::DIRECTION s
const version_info wesnoth_version(VERSION)
Represents version numbers.
std::vector< std::string > split(const config_attribute_value &val)
std::string str() const
Serializes the version number into string form.
void trim(std::string_view &s)
version_info()
Default constructor.
const version_info min_savegame_version(MIN_SAVEGAME_VERSION)
std::vector< unsigned int > nums_
bool operator>(const version_info &l, const version_info &r)
Greater-than operator for version_info.