Do only one call to GetVersionEx in the common case.
[gnulib.git] / lib / uname.c
1 /* uname replacement.
2    Copyright (C) 2009 Free Software Foundation, Inc.
3
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 3 of the License, or
7    (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
16
17 #include <config.h>
18
19 /* Specification.  */
20 #include <sys/utsname.h>
21
22 /* This file provides an implementation only for the native Windows API.  */
23 #if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
24
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <unistd.h>
29 #include <windows.h>
30
31 /* Mingw headers don't have all the platform codes.  */
32 #ifndef VER_PLATFORM_WIN32_CE
33 # define VER_PLATFORM_WIN32_CE 3
34 #endif
35
36 /* Some headers don't have all the processor architecture codes.  */
37 #ifndef PROCESSOR_ARCHITECTURE_AMD64
38 # define PROCESSOR_ARCHITECTURE_AMD64 9
39 #endif
40 #ifndef PROCESSOR_ARCHITECTURE_IA32_ON_WIN64
41 # define PROCESSOR_ARCHITECTURE_IA32_ON_WIN64 10
42 #endif
43
44 /* Mingw headers don't have the latest processor codes.  */
45 #ifndef PROCESSOR_AMD_X8664
46 # define PROCESSOR_AMD_X8664 8664
47 #endif
48
49 int
50 uname (struct utsname *buf)
51 {
52   OSVERSIONINFO version;
53   OSVERSIONINFOEX versionex;
54   BOOL have_versionex; /* indicates whether versionex is filled */
55   const char *super_version;
56
57   /* Preparation: Fill version and, if possible, also versionex.
58      But try to call GetVersionEx only once in the common case.  */
59   versionex.dwOSVersionInfoSize = sizeof (OSVERSIONINFOEX);
60   have_versionex = GetVersionEx (&versionex);
61   if (have_versionex)
62     {
63       /* We know that OSVERSIONINFO is a subset of OSVERSIONINFOEX.  */
64       memcpy (&version, &versionex, sizeof (OSVERSIONINFO));
65     }
66   else
67     {
68       version.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
69       if (!GetVersionEx (&version))
70         abort ();
71     }
72
73   /* Fill in nodename.  */
74   if (gethostname (buf->nodename, sizeof (buf->nodename)) < 0)
75     strcpy (buf->nodename, "localhost");
76
77   /* Determine major-major Windows version.  */
78   if (version.dwPlatformId == VER_PLATFORM_WIN32_NT)
79     {
80       /* Windows NT or newer.  */
81       super_version = "NT";
82     }
83   else if (version.dwPlatformId == VER_PLATFORM_WIN32_CE)
84     {
85       /* Windows CE or Embedded CE.  */
86       super_version = "CE";
87     }
88   else if (version.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS)
89     {
90       /* Windows 95/98/ME.  */
91       switch (version.dwMinorVersion)
92         {
93         case 0:
94           super_version = "95";
95           break;
96         case 10:
97           super_version = "98";
98           break;
99         case 90:
100           super_version = "ME";
101           break;
102         default:
103           super_version = "";
104           break;
105         }
106     }
107   else
108     super_version = "";
109
110   /* Fill in sysname.  */
111 #ifdef __MINGW32__
112   /* Returns a string compatible with the MSYS uname.exe program,
113      so that no further changes are needed to GNU config.guess.
114      For example,
115        $ ./uname.exe -s      => MINGW32_NT-5.1
116    */
117   sprintf (buf->sysname, "MINGW32_%s-%u.%u", super_version,
118            (unsigned int) version.dwMajorVersion,
119            (unsigned int) version.dwMinorVersion);
120 #else
121   sprintf (buf->sysname, "Windows%s", super_version);
122 #endif
123
124   /* Fill in release, version.  */
125   /* The MSYS uname.exe programs uses strings from a modified Cygwin runtime:
126        $ ./uname.exe -r      => 1.0.11(0.46/3/2)
127        $ ./uname.exe -v      => 2008-08-25 23:40
128      There is no point in imitating this behaviour.  */
129   if (version.dwPlatformId == VER_PLATFORM_WIN32_NT)
130     {
131       /* Windows NT or newer.  */
132       if (version.dwMajorVersion <= 4)
133         sprintf (buf->release, "Windows NT %u.%u",
134                  (unsigned int) version.dwMajorVersion,
135                  (unsigned int) version.dwMinorVersion);
136       else if (version.dwMajorVersion == 5)
137         switch (version.dwMinorVersion)
138           {
139           case 0:
140             strcpy (buf->release, "Windows 2000");
141             break;
142           case 1:
143             strcpy (buf->release, "Windows XP");
144             break;
145           case 2:
146             strcpy (buf->release, "Windows Server 2003");
147             break;
148           default:
149             strcpy (buf->release, "Windows");
150             break;
151           }
152       else if (version.dwMajorVersion == 6)
153         {
154           if (have_versionex && versionex.wProductType != VER_NT_WORKSTATION)
155             strcpy (buf->release, "Windows Server 2008");
156           else
157             switch (version.dwMinorVersion)
158               {
159               case 0:
160                 strcpy (buf->release, "Windows Vista");
161                 break;
162               case 1:
163               default: /* versions not yet known */
164                 strcpy (buf->release, "Windows 7");
165                 break;
166               }
167         }
168       else
169         strcpy (buf->release, "Windows");
170     }
171   else if (version.dwPlatformId == VER_PLATFORM_WIN32_CE)
172     {
173       /* Windows CE or Embedded CE.  */
174       sprintf (buf->release, "Windows CE %u.%u",
175                (unsigned int) version.dwMajorVersion,
176                (unsigned int) version.dwMinorVersion);
177     }
178   else
179     {
180       /* Windows 95/98/ME.  */
181       sprintf (buf->release, "Windows %s", super_version);
182     }
183   strcpy (buf->version, version.szCSDVersion);
184
185   /* Fill in machine.  */
186   {
187     SYSTEM_INFO info;
188
189     GetSystemInfo (&info);
190     /* Check for Windows NT or CE, since the info.wProcessorLevel is
191        garbage on Windows 95. */
192     if (version.dwPlatformId == VER_PLATFORM_WIN32_NT
193         || version.dwPlatformId == VER_PLATFORM_WIN32_CE)
194       {
195         /* Windows NT or newer, or Windows CE or Embedded CE.  */
196         switch (info.wProcessorArchitecture)
197           {
198           case PROCESSOR_ARCHITECTURE_AMD64:
199             strcpy (buf->machine, "x86_64");
200             break;
201           case PROCESSOR_ARCHITECTURE_IA64:
202             strcpy (buf->machine, "ia64");
203             break;
204           case PROCESSOR_ARCHITECTURE_INTEL:
205             strcpy (buf->machine, "i386");
206             if (info.wProcessorLevel >= 3)
207               buf->machine[1] =
208                 '0' + (info.wProcessorLevel <= 6 ? info.wProcessorLevel : 6);
209             break;
210           case PROCESSOR_ARCHITECTURE_IA32_ON_WIN64:
211             strcpy (buf->machine, "i686");
212             break;
213           case PROCESSOR_ARCHITECTURE_MIPS:
214             strcpy (buf->machine, "mips");
215             break;
216           case PROCESSOR_ARCHITECTURE_ALPHA:
217           case PROCESSOR_ARCHITECTURE_ALPHA64:
218             strcpy (buf->machine, "alpha");
219             break;
220           case PROCESSOR_ARCHITECTURE_PPC:
221             strcpy (buf->machine, "powerpc");
222             break;
223           case PROCESSOR_ARCHITECTURE_SHX:
224             strcpy (buf->machine, "sh");
225             break;
226           case PROCESSOR_ARCHITECTURE_ARM:
227             strcpy (buf->machine, "arm");
228             break;
229           default:
230             strcpy (buf->machine, "unknown");
231             break;
232           }
233       }
234     else
235       {
236         /* Windows 95/98/ME.  */
237         switch (info.dwProcessorType)
238           {
239           case PROCESSOR_AMD_X8664:
240             strcpy (buf->machine, "x86_64");
241             break;
242           case PROCESSOR_INTEL_IA64:
243             strcpy (buf->machine, "ia64");
244             break;
245           default:
246             if (info.dwProcessorType % 100 == 86)
247               sprintf (buf->machine, "i%u",
248                        (unsigned int) info.dwProcessorType);
249             else
250               strcpy (buf->machine, "unknown");
251             break;
252           }
253       }
254   }
255
256   return 0;
257 }
258
259 #endif