The Battle for Wesnoth  1.13.10+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
version.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2015 - 2017 by Ignacio Riquelme Morelle <shadowm2006@gmail.com>
3  Part of the Battle for Wesnoth Project http://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 #define GETTEXT_DOMAIN "wesnoth-lib"
16 
17 #include "desktop/version.hpp"
18 
19 #include "filesystem.hpp"
20 #include "formatter.hpp"
21 #include "gettext.hpp"
22 #include "log.hpp"
24 #include "../version.hpp"
25 
26 #include <cstring>
27 
28 #if defined(_X11) || defined(__APPLE__)
29 
30 #include <cerrno>
31 #include <sys/utsname.h>
32 
33 #endif
34 
35 #ifdef _WIN32
36 
37 #ifndef UNICODE
38 #define UNICODE
39 #endif
40 #define WIN32_LEAN_AND_MEAN
41 
42 #include <windows.h>
43 
44 #endif
45 
46 static lg::log_domain log_desktop("desktop");
47 #define ERR_DU LOG_STREAM(err, log_desktop)
48 #define LOG_DU LOG_STREAM(info, log_desktop)
49 
50 namespace desktop
51 {
52 
53 namespace
54 {
55 
56 #ifdef _WIN32
57 /**
58  * Detects whether we are running on Wine or not.
59  *
60  * This is for informational purposes only and all Windows code should assume
61  * we are running on the real thing instead.
62  */
63 bool on_wine()
64 {
65  HMODULE ntdll = GetModuleHandle(L"ntdll.dll");
66  if(!ntdll) {
67  return false;
68  }
69 
70  return GetProcAddress(ntdll, "wine_get_version") != nullptr;
71 }
72 #endif
73 
74 #if defined(_X11) || defined(__APPLE__)
75 /**
76  * Release policy for POSIX pipe streams opened with popen(3).
77  */
78 struct posix_pipe_release_policy
79 {
80  void operator()(std::FILE* f) const { if(f != nullptr) { pclose(f); } }
81 };
82 
83 /**
84  * Scoped POSIX pipe stream.
85  *
86  * The stream object type is the same as a regular file stream, but the release
87  * policy is different, as required by popen(3).
88  */
89 typedef std::unique_ptr<std::FILE, posix_pipe_release_policy> scoped_posix_pipe;
90 
91 /**
92  * Read a single line from the specified pipe.
93  *
94  * @returns An empty string if the pipe is invalid or nothing could be read.
95  */
96 std::string read_pipe_line(scoped_posix_pipe& p)
97 {
98  if(!p.get()) {
99  return "";
100  }
101 
102  std::string ver;
103  int c;
104 
105  ver.reserve(64);
106 
107  // We only want the first line.
108  while((c = std::fgetc(p.get())) && c != EOF && c != '\n' && c != '\r') {
109  ver.push_back(static_cast<char>(c));
110  }
111 
112  return ver;
113 }
114 #endif
115 
116 } // end anonymous namespace
117 
119 {
120 #if defined(_X11) || defined(__APPLE__)
121 
122 #ifdef __APPLE__
123 
124  //
125  // Standard Mac OS X version
126  //
127 
128  static const std::string version_plist = "/System/Library/CoreServices/SystemVersion.plist";
129  static const std::string defaults_bin = "/usr/bin/defaults";
130 
131  if(filesystem::file_exists(defaults_bin) && filesystem::file_exists(version_plist)) {
132  static const std::string cmdline
133  = defaults_bin + " read " + version_plist + " ProductUserVisibleVersion";
134 
135  scoped_posix_pipe p(popen(cmdline.c_str(), "r"));
136  const std::string& ver = read_pipe_line(p);
137 
138  if(!ver.empty()) {
139  const version_info version(ver);
140  if (version.major_version() == 10 && version.minor_version() < 12) {
141  return "Apple OS X " + ver;
142  } else {
143  return "Apple macOS " + ver;
144  }
145  }
146  }
147 
148 #else
149 
150  //
151  // Linux Standard Base version.
152  //
153 
154  static const std::string lsb_release_bin = "/usr/bin/lsb_release";
155 
156  if(filesystem::file_exists(lsb_release_bin)) {
157  static const std::string cmdline = lsb_release_bin + " -s -d";
158 
159  scoped_posix_pipe p(popen(cmdline.c_str(), "r"));
160  std::string ver = read_pipe_line(p);
161 
162  if(ver.length() >= 2 && ver[0] == '"' && ver[ver.length() - 1] == '"') {
163  ver.erase(ver.length() - 1, 1);
164  ver.erase(0, 1);
165  }
166 
167  // Check this again in case we got "" above for some weird reason.
168  if(!ver.empty()) {
169  return ver;
170  }
171  }
172 #endif
173 
174  //
175  // POSIX uname version.
176  //
177 
178  utsname u;
179 
180  if(uname(&u) != 0) {
181  ERR_DU << "os_version: uname error (" << strerror(errno) << ")\n";
182  }
183 
184  return formatter() << u.sysname << ' '
185  << u.release << ' '
186  << u.version << ' '
187  << u.machine;
188 
189 #elif defined(_WIN32)
190 
191  //
192  // Windows version.
193  //
194 
195  static const std::string base
196  = !on_wine() ? "Microsoft Windows" : "Wine/Microsoft Windows";
197 
198  OSVERSIONINFOEX v { sizeof(OSVERSIONINFOEX) };
199 
200  if(!GetVersionEx(reinterpret_cast<OSVERSIONINFO*>(&v))) {
201  ERR_DU << "os_version: GetVersionEx error ("
202  << GetLastError() << ")\n";
203  return base;
204  }
205 
206  const DWORD vnum = v.dwMajorVersion * 100 + v.dwMinorVersion;
208 
209  switch(vnum)
210  {
211  case 500:
212  version = "2000";
213  break;
214  case 501:
215  version = "XP";
216  break;
217  case 502:
218  // This will misidentify XP x64 but who really cares?
219  version = "Server 2003";
220  break;
221  case 600:
222  if(v.wProductType == VER_NT_WORKSTATION) {
223  version = "Vista";
224  } else {
225  version = "Server 2008";
226  }
227  break;
228  case 601:
229  if(v.wProductType == VER_NT_WORKSTATION) {
230  version = "7";
231  } else {
232  version = "Server 2008 R2";
233  }
234  break;
235  case 602:
236  if(v.wProductType == VER_NT_WORKSTATION) {
237  version = "8";
238  } else {
239  version = "Server 2012";
240  }
241  break;
242  case 603:
243  if(v.wProductType == VER_NT_WORKSTATION) {
244  version = "8.1";
245  } else {
246  version = "Server 2012 R2";
247  }
248  break;
249  case 1000:
250  if(v.wProductType == VER_NT_WORKSTATION) {
251  version = "10";
252  break;
253  } // else fallback to default
254  default:
255  if(v.wProductType != VER_NT_WORKSTATION) {
256  version = "Server";
257  }
258  }
259 
260  if(v.szCSDVersion && *v.szCSDVersion) {
261  version += " ";
262  version += unicode_cast<std::string>(std::wstring(v.szCSDVersion));
263  }
264 
265  version += " (";
266  // Add internal version numbers.
267  version += formatter()
268  << v.dwMajorVersion << '.'
269  << v.dwMinorVersion << '.'
270  << v.dwBuildNumber;
271  version += ")";
272 
273  return base + " " + version;
274 
275 #else
276 
277  //
278  // "I don't know where I am" version.
279  //
280 
281  ERR_DU << "os_version(): unsupported platform\n";
282  return _("operating_system^<unknown>");
283 
284 #endif
285 }
286 
287 } // end namespace desktop
288 
std::vector< char_t > string
static lg::log_domain log_desktop("desktop")
ucs4_convert_impl::enableif< TD, typename TS::value_type >::type unicode_cast(const TS &source)
static UNUSEDNOWARN std::string _(const char *str)
Definition: gettext.hpp:89
std::ostringstream wrapper.
Definition: formatter.hpp:38
Platform identification and version information functions.
#define ERR_DU
Definition: version.cpp:47
mock_party p
unsigned int minor_version() const
Retrieves the minor version number (x2 in "x1.x2.x3").
Definition: version.cpp:132
std::string os_version()
Returns a string with the running OS name and version information.
Definition: version.cpp:118
unsigned int major_version() const
Retrieves the major version number (x1 in "x1.x2.x3").
Definition: version.cpp:128
Declarations for File-IO.
Represents version numbers.
Definition: version.hpp:43
#define f
Standard logging facilities (interface).
mock_char c
bool file_exists(const std::string &name)
Returns true if a file or directory with such name already exists.
const std::string version
Definition: game_config.cpp:39