The Battle for Wesnoth 1.21.0-dev
 
Loading...
Searching...
No Matches
version.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2015 - 2025
3 by Iris Morelle <shadowm2006@gmail.com>
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#define GETTEXT_DOMAIN "wesnoth-lib"
17
18#include "desktop/version.hpp"
19
20#include "filesystem.hpp"
21#include "formatter.hpp"
22#include "gettext.hpp"
23#include "log.hpp"
25
26#include <cstring>
27
28#if defined(__APPLE__) || defined(_X11)
29#include <sys/utsname.h>
30#endif
31
32#if defined(__APPLE__)
33
34#include "apple_version.hpp"
36
37#include <map>
38#include <boost/algorithm/string/trim.hpp>
39
40#elif defined(_X11)
41
43
44#include <cerrno>
45#include <map>
46#include <boost/algorithm/string/trim.hpp>
47
48#endif
49
50#ifdef _WIN32
51#ifndef UNICODE
52#define UNICODE
53#endif
54#define WIN32_LEAN_AND_MEAN
55
56#include <windows.h>
57
58#endif
59
60#ifdef __ANDROID__
61#include <SDL3/SDL_system.h>
62#endif
63
64static lg::log_domain log_desktop("desktop");
65#define ERR_DU LOG_STREAM(err, log_desktop)
66#define LOG_DU LOG_STREAM(info, log_desktop)
67
68namespace desktop
69{
70
71namespace
72{
73
74#ifdef _WIN32
75/**
76 * Detects whether we are running on Wine or not.
77 *
78 * This is for informational purposes only and all Windows code should assume
79 * we are running on the real thing instead.
80 */
81bool on_wine()
82{
83 HMODULE ntdll = GetModuleHandle(L"ntdll.dll");
84 if(!ntdll) {
85 return false;
86 }
87
88 return GetProcAddress(ntdll, "wine_get_version") != nullptr;
89}
90
91/**
92 * Tries to find out the Windows 10 release number.
93 *
94 * This depends on the registry having special information in it. This may or
95 * may not break in the future or be faked by the application compatibility
96 * layer. Take with a grain of salt.
97 */
98std::string windows_release_id()
99{
100 char buf[256]{""};
101 DWORD size = sizeof(buf);
102
103 auto res = RegGetValueA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", "DisplayVersion", RRF_RT_REG_SZ, nullptr, buf, &size);
104 if(res != ERROR_SUCCESS) {
105 res = RegGetValueA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", "ReleaseId", RRF_RT_REG_SZ, nullptr, buf, &size);
106 }
107
108 return std::string{res == ERROR_SUCCESS ? buf : ""};
109}
110
111std::string windows_runtime_arch()
112{
113 SYSTEM_INFO si;
114 SecureZeroMemory(&si, sizeof(SYSTEM_INFO));
115 GetNativeSystemInfo(&si);
116
117 switch(si.wProcessorArchitecture) {
118 case PROCESSOR_ARCHITECTURE_INTEL:
119 return "x86";
120 case PROCESSOR_ARCHITECTURE_AMD64:
121 return "x86_64";
122 case PROCESSOR_ARCHITECTURE_ARM:
123 return "arm";
124 case PROCESSOR_ARCHITECTURE_ARM64:
125 return "arm64";
126 case PROCESSOR_ARCHITECTURE_IA64:
127 return "ia64";
128 default:
129 return _("cpu_architecture^<unknown>");
130 }
131}
132
133#endif
134
135#if defined(_X11)
136/**
137 * Release policy for POSIX pipe streams opened with popen(3).
138 */
139struct posix_pipe_release_policy
140{
141 void operator()(std::FILE* f) const { if(f != nullptr) { pclose(f); } }
142};
143
144/**
145 * Scoped POSIX pipe stream.
146 *
147 * The stream object type is the same as a regular file stream, but the release
148 * policy is different, as required by popen(3).
149 */
150typedef std::unique_ptr<std::FILE, posix_pipe_release_policy> scoped_posix_pipe;
151
152/**
153 * Read a single line from the specified pipe.
154 *
155 * @returns An empty string if the pipe is invalid or nothing could be read.
156 */
157std::string read_pipe_line(scoped_posix_pipe& p)
158{
159 if(!p.get()) {
160 return "";
161 }
162
163 std::string ver;
164 int c;
165
166 ver.reserve(64);
167
168 // We only want the first line.
169 while((c = std::fgetc(p.get())) && c != EOF && c != '\n' && c != '\r') {
170 ver.push_back(static_cast<char>(c));
171 }
172
173 return ver;
174}
175
176std::map<std::string, std::string> parse_fdo_osrelease(const std::string& path)
177{
178 auto in = filesystem::istream_file(path);
179 if(!in->good()) {
180 return {};
181 }
182
183 std::map<std::string, std::string> res;
184
185 // NOTE: intentionally basic "parsing" here. We are not supposed to see
186 // more complex shell syntax anyway.
187 // <https://www.freedesktop.org/software/systemd/man/os-release.html>
188 for(std::string s; std::getline(*in, s);) {
189 if(s.empty() || s.front() == '#') {
190 continue;
191 }
192
193 auto eqsign_pos = s.find('=');
194 if(!eqsign_pos || eqsign_pos == std::string::npos) {
195 continue;
196 }
197
198 auto lhs = s.substr(0, eqsign_pos),
199 rhs = eqsign_pos + 1 < s.length() ? utils::unescape(s.substr(eqsign_pos + 1)) : "";
200
201 boost::algorithm::trim(lhs);
202 boost::algorithm::trim(rhs);
203
204 // Unquote if the quotes match on both sides
205 if(rhs.length() >= 2 && rhs.front() == '"' && rhs.back() == '"') {
206 rhs.pop_back();
207 rhs.erase(0, 1);
208 }
209
210 res.emplace(std::move(lhs), std::move(rhs));
211 }
212
213 return res;
214}
215
216#endif
217
218} // end anonymous namespace
219
220std::string os_version()
221{
222#if defined(__APPLE__) || defined(_X11)
223 // Some systems, e.g. SunOS, need "struct" here
224 struct utsname u;
225
226 if(uname(&u) != 0) {
227 ERR_DU << "os_version: uname error (" << strerror(errno) << ")";
228 }
229#endif
230
231#if defined(__APPLE__)
232
233 //
234 // Standard Mac OS X version
235 //
236
237 return desktop::apple::os_version() + " " + u.machine;
238
239#elif defined(_X11)
240
241 //
242 // systemd/freedesktop.org method.
243 //
244
245 std::map<std::string, std::string> osrel;
246
247 static const std::string fdo_osrel_etc = "/etc/os-release";
248 static const std::string fdo_osrel_usr = "/usr/lib/os-release";
249
250 if(filesystem::file_exists(fdo_osrel_etc)) {
251 osrel = parse_fdo_osrelease(fdo_osrel_etc);
252 } else if(filesystem::file_exists(fdo_osrel_usr)) {
253 osrel = parse_fdo_osrelease(fdo_osrel_usr);
254 }
255
256 // Check both existence and emptiness in case some vendor sets PRETTY_NAME=""
257 auto osrel_distname = osrel["PRETTY_NAME"];
258 if(osrel_distname.empty()) {
259 osrel_distname = osrel["NAME"];
260 }
261
262 if(!osrel_distname.empty()) {
263 return osrel_distname + " " + u.machine;
264 }
265
266 //
267 // Linux Standard Base fallback.
268 //
269
270 static const std::string lsb_release_bin = "/usr/bin/lsb_release";
271
272 if(filesystem::file_exists(lsb_release_bin)) {
273 static const std::string cmdline = lsb_release_bin + " -s -d";
274
275 scoped_posix_pipe p(popen(cmdline.c_str(), "r"));
276 std::string ver = read_pipe_line(p);
277
278 if(ver.length() >= 2 && ver[0] == '"' && ver[ver.length() - 1] == '"') {
279 ver.erase(ver.length() - 1, 1);
280 ver.erase(0, 1);
281 }
282
283 // Check this again in case we got "" above for some weird reason.
284 if(!ver.empty()) {
285 return ver + " " + u.machine;
286 }
287 }
288
289 //
290 // POSIX uname version fallback.
291 //
292
293 return formatter() << u.sysname << ' '
294 << u.release << ' '
295 << u.version << ' '
296 << u.machine;
297
298#elif defined(_WIN32)
299
300 //
301 // Windows version.
302 //
303
304 static const std::string base
305 = !on_wine() ? "Microsoft Windows" : "Wine/Microsoft Windows";
306
307 OSVERSIONINFOEX v;
308
309 SecureZeroMemory(&v, sizeof(OSVERSIONINFOEX));
310 v.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
311
312#ifdef _MSC_VER
313// GetVersionEx is rather problematic, but it works for our usecase.
314// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms724451(v=vs.85).aspx
315// for more info.
316#pragma warning(push)
317#pragma warning(disable:4996)
318#endif
319 if(!GetVersionEx(reinterpret_cast<OSVERSIONINFO*>(&v))) {
320 ERR_DU << "os_version: GetVersionEx error (" << GetLastError() << ')';
321 return base;
322 }
323#ifdef _MSC_VER
324#pragma warning(pop)
325#endif
326
327 const DWORD vnum = v.dwMajorVersion * 100 + v.dwMinorVersion;
328 std::string version;
329
330 switch(vnum)
331 {
332 case 500:
333 version = "2000";
334 break;
335 case 501:
336 version = "XP";
337 break;
338 case 502:
339 // This will misidentify XP x64 but who really cares?
340 version = "Server 2003";
341 break;
342 case 600:
343 if(v.wProductType == VER_NT_WORKSTATION) {
344 version = "Vista";
345 } else {
346 version = "Server 2008";
347 }
348 break;
349 case 601:
350 if(v.wProductType == VER_NT_WORKSTATION) {
351 version = "7";
352 } else {
353 version = "Server 2008 R2";
354 }
355 break;
356 case 602:
357 if(v.wProductType == VER_NT_WORKSTATION) {
358 version = "8";
359 } else {
360 version = "Server 2012";
361 }
362 break;
363 case 603:
364 if(v.wProductType == VER_NT_WORKSTATION) {
365 version = "8.1";
366 } else {
367 version = "Server 2012 R2";
368 }
369 break;
370 case 1000:
371 if(v.wProductType == VER_NT_WORKSTATION) {
372 version = v.dwBuildNumber < 22000 ? "10" : "11";
373 const auto& release_id = windows_release_id();
374 if(!release_id.empty()) {
375 version += ' ';
376 version += release_id;
377 }
378 break;
379 } // else fallback to default
380 [[fallthrough]];
381 default:
382 if(v.wProductType != VER_NT_WORKSTATION) {
383 version = "Server";
384 }
385 }
386
387 if(*v.szCSDVersion) {
388 version += " ";
389 version += unicode_cast<std::string>(std::wstring(v.szCSDVersion));
390 }
391
392 version += " (";
393 // Add internal version numbers.
394 version += formatter()
395 << v.dwMajorVersion << '.'
396 << v.dwMinorVersion << '.'
397 << v.dwBuildNumber;
398 version += ")";
399
400 return base + " " + version + " " + windows_runtime_arch();
401
402#elif defined(__ANDROID__)
403
404 return formatter() << "Android (API level " << SDL_GetAndroidSDKVersion() << ")";
405
406#else
407
408 //
409 // "I don't know where I am" version.
410 //
411
412 ERR_DU << "os_version(): unsupported platform";
413 return _("operating_system^<unknown>");
414
415#endif
416}
417
418} // end namespace desktop
unsigned in
If equal to search_counter, the node is off the list.
std::ostringstream wrapper.
Definition formatter.hpp:40
#define ERR_DU
Platform identification and version information functions.
Declarations for File-IO.
static std::string _(const char *str)
Definition gettext.hpp:100
Standard logging facilities (interface).
std::string os_version()
std::string os_version()
Returns a string with the running OS name and version information.
Definition version.cpp:220
bool file_exists(const std::string &name)
Returns true if a file or directory with such name already exists.
filesystem::scoped_istream istream_file(const std::string &fname, bool treat_failure_as_error=true)
std::string unescape(std::string_view str)
Remove all escape characters (backslash)
mock_party p
static map_location::direction s
static lg::log_domain log_desktop("desktop")
#define f
#define c