utimens: cache whether utimensat syscall works
[pspp] / lib / uname.c
index 1eec7e2d2870b0b8d770375a1aad26413bc51ad0..4e79ba1b4e7647975b237a824759ab31190ac579 100644 (file)
 #if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
 
 #include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
 #include <unistd.h>
 #include <windows.h>
 
-/* Mingw headers don't have latest processor codes.  */
+/* Mingw headers don't have all the platform codes.  */
+#ifndef VER_PLATFORM_WIN32_CE
+# define VER_PLATFORM_WIN32_CE 3
+#endif
+
+/* Some headers don't have all the processor architecture codes.  */
+#ifndef PROCESSOR_ARCHITECTURE_AMD64
+# define PROCESSOR_ARCHITECTURE_AMD64 9
+#endif
+#ifndef PROCESSOR_ARCHITECTURE_IA32_ON_WIN64
+# define PROCESSOR_ARCHITECTURE_IA32_ON_WIN64 10
+#endif
+
+/* Mingw headers don't have the latest processor codes.  */
 #ifndef PROCESSOR_AMD_X8664
 # define PROCESSOR_AMD_X8664 8664
 #endif
@@ -35,44 +50,59 @@ int
 uname (struct utsname *buf)
 {
   OSVERSIONINFO version;
-  BOOL have_version;
+  OSVERSIONINFOEX versionex;
+  BOOL have_versionex; /* indicates whether versionex is filled */
   const char *super_version;
 
+  /* Preparation: Fill version and, if possible, also versionex.
+     But try to call GetVersionEx only once in the common case.  */
+  versionex.dwOSVersionInfoSize = sizeof (OSVERSIONINFOEX);
+  have_versionex = GetVersionEx ((OSVERSIONINFO *) &versionex);
+  if (have_versionex)
+    {
+      /* We know that OSVERSIONINFO is a subset of OSVERSIONINFOEX.  */
+      memcpy (&version, &versionex, sizeof (OSVERSIONINFO));
+    }
+  else
+    {
+      version.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
+      if (!GetVersionEx (&version))
+       abort ();
+    }
+
   /* Fill in nodename.  */
   if (gethostname (buf->nodename, sizeof (buf->nodename)) < 0)
     strcpy (buf->nodename, "localhost");
 
   /* Determine major-major Windows version.  */
-  version.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
-  have_version = GetVersionEx (&version);
-  if (have_version)
+  if (version.dwPlatformId == VER_PLATFORM_WIN32_NT)
     {
-      if (version.dwPlatformId == VER_PLATFORM_WIN32_NT)
-       {
-         /* Windows NT or newer.  */
-         super_version = "NT";
-       }
-      else if (version.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS)
+      /* Windows NT or newer.  */
+      super_version = "NT";
+    }
+  else if (version.dwPlatformId == VER_PLATFORM_WIN32_CE)
+    {
+      /* Windows CE or Embedded CE.  */
+      super_version = "CE";
+    }
+  else if (version.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS)
+    {
+      /* Windows 95/98/ME.  */
+      switch (version.dwMinorVersion)
        {
-         /* Windows 95/98/ME.  */
-         switch (version.dwMinorVersion)
-           {
-           case 0:
-             super_version = "95";
-             break;
-           case 10:
-             super_version = "98";
-             break;
-           case 90:
-             super_version = "ME";
-             break;
-           default:
-             super_version = "";
-             break;
-           }
+       case 0:
+         super_version = "95";
+         break;
+       case 10:
+         super_version = "98";
+         break;
+       case 90:
+         super_version = "ME";
+         break;
+       default:
+         super_version = "";
+         break;
        }
-      else
-       super_version = "";
     }
   else
     super_version = "";
@@ -84,17 +114,11 @@ uname (struct utsname *buf)
      For example,
        $ ./uname.exe -s      => MINGW32_NT-5.1
    */
-  if (have_version)
-    sprintf (buf->sysname, "MINGW32_%s-%u.%u", super_version,
-            (unsigned int) version.dwMajorVersion,
-            (unsigned int) version.dwMinorVersion);
-  else
-    strcpy (buf->sysname, "MINGW32");
+  sprintf (buf->sysname, "MINGW32_%s-%u.%u", super_version,
+          (unsigned int) version.dwMajorVersion,
+          (unsigned int) version.dwMinorVersion);
 #else
-  if (have_version)
-    sprintf (buf->sysname, "Windows%s", super_version);
-  else
-    strcpy (buf->sysname, "Windows");
+  sprintf (buf->sysname, "Windows%s", super_version);
 #endif
 
   /* Fill in release, version.  */
@@ -102,77 +126,82 @@ uname (struct utsname *buf)
        $ ./uname.exe -r      => 1.0.11(0.46/3/2)
        $ ./uname.exe -v      => 2008-08-25 23:40
      There is no point in imitating this behaviour.  */
-  if (have_version)
+  if (version.dwPlatformId == VER_PLATFORM_WIN32_NT)
     {
-      if (version.dwPlatformId == VER_PLATFORM_WIN32_NT)
+      /* Windows NT or newer.  */
+      struct windows_version
        {
-         /* Windows NT or newer.  */
-         if (version.dwMajorVersion <= 4)
-           sprintf (buf->release, "Windows NT %u.%u",
-                    (unsigned int) version.dwMajorVersion,
-                    (unsigned int) version.dwMinorVersion);
-         else if (version.dwMajorVersion == 5)
-           switch (version.dwMinorVersion)
-             {
-             case 0:
-               strcpy (buf->release, "Windows 2000");
-               break;
-             case 1:
-               strcpy (buf->release, "Windows XP");
-               break;
-             case 2:
-               strcpy (buf->release, "Windows Server 2003");
-               break;
-             default:
-               strcpy (buf->release, "Windows");
-               break;
-             }
-         else if (version.dwMajorVersion == 6)
-           {
-             OSVERSIONINFOEX versionex;
-
-             versionex.dwOSVersionInfoSize = sizeof (OSVERSIONINFOEX);
-             if (GetVersionEx ((OSVERSIONINFO *) &versionex)
-                 && versionex.wProductType != VER_NT_WORKSTATION)
-               strcpy (buf->release, "Windows Server 2008");
-             else
-               switch (version.dwMinorVersion)
-                 {
-                 case 0:
-                   strcpy (buf->release, "Windows Vista");
-                   break;
-                 case 1:
-                 default: /* versions not yet known */
-                   strcpy (buf->release, "Windows 7");
-                   break;
-                 }
-           }
-         else
-           strcpy (buf->release, "Windows");
-       }
-      else
+         int major;
+         int minor;
+         unsigned int server_offset;
+         const char *name;
+       };
+
+      /* Storing the workstation and server version names in a single
+         stream does not waste memory when they are the same.  These
+         macros abstract the representation.  VERSION1 is used if
+        version.wProductType does not matter, VERSION2 if it does.  */
+      #define VERSION1(major, minor, name) \
+       { major, minor, 0, name }
+      #define VERSION2(major, minor, workstation, server) \
+       { major, minor, sizeof workstation, workstation "\0" server }
+      static const struct windows_version versions[] =
        {
-         /* Windows 95/98/ME.  */
-         sprintf (buf->release, "Windows %s", super_version);
-       }
-      strcpy (buf->version, version.szCSDVersion);
+         VERSION2 (3, -1, "Windows NT Workstation", "Windows NT Server"),
+         VERSION2 (4, -1, "Windows NT Workstation", "Windows NT Server"),
+         VERSION1 (5, 0, "Windows 2000"),
+         VERSION1 (5, 1, "Windows XP"),
+         VERSION1 (5, 2, "Windows Server 2003"),
+         VERSION2 (6, 0, "Windows Vista", "Windows Server 2008"),
+         VERSION2 (6, 1, "Windows 7", "Windows Server 2008 R2"),
+         VERSION2 (-1, -1, "Windows", "Windows Server")
+       };
+      const char *base;
+      const struct windows_version *v = versions;
+
+      /* Find a version that matches ours.  The last element is a
+         wildcard that always ends the loop.  */
+      while ((v->major != version.dwMajorVersion && v->major != -1)
+            || (v->minor != version.dwMinorVersion && v->minor != -1))
+       v++;
+
+      if (have_versionex && versionex.wProductType != VER_NT_WORKSTATION)
+       base = v->name + v->server_offset;
+      else
+       base = v->name;
+      if (v->major == -1 || v->minor == -1)
+       sprintf (buf->release, "%s %u.%u",
+                base,
+                (unsigned int) version.dwMajorVersion,
+                (unsigned int) version.dwMinorVersion);
+      else
+       strcpy (buf->release, base);
+    }
+  else if (version.dwPlatformId == VER_PLATFORM_WIN32_CE)
+    {
+      /* Windows CE or Embedded CE.  */
+      sprintf (buf->release, "Windows CE %u.%u",
+              (unsigned int) version.dwMajorVersion,
+              (unsigned int) version.dwMinorVersion);
     }
   else
     {
-      strcpy (buf->release, "Windows");
-      strcpy (buf->version, "");
+      /* Windows 95/98/ME.  */
+      sprintf (buf->release, "Windows %s", super_version);
     }
