text: /* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
   *
   * Permission is hereby granted, free of charge, to any person obtaining a copy
   * of this software and associated documentation files (the "Software"), to
   * deal in the Software without restriction, including without limitation the
   * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
   * sell copies of the Software, and to permit persons to whom the Software is
   * furnished to do so, subject to the following conditions:
   *
   * The above copyright notice and this permission notice shall be included in
   * all copies or substantial portions of the Software.
   *
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
   * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
   * IN THE SOFTWARE.
   */
  
  #include <assert.h>
  #include <direct.h>
  #include <limits.h>
  #include <malloc.h>
  #include <stdio.h>
  #include <string.h>
  #include <time.h>
  #include <wchar.h>
  
  #include "uv.h"
  #include "internal.h"
  
  #include <winsock2.h>
  #include <winperf.h>
  #include <iphlpapi.h>
  #include <psapi.h>
  #include <tlhelp32.h>
  #include <windows.h>
  
  
  /*
   * Max title length; the only thing MSDN tells us about the maximum length
   * of the console title is that it is smaller than 64K. However in practice
   * it is much smaller, and there is no way to figure out what the exact length
   * of the title is or can be, at least not on XP. To make it even more
   * annoying, GetConsoleTitle failes when the buffer to be read into is bigger
   * than the actual maximum length. So we make a conservative guess here;
   * just don't put the novel you're writing in the title, unless the plot
   * survives truncation.
   */
  #define MAX_TITLE_LENGTH 8192
  
  /* The number of nanoseconds in one second. */
  #define UV__NANOSEC 1000000000
  
  
  /* Cached copy of the process title, plus a mutex guarding it. */
  static char *process_title;
  static CRITICAL_SECTION process_title_lock;
  
  /* Interval (in seconds) of the high-resolution clock. */
  static double hrtime_interval_ = 0;
  
  
  /*
   * One-time intialization code for functionality defined in util.c.
   */
  void uv__util_init() {
    LARGE_INTEGER perf_frequency;
  
    /* Initialize process title access mutex. */
    InitializeCriticalSection(&process_title_lock);
  
    /* Retrieve high-resolution timer frequency
     * and precompute its reciprocal. 
     */
    if (QueryPerformanceFrequency(&perf_frequency)) {
      hrtime_interval_ = 1.0 / perf_frequency.QuadPart;
    } else {
      hrtime_interval_= 0;
    }
  }
  
  
  int uv_utf16_to_utf8(const WCHAR* utf16Buffer, size_t utf16Size,
      char* utf8Buffer, size_t utf8Size) {
    return WideCharToMultiByte(CP_UTF8,
                               0,
                               utf16Buffer,
                               utf16Size,
                               utf8Buffer,
                               utf8Size,
                               NULL,
                               NULL);
  }
  
  
  int uv_utf8_to_utf16(const char* utf8Buffer, WCHAR* utf16Buffer,
      size_t utf16Size) {
    return MultiByteToWideChar(CP_UTF8,
                               0,
                               utf8Buffer,
                               -1,
                               utf16Buffer,
                               utf16Size);
  }
  
  
  int uv_exepath(char* buffer, size_t* size_ptr) {
    int utf8_len, utf16_buffer_len, utf16_len;
    WCHAR* utf16_buffer;
    int err;
  
    if (buffer == NULL || size_ptr == NULL || *size_ptr == 0) {
      return UV_EINVAL;
    }
  
    if (*size_ptr > 32768) {
      /* Windows paths can never be longer than this. */
      utf16_buffer_len = 32768;
    } else {
      utf16_buffer_len = (int) *size_ptr;
    }
  
    utf16_buffer = (WCHAR*) malloc(sizeof(WCHAR) * utf16_buffer_len);
    if (!utf16_buffer) {
      return UV_ENOMEM;
    }
  
    /* Get the path as UTF-16. */
    utf16_len = GetModuleFileNameW(NULL, utf16_buffer, utf16_buffer_len);
    if (utf16_len <= 0) {
      err = GetLastError();
      goto error;
    }
  
    /* utf16_len contains the length, *not* including the terminating null. */
    utf16_buffer[utf16_len] = L'\0';
  
    /* Convert to UTF-8 */
    utf8_len = WideCharToMultiByte(CP_UTF8,
                                   0,
                                   utf16_buffer,
                                   -1,
                                   buffer,
                                   *size_ptr > INT_MAX ? INT_MAX : (int) *size_ptr,
                                   NULL,
                                   NULL);
    if (utf8_len == 0) {
      err = GetLastError();
      goto error;
    }
  
    free(utf16_buffer);
  
    /* utf8_len *does* include the terminating null at this point, but the */
    /* returned size shouldn't. */
    *size_ptr = utf8_len - 1;
    return 0;
  
   error:
    free(utf16_buffer);
    return uv_translate_sys_error(err);
  }
  
  
  int uv_cwd(char* buffer, size_t* size) {
    DWORD utf16_len;
    WCHAR utf16_buffer[MAX_PATH];
    int r;
  
    if (buffer == NULL || size == NULL) {
      return UV_EINVAL;
    }
  
    utf16_len = GetCurrentDirectoryW(MAX_PATH, utf16_buffer);
    if (utf16_len == 0) {
      return uv_translate_sys_error(GetLastError());
    } else if (utf16_len > MAX_PATH) {
      /* This should be impossible;  however the CRT has a code path to deal */
      /* with this scenario, so I added a check anyway. */
      return UV_EIO;
    }
  
    /* utf16_len contains the length, *not* including the terminating null. */
    utf16_buffer[utf16_len] = L'\0';
  
    /* The returned directory should not have a trailing slash, unless it */
    /* points at a drive root, like c:\. Remove it if needed.*/
    if (utf16_buffer[utf16_len - 1] == L'\\' &&
        !(utf16_len == 3 && utf16_buffer[1] == L':')) {
      utf16_len--;
      utf16_buffer[utf16_len] = L'\0';
    }
  
    /* Check how much space we need */
    r = WideCharToMultiByte(CP_UTF8,
                            0,
                            utf16_buffer,
                            -1,
                            NULL,
                            0,
                            NULL,
                            NULL);
    if (r == 0) {
      return uv_translate_sys_error(GetLastError());
    } else if (r > (int) *size) {
      *size = r -1;
      return UV_ENOBUFS;
    }
  
    /* Convert to UTF-8 */
    r = WideCharToMultiByte(CP_UTF8,
                            0,
                            utf16_buffer,
                            -1,
                            buffer,
                            *size > INT_MAX ? INT_MAX : (int) *size,
                            NULL,
                            NULL);
    if (r == 0) {
      return uv_translate_sys_error(GetLastError());
    }
  
    *size = r - 1;
    return 0;
  }
  
  
  int uv_chdir(const char* dir) {
    WCHAR utf16_buffer[MAX_PATH];
    size_t utf16_len;
    WCHAR drive_letter, env_var[4];
  
    if (dir == NULL) {
      return UV_EINVAL;
    }
  
    if (MultiByteToWideChar(CP_UTF8,
                            0,
                            dir,
                            -1,
                            utf16_buffer,
                            MAX_PATH) == 0) {
      DWORD error = GetLastError();
      /* The maximum length of the current working directory is 260 chars, */
      /* including terminating null. If it doesn't fit, the path name must be */
      /* too long. */
      if (error == ERROR_INSUFFICIENT_BUFFER) {
        return UV_ENAMETOOLONG;
      } else {
        return uv_translate_sys_error(error);
      }
    }
  
    if (!SetCurrentDirectoryW(utf16_buffer)) {
      return uv_translate_sys_error(GetLastError());
    }
  
    /* Windows stores the drive-local path in an "hidden" environment variable, */
    /* which has the form "=C:=C:\Windows". SetCurrentDirectory does not */
    /* update this, so we'll have to do it. */
    utf16_len = GetCurrentDirectoryW(MAX_PATH, utf16_buffer);
    if (utf16_len == 0) {
      return uv_translate_sys_error(GetLastError());
    } else if (utf16_len > MAX_PATH) {
      return UV_EIO;
    }
  
    /* The returned directory should not have a trailing slash, unless it */
    /* points at a drive root, like c:\. Remove it if needed. */
    if (utf16_buffer[utf16_len - 1] == L'\\' &&
        !(utf16_len == 3 && utf16_buffer[1] == L':')) {
      utf16_len--;
      utf16_buffer[utf16_len] = L'\0';
    }
  
    if (utf16_len < 2 || utf16_buffer[1] != L':') {
      /* Doesn't look like a drive letter could be there - probably an UNC */
      /* path. TODO: Need to handle win32 namespaces like \\?\C:\ ? */
      drive_letter = 0;
    } else if (utf16_buffer[0] >= L'A' && utf16_buffer[0] <= L'Z') {
      drive_letter = utf16_buffer[0];
    } else if (utf16_buffer[0] >= L'a' && utf16_buffer[0] <= L'z') {
      /* Convert to uppercase. */
      drive_letter = utf16_buffer[0] - L'a' + L'A';
    } else {
      /* Not valid. */
      drive_letter = 0;
    }
  
    if (drive_letter != 0) {
      /* Construct the environment variable name and set it. */
      env_var[0] = L'=';
      env_var[1] = drive_letter;
      env_var[2] = L':';
      env_var[3] = L'\0';
  
      if (!SetEnvironmentVariableW(env_var, utf16_buffer)) {
        return uv_translate_sys_error(GetLastError());
      }
    }
  
    return 0;
  }
  
  
  void uv_loadavg(double avg[3]) {
    /* Can't be implemented */
    avg[0] = avg[1] = avg[2] = 0;
  }
  
  
  uint64_t uv_get_free_memory(void) {
    MEMORYSTATUSEX memory_status;
    memory_status.dwLength = sizeof(memory_status);
  
    if (!GlobalMemoryStatusEx(&memory_status)) {
       return -1;
    }
  
    return (uint64_t)memory_status.ullAvailPhys;
  }
  
  
  uint64_t uv_get_total_memory(void) {
    MEMORYSTATUSEX memory_status;
    memory_status.dwLength = sizeof(memory_status);
  
    if (!GlobalMemoryStatusEx(&memory_status)) {
      return -1;
    }
  
    return (uint64_t)memory_status.ullTotalPhys;
  }
  
  
  int uv_parent_pid() {
    int parent_pid = -1;
    HANDLE handle;
    PROCESSENTRY32 pe;
    DWORD current_pid = GetCurrentProcessId();
  
    pe.dwSize = sizeof(PROCESSENTRY32);
    handle = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
  
    if (Process32First(handle, &pe)) {
      do {
        if (pe.th32ProcessID == current_pid) {
          parent_pid = pe.th32ParentProcessID;
          break;
        }
      } while( Process32Next(handle, &pe));
    }
  
    CloseHandle(handle);
    return parent_pid;
  }
  
  
  char** uv_setup_args(int argc, char** argv) {
    return argv;
  }
  
  
  int uv_set_process_title(const char* title) {
    int err;
    int length;
    WCHAR* title_w = NULL;
  
    uv__once_init();
  
    /* Find out how big the buffer for the wide-char title must be */
    length = uv_utf8_to_utf16(title, NULL, 0);
    if (!length) {
      err = GetLastError();
      goto done;
    }
  
    /* Convert to wide-char string */
    title_w = (WCHAR*)malloc(sizeof(WCHAR) * length);
    if (!title_w) {
      uv_fatal_error(ERROR_OUTOFMEMORY, "malloc");
    }
  
    length = uv_utf8_to_utf16(title, title_w, length);
    if (!length) {
      err = GetLastError();
      goto done;
    }
  
    /* If the title must be truncated insert a \0 terminator there */
    if (length > MAX_TITLE_LENGTH) {
      title_w[MAX_TITLE_LENGTH - 1] = L'\0';
    }
  
    if (!SetConsoleTitleW(title_w)) {
      err = GetLastError();
      goto done;
    }
  
    EnterCriticalSection(&process_title_lock);
    free(process_title);
    process_title = strdup(title);
    LeaveCriticalSection(&process_title_lock);
  
    err = 0;
  
  done:
    free(title_w);
    return uv_translate_sys_error(err);
  }
  
  
  static int uv__get_process_title() {
    WCHAR title_w[MAX_TITLE_LENGTH];
    int length;
  
    if (!GetConsoleTitleW(title_w, sizeof(title_w) / sizeof(WCHAR))) {
      return -1;
    }
  
    /* Find out what the size of the buffer is that we need */
    length = uv_utf16_to_utf8(title_w, -1, NULL, 0);
    if (!length) {
      return -1;
    }
  
    assert(!process_title);
    process_title = (char*)malloc(length);
    if (!process_title) {
      uv_fatal_error(ERROR_OUTOFMEMORY, "malloc");
    }
  
    /* Do utf16 -> utf8 conversion here */
    if (!uv_utf16_to_utf8(title_w, -1, process_title, length)) {
      free(process_title);
      return -1;
    }
  
    return 0;
  }
  
  
  int uv_get_process_title(char* buffer, size_t size) {
    uv__once_init();
  
    EnterCriticalSection(&process_title_lock);
    /*
     * If the process_title was never read before nor explicitly set,
     * we must query it with getConsoleTitleW
     */
    if (!process_title && uv__get_process_title() == -1) {
      LeaveCriticalSection(&process_title_lock);
      return uv_translate_sys_error(GetLastError());
    }
  
    assert(process_title);
    strncpy(buffer, process_title, size);
    LeaveCriticalSection(&process_title_lock);
  
    return 0;
  }
  
  
  uint64_t uv_hrtime(void) {
    uv__once_init();
    return uv__hrtime(UV__NANOSEC);
  }
  
  uint64_t uv__hrtime(double scale) {
    LARGE_INTEGER counter;
  
    /* If the performance interval is zero, there's no support. */
    if (hrtime_interval_ == 0) {
      return 0;
    }
  
    if (!QueryPerformanceCounter(&counter)) {
      return 0;
    }
  
    /* Because we have no guarantee about the order of magnitude of the
     * performance counter interval, integer math could cause this computation
     * to overflow. Therefore we resort to floating point math.
     */
    return (uint64_t) ((double) counter.QuadPart * hrtime_interval_ * scale);
  }
  
  
  int uv_resident_set_memory(size_t* rss) {
    HANDLE current_process;
    PROCESS_MEMORY_COUNTERS pmc;
  
    current_process = GetCurrentProcess();
  
    if (!GetProcessMemoryInfo(current_process, &pmc, sizeof(pmc))) {
      return uv_translate_sys_error(GetLastError());
    }
  
    *rss = pmc.WorkingSetSize;
  
    return 0;
  }
  
  
  int uv_uptime(double* uptime) {
    BYTE stack_buffer[4096];
    BYTE* malloced_buffer = NULL;
    BYTE* buffer = (BYTE*) stack_buffer;
    size_t buffer_size = sizeof(stack_buffer);
    DWORD data_size;
  
    PERF_DATA_BLOCK* data_block;
    PERF_OBJECT_TYPE* object_type;
    PERF_COUNTER_DEFINITION* counter_definition;
  
    DWORD i;
  
    for (;;) {
      LONG result;
  
      data_size = (DWORD) buffer_size;
      result = RegQueryValueExW(HKEY_PERFORMANCE_DATA,
                                L"2",
                                NULL,
                                NULL,
                                buffer,
                                &data_size);
      if (result == ERROR_SUCCESS) {
        break;
      } else if (result != ERROR_MORE_DATA) {
        *uptime = 0;
        return uv_translate_sys_error(result);
      }
  
      free(malloced_buffer);
  
      buffer_size *= 2;
      /* Don't let the buffer grow infinitely. */
      if (buffer_size > 1 << 20) {
        goto internalError;
      }
  
      buffer = malloced_buffer = (BYTE*) malloc(buffer_size);
      if (malloced_buffer == NULL) {
        *uptime = 0;
        return UV_ENOMEM;
      }
    }
  
    if (data_size < sizeof(*data_block))
      goto internalError;
  
    data_block = (PERF_DATA_BLOCK*) buffer;
  
    if (wmemcmp(data_block->Signature, L"PERF", 4) != 0)
      goto internalError;
  
    if (data_size < data_block->HeaderLength + sizeof(*object_type))
      goto internalError;
  
    object_type = (PERF_OBJECT_TYPE*) (buffer + data_block->HeaderLength);
  
    if (object_type->NumInstances != PERF_NO_INSTANCES)
      goto internalError;
  
    counter_definition = (PERF_COUNTER_DEFINITION*) (buffer +
        data_block->HeaderLength + object_type->HeaderLength);
    for (i = 0; i < object_type->NumCounters; i++) {
      if ((BYTE*) counter_definition + sizeof(*counter_definition) >
          buffer + data_size) {
        break;
      }
  
      if (counter_definition->CounterNameTitleIndex == 674 &&
          counter_definition->CounterSize == sizeof(uint64_t)) {
        if (counter_definition->CounterOffset + sizeof(uint64_t) > data_size ||
            !(counter_definition->CounterType & PERF_OBJECT_TIMER)) {
          goto internalError;
        } else {
          BYTE* address = (BYTE*) object_type + object_type->DefinitionLength +
                          counter_definition->CounterOffset;
          uint64_t value = *((uint64_t*) address);
          *uptime = (double) (object_type->PerfTime.QuadPart - value) /
                    (double) object_type->PerfFreq.QuadPart;
          free(malloced_buffer);
          return 0;
        }
      }
  
      counter_definition = (PERF_COUNTER_DEFINITION*)
          ((BYTE*) counter_definition + counter_definition->ByteLength);
    }
  
    /* If we get here, the uptime value was not found. */
    free(malloced_buffer);
    *uptime = 0;
    return UV_ENOSYS;
  
   internalError:
    free(malloced_buffer);
    *uptime = 0;
    return UV_EIO;
  }
  
  
  int uv_cpu_info(uv_cpu_info_t** cpu_infos_ptr, int* cpu_count_ptr) {
    uv_cpu_info_t* cpu_infos;
    SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION* sppi;
    DWORD sppi_size;
    SYSTEM_INFO system_info;
    DWORD cpu_count, r, i;
    NTSTATUS status;
    ULONG result_size;
    int err;
    uv_cpu_info_t* cpu_info;
  
    cpu_infos = NULL;
    cpu_count = 0;
    sppi = NULL;
  
    uv__once_init();
  
    GetSystemInfo(&system_info);
    cpu_count = system_info.dwNumberOfProcessors;
  
    cpu_infos = calloc(cpu_count, sizeof *cpu_infos);
    if (cpu_infos == NULL) {
      err = ERROR_OUTOFMEMORY;
      goto error;
    }
  
    sppi_size = cpu_count * sizeof(*sppi);
    sppi = malloc(sppi_size);
    if (sppi == NULL) {
      err = ERROR_OUTOFMEMORY;
      goto error;
    }
  
    status = pNtQuerySystemInformation(SystemProcessorPerformanceInformation,
                                       sppi,
                                       sppi_size,
                                       &result_size);
    if (!NT_SUCCESS(status)) {
      err = pRtlNtStatusToDosError(status);
      goto error;
    }
  
    assert(result_size == sppi_size);
  
    for (i = 0; i < cpu_count; i++) {
      WCHAR key_name[128];
      HKEY processor_key;
      DWORD cpu_speed;
      DWORD cpu_speed_size = sizeof(cpu_speed);
      WCHAR cpu_brand[256];
      DWORD cpu_brand_size = sizeof(cpu_brand);
      size_t len;
  
      len = _snwprintf(key_name,
                       ARRAY_SIZE(key_name),
                       L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\%d",
                       i);
  
      assert(len > 0 && len < ARRAY_SIZE(key_name));
  
      r = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
                        key_name,
                        0,
                        KEY_QUERY_VALUE,
                        &processor_key);
      if (r != ERROR_SUCCESS) {
        err = GetLastError();
        goto error;
      }
  
      if (RegQueryValueExW(processor_key,
                           L"~MHz",
                           NULL,
                           NULL,
                           (BYTE*) &cpu_speed,
                           &cpu_speed_size) != ERROR_SUCCESS) {
        err = GetLastError();
        RegCloseKey(processor_key);
        goto error;
      }
  
      if (RegQueryValueExW(processor_key,
                           L"ProcessorNameString",
                           NULL,
                           NULL,
                           (BYTE*) &cpu_brand,
                           &cpu_brand_size) != ERROR_SUCCESS) {
        err = GetLastError();
        RegCloseKey(processor_key);
        goto error;
      }
  
      RegCloseKey(processor_key);
  
      cpu_info = &cpu_infos[i];
      cpu_info->speed = cpu_speed;
      cpu_info->cpu_times.user = sppi[i].UserTime.QuadPart / 10000;
      cpu_info->cpu_times.sys = (sppi[i].KernelTime.QuadPart -
          sppi[i].IdleTime.QuadPart) / 10000;
      cpu_info->cpu_times.idle = sppi[i].IdleTime.QuadPart / 10000;
      cpu_info->cpu_times.irq = sppi[i].InterruptTime.QuadPart / 10000;
      cpu_info->cpu_times.nice = 0;
  
  
      len = WideCharToMultiByte(CP_UTF8,
                                0,
                                cpu_brand,
                                cpu_brand_size / sizeof(WCHAR),
                                NULL,
                                0,
                                NULL,
                                NULL);
      if (len == 0) {
        err = GetLastError();
        goto error;
      }
  
      assert(len > 0);
  
      /* Allocate 1 extra byte for the null terminator. */
      cpu_info->model = malloc(len + 1);
      if (cpu_info->model == NULL) {
        err = ERROR_OUTOFMEMORY;
        goto error;
      }
  
      if (WideCharToMultiByte(CP_UTF8,
                              0,
                              cpu_brand,
                              cpu_brand_size / sizeof(WCHAR),
                              cpu_info->model,
                              len,
                              NULL,
                              NULL) == 0) {
        err = GetLastError();
        goto error;
      }
  
      /* Ensure that cpu_info->model is null terminated. */
      cpu_info->model[len] = '\0';
    }
  
    free(sppi);
  
    *cpu_count_ptr = cpu_count;
    *cpu_infos_ptr = cpu_infos;
  
    return 0;
  
   error:
    /* This is safe because the cpu_infos array is zeroed on allocation. */
    for (i = 0; i < cpu_count; i++)
      free(cpu_infos[i].model);
  
    free(cpu_infos);
    free(sppi);
  
    return uv_translate_sys_error(err);
  }
  
  
  void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count) {
    int i;
  
    for (i = 0; i < count; i++) {
      free(cpu_infos[i].model);
    }
  
    free(cpu_infos);
  }
  
  
  static int is_windows_version_or_greater(DWORD os_major,
                                           DWORD os_minor,
                                           WORD service_pack_major,
                                           WORD service_pack_minor) {
    OSVERSIONINFOEX osvi;
    DWORDLONG condition_mask = 0;
    int op = VER_GREATER_EQUAL;
  
    /* Initialize the OSVERSIONINFOEX structure. */
    ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
    osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
    osvi.dwMajorVersion = os_major;
    osvi.dwMinorVersion = os_minor;
    osvi.wServicePackMajor = service_pack_major;
    osvi.wServicePackMinor = service_pack_minor;
  
    /* Initialize the condition mask. */
    VER_SET_CONDITION(condition_mask, VER_MAJORVERSION, op);
    VER_SET_CONDITION(condition_mask, VER_MINORVERSION, op);
    VER_SET_CONDITION(condition_mask, VER_SERVICEPACKMAJOR, op);
    VER_SET_CONDITION(condition_mask, VER_SERVICEPACKMINOR, op);
  
    /* Perform the test. */
    return (int) VerifyVersionInfo(
      &osvi, 
      VER_MAJORVERSION | VER_MINORVERSION | 
      VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR,
      condition_mask);
  }
  
  
  static int address_prefix_match(int family,
                                  struct sockaddr* address,
                                  struct sockaddr* prefix_address,
                                  int prefix_len) {
    uint8_t* address_data;
    uint8_t* prefix_address_data;
    int i;
  
    assert(address->sa_family == family);
    assert(prefix_address->sa_family == family);
  
    if (family == AF_INET6) {
      address_data = (uint8_t*) &(((struct sockaddr_in6 *) address)->sin6_addr);
      prefix_address_data =
        (uint8_t*) &(((struct sockaddr_in6 *) prefix_address)->sin6_addr);
    } else {
      address_data = (uint8_t*) &(((struct sockaddr_in *) address)->sin_addr);
      prefix_address_data =
        (uint8_t*) &(((struct sockaddr_in *) prefix_address)->sin_addr);
    }
  
    for (i = 0; i < prefix_len >> 3; i++) {
      if (address_data[i] != prefix_address_data[i])
        return 0;
    }
  
    if (prefix_len % 8)
      return prefix_address_data[i] ==
        (address_data[i] & (0xff << (8 - prefix_len % 8)));
  
    return 1;
  }
  
  
  int uv_interface_addresses(uv_interface_address_t** addresses_ptr,
      int* count_ptr) {
    IP_ADAPTER_ADDRESSES* win_address_buf;
    ULONG win_address_buf_size;
    IP_ADAPTER_ADDRESSES* adapter;
  
    uv_interface_address_t* uv_address_buf;
    char* name_buf;
    size_t uv_address_buf_size;
    uv_interface_address_t* uv_address;
  
    int count;
  
    int is_vista_or_greater;
    ULONG flags;
  
    is_vista_or_greater = is_windows_version_or_greater(6, 0, 0, 0);
    if (is_vista_or_greater) {
      flags = GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST |
        GAA_FLAG_SKIP_DNS_SERVER;
    } else {
      /* We need at least XP SP1. */
      if (!is_windows_version_or_greater(5, 1, 1, 0))
        return UV_ENOTSUP;
  
      flags = GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST |
        GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_INCLUDE_PREFIX;
    }
    
  
    /* Fetch the size of the adapters reported by windows, and then get the */
    /* list itself. */
    win_address_buf_size = 0;
    win_address_buf = NULL;
  
    for (;;) {
      ULONG r;
  
      /* If win_address_buf is 0, then GetAdaptersAddresses will fail with */
      /* ERROR_BUFFER_OVERFLOW, and the required buffer size will be stored in */
      /* win_address_buf_size. */
      r = GetAdaptersAddresses(AF_UNSPEC,
                               flags,
                               NULL,
                               win_address_buf,
                               &win_address_buf_size);
  
      if (r == ERROR_SUCCESS)
        break;
  
      free(win_address_buf);
  
      switch (r) {
        case ERROR_BUFFER_OVERFLOW:
          /* This happens when win_address_buf is NULL or too small to hold */
          /* all adapters. */
          win_address_buf = malloc(win_address_buf_size);
          if (win_address_buf == NULL)
            return UV_ENOMEM;
  
          continue;
  
        case ERROR_NO_DATA: {
          /* No adapters were found. */
          uv_address_buf = malloc(1);
          if (uv_address_buf == NULL)
            return UV_ENOMEM;
  
          *count_ptr = 0;
          *addresses_ptr = uv_address_buf;
  
          return 0;
        }
  
        case ERROR_ADDRESS_NOT_ASSOCIATED:
          return UV_EAGAIN;
  
        case ERROR_INVALID_PARAMETER:
          /* MSDN says:
           *   "This error is returned for any of the following conditions: the
           *   SizePointer parameter is NULL, the Address parameter is not
           *   AF_INET, AF_INET6, or AF_UNSPEC, or the address information for
           *   the parameters requested is greater than ULONG_MAX."
           * Since the first two conditions are not met, it must be that the
           * adapter data is too big.
           */
          return UV_ENOBUFS;
  
        default:
          /* Other (unspecified) errors can happen, but we don't have any */
          /* special meaning for them. */
          assert(r != ERROR_SUCCESS);
          return uv_translate_sys_error(r);
      }
    }
  
    /* Count the number of enabled interfaces and compute how much space is */
    /* needed to store their info. */
    count = 0;
    uv_address_buf_size = 0;
  
    for (adapter = win_address_buf;
         adapter != NULL;
         adapter = adapter->Next) {
      IP_ADAPTER_UNICAST_ADDRESS* unicast_address;
      int name_size;
  
      /* Interfaces that are not 'up' should not be reported. Also skip */
      /* interfaces that have no associated unicast address, as to avoid */
      /* allocating space for the name for this interface. */
      if (adapter->OperStatus != IfOperStatusUp ||
          adapter->FirstUnicastAddress == NULL)
        continue;
  
      /* Compute the size of the interface name. */
      name_size = WideCharToMultiByte(CP_UTF8,
                                      0,
                                      adapter->FriendlyName,
                                      -1,
                                      NULL,
                                      0,
                                      NULL,
                                      FALSE);
      if (name_size <= 0) {
        free(win_address_buf);
        return uv_translate_sys_error(GetLastError());
      }
      uv_address_buf_size += name_size;
  
      /* Count the number of addresses associated with this interface, and */
      /* compute the size. */
      for (unicast_address = (IP_ADAPTER_UNICAST_ADDRESS*)
                             adapter->FirstUnicastAddress;
           unicast_address != NULL;
           unicast_address = unicast_address->Next) {
        count++;
        uv_address_buf_size += sizeof(uv_interface_address_t);
      }
    }
  
    /* Allocate space to store interface data plus adapter names. */
    uv_address_buf = malloc(uv_address_buf_size);
    if (uv_address_buf == NULL) {
      free(win_address_buf);
      return UV_ENOMEM;
    }
  
    /* Compute the start of the uv_interface_address_t array, and the place in */
    /* the buffer where the interface names will be stored. */
    uv_address = uv_address_buf;
    name_buf = (char*) (uv_address_buf + count);
  
    /* Fill out the output buffer. */
    for (adapter = win_address_buf;
         adapter != NULL;
         adapter = adapter->Next) {
      IP_ADAPTER_UNICAST_ADDRESS* unicast_address;
      int name_size;
      size_t max_name_size;
  
      if (adapter->OperStatus != IfOperStatusUp ||
          adapter->FirstUnicastAddress == NULL)
        continue;
  
      /* Convert the interface name to UTF8. */
      max_name_size = (char*) uv_address_buf + uv_address_buf_size - name_buf;
      if (max_name_size > (size_t) INT_MAX)
        max_name_size = INT_MAX;
      name_size = WideCharToMultiByte(CP_UTF8,
                                      0,
                                      adapter->FriendlyName,
                                      -1,
                                      name_buf,
                                      (int) max_name_size,
                                      NULL,
                                      FALSE);
      if (name_size <= 0) {
        free(win_address_buf);
        free(uv_address_buf);
        return uv_translate_sys_error(GetLastError());
      }
  
      /* Add an uv_interface_address_t element for every unicast address. */
      for (unicast_address = (IP_ADAPTER_UNICAST_ADDRESS*)
                             adapter->FirstUnicastAddress;
           unicast_address != NULL;
           unicast_address = unicast_address->Next) {
        struct sockaddr* sa;
        ULONG prefix_len;
  
        sa = unicast_address->Address.lpSockaddr;
  
        /* XP has no OnLinkPrefixLength field. */
        if (is_vista_or_greater) {
          prefix_len = unicast_address->OnLinkPrefixLength;
        } else {
          /* Prior to Windows Vista the FirstPrefix pointed to the list with
           * single prefix for each IP address assigned to the adapter.
           * Order of FirstPrefix does not match order of FirstUnicastAddress,
           * so we need to find corresponding prefix.
           */
          IP_ADAPTER_PREFIX* prefix;
          prefix_len = 0;
  
          for (prefix = adapter->FirstPrefix; prefix; prefix = prefix->Next) {
            /* We want the longest matching prefix. */
            if (prefix->Address.lpSockaddr->sa_family != sa->sa_family ||
                prefix->PrefixLength <= prefix_len)
              continue;
  
            if (address_prefix_match(sa->sa_family, sa, 
                prefix->Address.lpSockaddr, prefix->PrefixLength)) {
              prefix_len = prefix->PrefixLength;
            }
          }
  
          /* If there is no matching prefix information, return a single-host
           * subnet mask (e.g. 255.255.255.255 for IPv4). 
           */
          if (!prefix_len)
            prefix_len = (sa->sa_family == AF_INET6) ? 128 : 32;
        }
  
        memset(uv_address, 0, sizeof *uv_address);
  
        uv_address->name = name_buf;
  
        if (adapter->PhysicalAddressLength == sizeof(uv_address->phys_addr)) {
          memcpy(uv_address->phys_addr,
                 adapter->PhysicalAddress,
                 sizeof(uv_address->phys_addr));
        }
  
        uv_address->is_internal =
            (adapter->IfType == IF_TYPE_SOFTWARE_LOOPBACK);
  
        if (sa->sa_family == AF_INET6) {
          uv_address->address.address6 = *((struct sockaddr_in6 *) sa);
  
          uv_address->netmask.netmask6.sin6_family = AF_INET6;
          memset(uv_address->netmask.netmask6.sin6_addr.s6_addr, 0xff, prefix_len >> 3);
          /* This check ensures that we don't write past the size of the data. */
          if (prefix_len % 8) {
            uv_address->netmask.netmask6.sin6_addr.s6_addr[prefix_len >> 3] =
                0xff << (8 - prefix_len % 8);
          }
  
        } else {
          uv_address->address.address4 = *((struct sockaddr_in *) sa);
  
          uv_address->netmask.netmask4.sin_family = AF_INET;
          uv_address->netmask.netmask4.sin_addr.s_addr = (prefix_len > 0) ?
              htonl(0xffffffff << (32 - prefix_len)) : 0;
        }
  
        uv_address++;
      }
  
      name_buf += name_size;
    }
  
    free(win_address_buf);
  
    *addresses_ptr = uv_address_buf;
    *count_ptr = count;
  
    return 0;
  }
  
  
  void uv_free_interface_addresses(uv_interface_address_t* addresses,
      int count) {
    free(addresses);
  }
  
  
  int uv_getrusage(uv_rusage_t *uv_rusage) {
    FILETIME createTime, exitTime, kernelTime, userTime;
    SYSTEMTIME kernelSystemTime, userSystemTime;
    int ret;
  
    ret = GetProcessTimes(GetCurrentProcess(), &createTime, &exitTime, &kernelTime, &userTime);
    if (ret == 0) {
      return uv_translate_sys_error(GetLastError());
    }
  
    ret = FileTimeToSystemTime(&kernelTime, &kernelSystemTime);
    if (ret == 0) {
      return uv_translate_sys_error(GetLastError());
    }
  
    ret = FileTimeToSystemTime(&userTime, &userSystemTime);
    if (ret == 0) {
      return uv_translate_sys_error(GetLastError());
    }
  
    memset(uv_rusage, 0, sizeof(*uv_rusage));
  
    uv_rusage->ru_utime.tv_sec = userSystemTime.wHour * 3600 +
                                 userSystemTime.wMinute * 60 +
                                 userSystemTime.wSecond;
    uv_rusage->ru_utime.tv_usec = userSystemTime.wMilliseconds * 1000;
  
    uv_rusage->ru_stime.tv_sec = kernelSystemTime.wHour * 3600 +
                                 kernelSystemTime.wMinute * 60 +
                                 kernelSystemTime.wSecond;
    uv_rusage->ru_stime.tv_usec = kernelSystemTime.wMilliseconds * 1000;
  
    return 0;
  }
  