+  strcpy (buf->version, version.szCSDVersion);
 
   /* Fill in machine.  */
   {
     SYSTEM_INFO info;
 
     GetSystemInfo (&info);
-    /* Check for Windows NT, since the info.wProcessorLevel is
+    /* Check for Windows NT or CE, since the info.wProcessorLevel is
        garbage on Windows 95. */
-    if (have_version && version.dwPlatformId == VER_PLATFORM_WIN32_NT)
+    if (version.dwPlatformId == VER_PLATFORM_WIN32_NT
+       || version.dwPlatformId == VER_PLATFORM_WIN32_CE)
       {
-       /* Windows NT or newer.  */
+       /* Windows NT or newer, or Windows CE or Embedded CE.  */
        switch (info.wProcessorArchitecture)
          {
          case PROCESSOR_ARCHITECTURE_AMD64:
@@ -187,6 +216,25 @@ uname (struct utsname *buf)
              buf->machine[1] =
                '0' + (info.wProcessorLevel <= 6 ? info.wProcessorLevel : 6);
            break;
+         case PROCESSOR_ARCHITECTURE_IA32_ON_WIN64:
+           strcpy (buf->machine, "i686");
+           break;
+         case PROCESSOR_ARCHITECTURE_MIPS:
+           strcpy (buf->machine, "mips");
+           break;
+         case PROCESSOR_ARCHITECTURE_ALPHA:
+         case PROCESSOR_ARCHITECTURE_ALPHA64:
+           strcpy (buf->machine, "alpha");
+           break;
+         case PROCESSOR_ARCHITECTURE_PPC:
+           strcpy (buf->machine, "powerpc");
+           break;
+         case PROCESSOR_ARCHITECTURE_SHX:
+           strcpy (buf->machine, "sh");
+           break;
+         case PROCESSOR_ARCHITECTURE_ARM:
+           strcpy (buf->machine, "arm");
+           break;
          default:
            strcpy (buf->machine, "unknown");
            break;