]> sourceware.org Git - newlib-cygwin.git/blobdiff - winsup/utils/cygcheck.cc
Cygwin: add 3.2.1 release file and add fixes up to this point
[newlib-cygwin.git] / winsup / utils / cygcheck.cc
index 58c1a66a02fb1bcc1379f16252509c192fccb18d..a45ab5d4d4f6d03848aef3a83c3edbd5e6774cd5 100644 (file)
@@ -1,13 +1,12 @@
 /* cygcheck.cc
 
-   Copyright 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 Red Hat, Inc.
-
    This file is part of Cygwin.
 
    This software is a copyrighted work licensed under the terms of the
    Cygwin license.  Please consult the file "CYGWIN_LICENSE" for
    details. */
 
+#define _WIN32_WINNT 0x0a00
 #define cygwin_internal cygwin_internal_dontuse
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/time.h>
 #include <ctype.h>
+#include <fcntl.h>
 #include <io.h>
 #include <windows.h>
 #include <wininet.h>
 #include "path.h"
+#include "wide_path.h"
 #include <getopt.h>
-#include "cygwin/include/sys/cygwin.h"
-#include "cygwin/include/mntent.h"
+#include "../cygwin/include/cygwin/version.h"
+#include "../cygwin/include/sys/cygwin.h"
+#define _NOMNTENT_MACROS
+#include "../cygwin/include/mntent.h"
 #undef cygwin_internal
+#include "loadlib.h"
+
+#ifndef max
+#define max __max
+#endif
 
+#ifndef alloca
 #define alloca __builtin_alloca
+#endif
 
 int verbose = 0;
 int registry = 0;
@@ -36,6 +46,9 @@ int dump_only = 0;
 int find_package = 0;
 int list_package = 0;
 int grep_packages = 0;
+int del_orphaned_reg = 0;
+
+static char emptystr[] = "";
 
 #ifdef __GNUC__
 typedef long long longlong;
@@ -43,11 +56,12 @@ typedef long long longlong;
 typedef __int64 longlong;
 #endif
 
+/* In dump_setup.cc  */
 void dump_setup (int, char **, bool);
 void package_find (int, char **);
 void package_list (int, char **);
-
-static const char version[] = "$Revision$";
+/* In bloda.cc  */
+void dump_dodgy_apps (int verbose);
 
 static const char *known_env_vars[] = {
   "c_include_path",
@@ -83,32 +97,64 @@ static common_apps[] = {
   {"awk", 0},
   {"bash", 0},
   {"cat", 0},
+  {"certutil", 0},
+  {"clinfo", 0},
+  {"comp", 0},
+  {"convert", 0},
   {"cp", 0},
   {"cpp", 1},
   {"crontab", 0},
+  {"curl", 0},
+  {"expand", 0},
   {"find", 0},
+  {"ftp", 0},
   {"gcc", 0},
   {"gdb", 0},
   {"grep", 0},
+  {"hostname", 0},
   {"kill", 0},
+  {"klist", 0},
   {"ld", 0},
   {"ls", 0},
   {"make", 0},
   {"mv", 0},
+  {"nslookup", 0},
+  {"patch", 0},
   {"perl", 0},
+  {"replace", 0},
   {"rm", 0},
   {"sed", 0},
-  {"ssh", 0},
   {"sh", 0},
+  {"shutdown", 0},
+  {"sort", 0},
+  {"ssh", 0},
   {"tar", 0},
   {"test", 0},
+  {"timeout", 0},
   {"vi", 0},
   {"vim", 0},
+  {"whoami", 0},
   {0, 0}
 };
 
-static int num_paths = 0, max_paths = 0;
-static char **paths = 0;
+/* Options without ASCII single char representation. */
+enum
+{
+  CO_DELETE_KEYS = 0x100,
+};
+
+static int num_paths, max_paths;
+struct pathlike
+{
+  char *dir;
+  bool issys;
+  void check_existence (const char *fn, int showall, int verbose,
+                       char* first, const char *ext1 = "",
+                       const char *ext2 = "");
+};
+
+pathlike *paths;
+int first_nonsys_path;
 
 void
 eprintf (const char *format, ...)
@@ -123,17 +169,43 @@ eprintf (const char *format, ...)
  * display_error() is used to report failure modes
  */
 static int
-display_error (const char *name, bool show_error = true, bool print_failed = true)
+display_error (const char *name, bool show_error, bool print_failed)
 {
+  fprintf (stderr, "cygcheck: %s", name);
   if (show_error)
-    fprintf (stderr, "cygcheck: %s%s: %lu\n", name,
+    fprintf (stderr, "%s: %lu\n",
        print_failed ? " failed" : "", GetLastError ());
   else
-    fprintf (stderr, "cygcheck: %s%s\n", name,
+    fprintf (stderr, "%s\n",
        print_failed ? " failed" : "");
   return 1;
 }
 
+static int
+display_error (const char *name)
+{
+  return display_error (name, true, true);
+}
+
+static int
+display_error (const char *fmt, const char *x)
+{
+  char buf[4000];
+  snprintf (buf, sizeof buf, fmt, x);
+  return display_error (buf, false, false);
+}
+
+static int
+display_error_fmt (const char *fmt, ...)
+{
+  char buf[4000];
+  va_list va;
+
+  va_start (va, fmt);
+  vsnprintf (buf, sizeof buf, fmt, va);
+  return display_error (buf, false, false);
+}
+
 /* Display a WinInet error message, and close a variable number of handles.
    (Passed a list of handles terminated by NULL.)  */
 static int
@@ -149,12 +221,12 @@ display_internet_error (const char *message, ...)
   if (err)
     {
       if (FormatMessage (FORMAT_MESSAGE_FROM_HMODULE,
-          GetModuleHandle ("wininet.dll"), err, 0, err_buf,
-          sizeof (err_buf), NULL) == 0)
-        strcpy (err_buf, "(Unknown error)");
+         GetModuleHandle ("wininet.dll"), err, 0, err_buf,
+         sizeof (err_buf), NULL) == 0)
+       strcpy (err_buf, "(Unknown error)");
 
-      fprintf (stderr, "cygcheck: %s: %s (win32 error %d)\n", message,
-               err_buf, err);
+      fprintf (stderr, "cygcheck: %s: %s (win32 error %lu)\n", message,
+              err_buf, err);
     }
   else
     fprintf (stderr, "cygcheck: %s\n", message);
@@ -168,33 +240,37 @@ display_internet_error (const char *message, ...)
 }
 
 static void
-add_path (char *s, int maxlen)
+add_path (char *s, int maxlen, bool issys)
 {
   if (num_paths >= max_paths)
     {
       max_paths += 10;
-      if (paths)
-       paths = (char **) realloc (paths, max_paths * sizeof (char *));
-      else
-       paths = (char **) malloc (max_paths * sizeof (char *));
+      /* Extend path array */
+      paths = (pathlike *) realloc (paths, (1 + max_paths) * sizeof (paths[0]));
     }
-  paths[num_paths] = (char *) malloc (maxlen + 1);
-  if (paths[num_paths] == NULL)
+
+  pathlike *pth = paths + num_paths;
+
+  /* Allocate space for directory in path list */
+  char *dir = (char *) calloc (maxlen + 2, sizeof (char));
+  if (dir == NULL)
     {
-      display_error ("add_path: malloc()");
+      display_error ("add_path: calloc() failed");
       return;
     }
-  memcpy (paths[num_paths], s, maxlen);
-  paths[num_paths][maxlen] = 0;
-  char *e = paths[num_paths] + strlen (paths[num_paths]);
-  if (e[-1] == '\\' && e[-2] != ':')
-    *--e = 0;
-  for (int i = 1; i < num_paths; i++)
-    if (strcasecmp (paths[num_paths], paths[i]) == 0)
-      {
-       free (paths[num_paths]);
-       return;
-      }
+
+  /* Copy input directory to path list */
+  memcpy (dir, s, maxlen);
+
+  /* Add a trailing slash by default */
+  char *e = strchr (dir, '\0');
+  if (e != dir && e[-1] != '\\')
+    strcpy (e, "\\");
+
+  /* Fill out this element */
+  pth->dir = dir;
+  pth->issys = issys;
+  pth[1].dir = NULL;
   num_paths++;
 }
 
@@ -202,99 +278,158 @@ static void
 init_paths ()
 {
   char tmp[4000], *sl;
-  add_path ((char *) ".", 1);  /* to be replaced later */
+  add_path ((char *) ".", 1, true);    /* to be replaced later */
 
   if (GetCurrentDirectory (4000, tmp))
-    add_path (tmp, strlen (tmp));
+    add_path (tmp, strlen (tmp), true);
   else
     display_error ("init_paths: GetCurrentDirectory()");
 
   if (GetSystemDirectory (tmp, 4000))
-    add_path (tmp, strlen (tmp));
+    add_path (tmp, strlen (tmp), true);
   else
     display_error ("init_paths: GetSystemDirectory()");
   sl = strrchr (tmp, '\\');
   if (sl)
     {
       strcpy (sl, "\\SYSTEM");
-      add_path (tmp, strlen (tmp));
+      add_path (tmp, strlen (tmp), true);
     }
   GetWindowsDirectory (tmp, 4000);
-  add_path (tmp, strlen (tmp));
+  add_path (tmp, strlen (tmp), true);
 
   char *wpath = getenv ("PATH");
-  if (wpath)
+  if (!wpath)
+    display_error ("WARNING: PATH is not set\n", "");
+  else
     {
       char *b, *e;
       b = wpath;
       while (1)
        {
-         for (e = b; *e && *e != ';'; e++);
-         if (strncmp(b, ".", 1) && strncmp(b, ".\\", 2))
-           add_path (b, e - b);
+         for (e = b; *e && *e != ';'; e++)
+           continue;   /* loop terminates at first ';' or EOS */
+         if (strncmp(b, ".\\", 2) != 0)
+           add_path (b, e - b, false);
          if (!*e)
            break;
          b = e + 1;
        }
     }
-  else
-    printf ("WARNING: PATH is not set at all!\n");
 }
 
-static char *
-find_on_path (char *file, char *default_extension,
-             int showall = 0, int search_sysdirs = 0)
+#define LINK_EXTENSION ".lnk"
+
+void
+pathlike::check_existence (const char *fn, int showall, int verbose,
+                          char* first, const char *ext1, const char *ext2)
+{
+  char file[4000];
+  snprintf (file, sizeof file, "%s%s%s%s", dir, fn, ext1, ext2);
+
+  wide_path wpath (file);
+  if (GetFileAttributesW (wpath) != (DWORD) - 1)
+    {
+      char *lastdot = strrchr (file, '.');
+      bool is_link = lastdot && !strcmp (lastdot, LINK_EXTENSION);
+      // If file is a link, fix up the extension before printing
+      if (is_link)
+       *lastdot = '\0';
+      if (showall)
+       printf ("Found: %s\n", file);
+      if (verbose && *first != '\0' && strcasecmp (first, file) != 0)
+       {
+         char *flastdot = strrchr (first, '.');
+         bool f_is_link = flastdot && !strcmp (flastdot, LINK_EXTENSION);
+         // if first is a link, fix up the extension before printing
+         if (f_is_link)
+           *flastdot = '\0';
+         printf ("Warning: %s hides %s\n", first, file);
+         if (f_is_link)
+           *flastdot = '.';
+       }
+      if (is_link)
+       *lastdot = '.';
+      if (!*first)
+       strcpy (first, file);
+    }
+}
+
+static const char *
+find_on_path (const char *in_file, const char *ext, bool showall = false,
+             bool search_sys = false, bool checklinks = false)
 {
   static char rv[4000];
-  char tmp[4000], *ptr = rv;
 
-  if (!file)
+  /* Sort of a kludge but we've already tested this once, so don't try it
+     again */
+  if (in_file == rv)
+    return in_file;
+
+  static pathlike abspath[2] =
+  {
+    {emptystr, 0},
+    {NULL, 0}
+  };
+
+  *rv = '\0';
+  if (!in_file)
     {
-      display_error ("find_on_path: NULL pointer for file", false, false);
+      display_error ("internal error find_on_path: NULL pointer for file",
+                    false, false);
       return 0;
     }
 
-  if (default_extension == NULL)
+  if (!ext)
     {
-      display_error ("find_on_path: NULL pointer for default_extension", false, false);
+      display_error ("internal error find_on_path: "
+                    "NULL pointer for default_extension", false, false);
       return 0;
     }
 
-  if (strchr (file, ':') || strchr (file, '\\') || strchr (file, '/'))
+  const char *file;
+  pathlike *search_paths;
+  if (!strpbrk (in_file, ":/\\"))
     {
-      char *fn = cygpath (file, NULL);
-      if (access (fn, F_OK) == 0)
-       return fn;
-      strcpy (rv, fn);
-      strcat (rv, default_extension);
-      return access (rv, F_OK) == 0 ? rv : fn;
+      file = in_file;
+      search_paths = paths;
+    }
+  else
+    {
+      file = cygpath (in_file, NULL);
+      search_paths = abspath;
+      showall = false;
     }
 
-  if (strchr (file, '.'))
-    default_extension = (char *) "";
-
-  for (int i = 0; i < num_paths; i++)
+  if (!file)
     {
-      if (!search_sysdirs && (i == 0 || i == 2 || i == 3))
-       continue;
-      if (i == 0 || !search_sysdirs || strcasecmp (paths[i], paths[0]))
-       {
-         sprintf (ptr, "%s\\%s%s", paths[i], file, default_extension);
-         if (GetFileAttributes (ptr) != (DWORD) - 1)
-           {
-             if (showall)
-               printf ("Found: %s\n", ptr);
-             if (ptr == tmp && verbose)
-               printf ("Warning: %s hides %s\n", rv, ptr);
-             ptr = tmp;
-           }
-       }
+      display_error ("internal error find_on_path: "
+                    "cygpath conversion failed for %s\n", in_file);
+      return 0;
     }
 
-  if (ptr == tmp)
-    return rv;
+  char *hasext = strrchr (file, '.');
+  if (hasext && !strpbrk (hasext, "/\\"))
+    ext = "";
 
-  return 0;
+  for (pathlike *pth = search_paths; pth->dir; pth++)
+    if (!pth->issys || search_sys)
+      {
+       pth->check_existence (file, showall, verbose, rv, ext);
+
+       if (checklinks)
+         pth->check_existence (file, showall, verbose, rv, ext,
+                               LINK_EXTENSION);
+
+       if (!*ext)
+         continue;
+
+       pth->check_existence (file, showall, verbose, rv);
+       if (checklinks)
+         pth->check_existence (file, showall, verbose, rv, LINK_EXTENSION);
+      }
+
+  return *rv ? rv : NULL;
 }
 
 #define DID_NEW                1
@@ -310,7 +445,7 @@ struct Did
 static Did *did = 0;
 
 static Did *
-already_did (char *file)
+already_did (const char *file)
 {
   Did *d;
   for (d = did; d; d = d->next)
@@ -324,38 +459,6 @@ already_did (char *file)
   return d;
 }
 
-static int
-get_word (HANDLE fh, int offset)
-{
-  short rv;
-  unsigned r;
-
-  if (SetFilePointer (fh, offset, 0, FILE_BEGIN) == INVALID_SET_FILE_POINTER
-      && GetLastError () != NO_ERROR)
-    display_error ("get_word: SetFilePointer()");
-
-  if (!ReadFile (fh, &rv, 2, (DWORD *) &r, 0))
-    display_error ("get_word: Readfile()");
-
-  return rv;
-}
-
-static int
-get_dword (HANDLE fh, int offset)
-{
-  int rv;
-  unsigned r;
-
-  if (SetFilePointer (fh, offset, 0, FILE_BEGIN) == INVALID_SET_FILE_POINTER
-      && GetLastError () != NO_ERROR)
-    display_error ("get_dword: SetFilePointer()");
-
-  if (!ReadFile (fh, &rv, 4, (DWORD *) &r, 0))
-    display_error ("get_dword: Readfile()");
-
-  return rv;
-}
-
 struct Section
 {
   char name[8];
@@ -413,8 +516,7 @@ struct ImpDirectory
   unsigned iat_rva;
 };
 
-
-static bool track_down (char *file, char *suffix, int lvl);
+static bool track_down (const char *file, const char *suffix, int lvl);
 
 #define CYGPREFIX (sizeof ("%%% Cygwin ") - 1)
 static void
@@ -498,7 +600,39 @@ dll_info (const char *path, HANDLE fh, int lvl, int recurse)
 {
   DWORD junk;
   int i;
+  if (is_symlink (fh))
+    {
+      if (!verbose)
+       puts ("");
+      else
+       {
+         char buf[PATH_MAX + 1] = "";
+         readlink (fh, buf, sizeof(buf) - 1);
+         printf (" (symlink to %s)\n", buf);
+       }
+      return;
+    }
   int pe_header_offset = get_dword (fh, 0x3c);
+  if (GetLastError () != NO_ERROR)
+    display_error ("get_dword");
+  WORD arch = get_word (fh, pe_header_offset + 4);
+  if (GetLastError () != NO_ERROR)
+    display_error ("get_word");
+#ifdef __x86_64__
+  if (arch != IMAGE_FILE_MACHINE_AMD64)
+    {
+      puts (verbose ? " (not x86_64 dll)" : "\n");
+      return;
+    }
+  int base_off = 108;
+#else
+  if (arch != IMAGE_FILE_MACHINE_I386)
+    {
+      puts (verbose ? " (not x86 dll)" : "\n");
+      return;
+    }
+  int base_off = 92;
+#endif
   int opthdr_ofs = pe_header_offset + 4 + 20;
   unsigned short v[6];
 
@@ -521,13 +655,25 @@ dll_info (const char *path, HANDLE fh, int lvl, int recurse)
   else
     printf ("\n");
 
-  int num_entries = get_dword (fh, opthdr_ofs + 92);
-  int export_rva = get_dword (fh, opthdr_ofs + 96);
-  int export_size = get_dword (fh, opthdr_ofs + 100);
-  int import_rva = get_dword (fh, opthdr_ofs + 104);
-  int import_size = get_dword (fh, opthdr_ofs + 108);
+  int num_entries = get_dword (fh, opthdr_ofs + base_off + 0);
+  if (GetLastError () != NO_ERROR)
+    display_error ("get_dword");
+  int export_rva = get_dword (fh, opthdr_ofs + base_off + 4);
+  if (GetLastError () != NO_ERROR)
+    display_error ("get_dword");
+  int export_size = get_dword (fh, opthdr_ofs + base_off + 8);
+  if (GetLastError () != NO_ERROR)
+    display_error ("get_dword");
+  int import_rva = get_dword (fh, opthdr_ofs + base_off + 12);
+  if (GetLastError () != NO_ERROR)
+    display_error ("get_dword");
+  int import_size = get_dword (fh, opthdr_ofs + base_off + 16);
+  if (GetLastError () != NO_ERROR)
+    display_error ("get_dword");
 
   int nsections = get_word (fh, pe_header_offset + 4 + 2);
+  if (nsections == -1)
+    display_error ("get_word");
   char *sections = (char *) malloc (nsections * 40);
 
   if (SetFilePointer (fh, pe_header_offset + 4 + 20 +
@@ -557,17 +703,20 @@ dll_info (const char *path, HANDLE fh, int lvl, int recurse)
 
          ExpDirectory *ed = (ExpDirectory *) exp;
          int ofs = ed->name_rva - export_rva;
-         struct tm *tm = localtime ((const time_t *) &(ed->timestamp));
-         if (tm->tm_year < 60)
+         time_t ts = ed->timestamp;    /* timestamp is only 4 bytes! */
+         struct tm *tm = localtime (&ts);
+         if (tm && tm->tm_year < 60)
            tm->tm_year += 2000;
-         if (tm->tm_year < 200)
+         if (tm && tm->tm_year < 200)
            tm->tm_year += 1900;
          printf ("%*c", lvl + 2, ' ');
-         printf ("\"%s\" v%d.%d ts=", exp + ofs,
+         printf ("\"%s\" v%d.%d", exp + ofs,
                  ed->major_ver, ed->minor_ver);
-         printf ("%d/%d/%d %d:%02d\n",
-                 tm->tm_year, tm->tm_mon + 1, tm->tm_mday,
-                 tm->tm_hour, tm->tm_min);
+         if (tm)
+           printf (" ts=%04d-%02d-%02d %02d:%02d",
+                   tm->tm_year, tm->tm_mon + 1, tm->tm_mday,
+                   tm->tm_hour, tm->tm_min);
+         putchar ('\n');
        }
     }
 
@@ -606,7 +755,7 @@ dll_info (const char *path, HANDLE fh, int lvl, int recurse)
 
 // Return true on success, false if error printed
 static bool
-track_down (char *file, char *suffix, int lvl)
+track_down (const char *file, const char *suffix, int lvl)
 {
   if (file == NULL)
     {
@@ -620,10 +769,15 @@ track_down (char *file, char *suffix, int lvl)
       return false;
     }
 
-  char *path = find_on_path (file, suffix, 0, 1);
+  const char *path = find_on_path (file, suffix, false, true);
   if (!path)
     {
-      printf ("Error: could not find %s\n", file);
+      /* The api-ms-win-*.dll files are in system32/downlevel and not in the
+        DLL search path, so find_on_path doesn't find them.  Since they are
+        never actually linked against by the executables, they are of no
+        interest to us.  Skip any error message in not finding them. */
+      if (strncasecmp (file, "api-ms-win-", 11) || strcasecmp (suffix, ".dll"))
+       display_error ("track_down: could not find %s\n", file);
       return false;
     }
 
@@ -657,26 +811,34 @@ track_down (char *file, char *suffix, int lvl)
   if (lvl)
     printf ("%*c", lvl, ' ');
 
-  if (!path)
-    {
-      printf ("%s not found\n", file);
-      return false;
-    }
-
   printf ("%s", path);
 
+  wide_path wpath (path);
   HANDLE fh =
-    CreateFile (path, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
-               NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+    CreateFileW (wpath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
+                NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
   if (fh == INVALID_HANDLE_VALUE)
     {
-      printf (" - Cannot open\n");
+      display_error ("cannot open - '%s'\n", path);
       return false;
     }
 
   d->state = DID_ACTIVE;
 
-  dll_info (path, fh, lvl, 1);
+  if (is_exe (fh))
+    dll_info (path, fh, lvl, 1);
+  else if (is_symlink (fh))
+    display_error ("%s is a symlink instead of a DLL\n", path);
+  else
+    {
+      int magic = get_word (fh, 0x0);
+      if (magic == -1)
+       display_error ("get_word");
+      magic &= 0x00FFFFFF;
+      display_error_fmt ("%s is not a DLL: magic number %x (%d) '%s'\n",
+                        path, magic, magic, (char *)&magic);
+    }
+
   d->state = DID_INACTIVE;
   if (!CloseHandle (fh))
     display_error ("track_down: CloseHandle()");
@@ -686,8 +848,10 @@ track_down (char *file, char *suffix, int lvl)
 static void
 ls (char *f)
 {
-  HANDLE h = CreateFile (f, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
-                        0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
+  wide_path wpath (f);
+  HANDLE h = CreateFileW (wpath, GENERIC_READ,
+                         FILE_SHARE_READ | FILE_SHARE_WRITE,
+                         0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
   BY_HANDLE_FILE_INFORMATION info;
 
   if (!GetFileInformationByHandle (h, &info))
@@ -705,34 +869,103 @@ ls (char *f)
     display_error ("ls: CloseHandle()");
 }
 
+/* Remove filename from 's' and return directory name without trailing
+   backslash, or NULL if 's' doesn't seem to have a dirname.  */
+static char *
+dirname (const char *s)
+{
+  static char buf[PATH_MAX];
+
+  if (!s)
+    return NULL;
+
+  strncpy (buf, s, PATH_MAX);
+  buf[PATH_MAX - 1] = '\0';   // in case strlen(s) > PATH_MAX
+  char *lastsep = strrchr (buf, '\\');
+  if (!lastsep)
+    return NULL;          // no backslash -> no dirname
+  else if (lastsep - buf <= 2 && buf[1] == ':')
+    lastsep[1] = '\0';    // can't remove backslash of "x:\"
+  else
+    *lastsep = '\0';
+  return buf;
+}
+
+// Find a real application on the path (possibly following symlinks)
+static const char *
+find_app_on_path (const char *app, bool showall = false)
+{
+  const char *papp = find_on_path (app, ".exe", showall, false, true);
+
+  if (!papp)
+    return NULL;
+
+  wide_path wpath (papp);
+  HANDLE fh =
+    CreateFileW (wpath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
+                NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+  if (fh == INVALID_HANDLE_VALUE)
+    return NULL;
+
+  if (is_symlink (fh))
+    {
+      static char tmp[SYMLINK_MAX + 1];
+      if (!readlink (fh, tmp, SYMLINK_MAX))
+       display_error("readlink failed");
+
+      /* Resolve the linkname relative to the directory of the link.  */
+      char *ptr = cygpath_rel (dirname (papp), tmp, NULL);
+      printf (" -> %s\n", ptr);
+      if (!strchr (ptr, '\\'))
+       {
+         char *lastsep;
+         strncpy (tmp, cygpath (papp, NULL), SYMLINK_MAX);
+         lastsep = strrchr (tmp, '\\');
+         strncpy (lastsep+1, ptr, SYMLINK_MAX - (lastsep-tmp));
+         ptr = tmp;
+       }
+      if (!CloseHandle (fh))
+       display_error ("find_app_on_path: CloseHandle()");
+      /* FIXME: We leak the ptr returned by cygpath() here which is a
+        malloc()d string.  */
+      return find_app_on_path (ptr, showall);
+    }
+
+  if (!CloseHandle (fh))
+    display_error ("find_app_on_path: CloseHandle()");
+  return papp;
+}
+
 // Return true on success, false if error printed
 static bool
-cygcheck (char *app)
+cygcheck (const char *app)
 {
-  char *papp = find_on_path (app, (char *) ".exe", 1, 0);
+  const char *papp = find_app_on_path (app, 1);
   if (!papp)
     {
-      printf ("Error: could not find %s\n", app);
+      display_error ("could not find '%s'\n", app);
       return false;
     }
-  char *s = strdup (papp);
-  char *sl = 0, *t;
-  for (t = s; *t; t++)
-    if (*t == '/' || *t == '\\' || *t == ':')
-      sl = t;
-  if (sl == 0)
-    paths[0] = (char *) ".";
+
+  char *s;
+  char *sep = strpbrk (papp, ":/\\");
+  if (!sep)
+    {
+      static char dot[] = ".";
+      s = dot;
+    }
   else
     {
-      *sl = 0;
-      paths[0] = s;
+      int n = sep - papp;
+      s = (char *) malloc (n + 2);
+      memcpy ((char *) s, papp, n);
+      strcpy (s + n, "\\");
     }
-  did = 0;
-  return track_down (papp, (char *) ".exe", 0);
-}
 
-
-extern char **environ;
+  paths[0].dir = s;
+  did = NULL;
+  return track_down (papp, ".exe", 0);
+}
 
 struct RegInfo
 {
@@ -754,7 +987,7 @@ show_reg (RegInfo * ri, int nest)
 }
 
 static void
-scan_registry (RegInfo * prev, HKEY hKey, char *name, int cygnus)
+scan_registry (RegInfo * prev, HKEY hKey, char *name, int cygwin, bool wow64)
 {
   RegInfo ri;
   ri.prev = prev;
@@ -763,8 +996,8 @@ scan_registry (RegInfo * prev, HKEY hKey, char *name, int cygnus)
 
   char *cp;
   for (cp = name; *cp; cp++)
-    if (strncasecmp (cp, "cygnus", 6) == 0)
-      cygnus = 1;
+    if (strncasecmp (cp, "Cygwin", 6) == 0)
+      cygwin = 1;
 
   DWORD num_subkeys, max_subkey_len, num_values;
   DWORD max_value_len, max_valdata_len, i;
@@ -781,7 +1014,7 @@ scan_registry (RegInfo * prev, HKEY hKey, char *name, int cygnus)
       return;
     }
 
-  if (cygnus)
+  if (cygwin)
     {
       show_reg (&ri, 0);
 
@@ -834,10 +1067,18 @@ scan_registry (RegInfo * prev, HKEY hKey, char *name, int cygnus)
          ERROR_SUCCESS)
        {
          HKEY sKey;
+         /* Don't recurse more than one level into the WOW64 subkey since
+            that would lead to an endless recursion. */
+         if (!strcasecmp (subkey_name, "Wow6432Node"))
+           {
+             if (wow64)
+               continue;
+             wow64 = true;
+           }
          if (RegOpenKeyEx (hKey, subkey_name, 0, KEY_READ, &sKey)
              == ERROR_SUCCESS)
            {
-             scan_registry (&ri, sKey, subkey_name, cygnus);
+             scan_registry (&ri, sKey, subkey_name, cygwin, wow64);
              if (RegCloseKey (sKey) != ERROR_SUCCESS)
                display_error ("scan_registry: RegCloseKey()");
            }
@@ -847,14 +1088,10 @@ scan_registry (RegInfo * prev, HKEY hKey, char *name, int cygnus)
 }
 
 void
-pretty_id (const char *s, char *cygwin, size_t cyglen)
+pretty_id ()
 {
   char *groups[16384];
 
-  strcpy (cygwin + cyglen++, " ");
-  strcpy (cygwin + cyglen, s);
-  putenv (cygwin);
-
   char *id = cygpath ("/bin/id.exe", NULL);
   for (char *p = id; (p = strchr (p, '/')); p++)
     *p = '\\';
@@ -865,9 +1102,10 @@ pretty_id (const char *s, char *cygwin, size_t cyglen)
       return;
     }
 
-  FILE *f = popen (id, "rt");
-
   char buf[16384];
+  snprintf (buf, sizeof (buf), "\"%s\"", id);
+  FILE *f = popen (buf, "rt");
+
   buf[0] = '\0';
   fgets (buf, sizeof (buf), f);
   pclose (f);
@@ -909,13 +1147,13 @@ pretty_id (const char *s, char *cygwin, size_t cyglen)
     }
   ng--;
 
-  printf ("\nOutput from %s (%s)\n", id, s);
+  printf ("\nOutput from %s\n", id);
   int n = 80 / (int) ++sz;
   int i = n > 2 ? n - 2 : 0;
   sz = -sz;
   for (char **g = groups; g <= ng; g++)
     if ((g != ng) && (++i < n))
-      printf ("%*s", sz, *g);
+      printf ("%*s", (int) sz, *g);
     else
       {
        puts (*g);
@@ -951,7 +1189,7 @@ dump_sysinfo_services ()
     }
 
   /* check for a recent cygrunsrv */
-  snprintf (buf, sizeof (buf), "%s --version", cygrunsrv);
+  snprintf (buf, sizeof (buf), "\"%s\" --version", cygrunsrv);
   if ((f = popen (buf, "rt")) == NULL)
     {
       printf ("Failed to execute '%s', skipping services check.\n", buf);
@@ -961,15 +1199,17 @@ dump_sysinfo_services ()
   int ret = fscanf (f, "cygrunsrv V%u.%u", &maj, &min);
   if (ferror (f) || feof (f) || ret == EOF || maj < 1 || min < 10)
     {
-      puts ("The version of cygrunsrv installed is too old to dump service info.\n");
+      puts ("The version of cygrunsrv installed is too old to dump "
+           "service info.\n");
       return;
     }
-  fclose (f);
+  pclose (f);
 
   /* For verbose mode, just run cygrunsrv --list --verbose and copy output
      verbatim; otherwise run cygrunsrv --list and then cygrunsrv --query for
      each service.  */
-  snprintf (buf, sizeof (buf), (verbose ? "%s --list --verbose" : "%s --list"),
+  snprintf (buf, sizeof (buf),
+           (verbose ? "\"%s\" --list --verbose" : "\"%s\" --list"),
            cygrunsrv);
   if ((f = popen (buf, "rt")) == NULL)
     {
@@ -1000,10 +1240,11 @@ dump_sysinfo_services ()
       if (nchars > 0)
        for (char *srv = strtok (buf, "\n"); srv; srv = strtok (NULL, "\n"))
          {
-           snprintf (buf2, sizeof (buf2), "%s --query %s", cygrunsrv, srv);
+           snprintf (buf2, sizeof (buf2), "\"%s\" --query %s", cygrunsrv, srv);
            if ((f = popen (buf2, "rt")) == NULL)
              {
-               printf ("Failed to execute '%s', skipping services check.\n", buf2);
+               printf ("Failed to execute '%s', skipping services check.\n",
+                       buf2);
                return;
              }
 
@@ -1022,6 +1263,91 @@ dump_sysinfo_services ()
     puts ("No Cygwin services found.\n");
 }
 
+enum handle_reg_t
+{
+  PRINT_KEY,
+  DELETE_KEY
+};
+
+void
+handle_reg_installation (handle_reg_t what)
+{
+  HKEY key;
+
+  if (what == PRINT_KEY)
+    printf ("Cygwin installations found in the registry:\n");
+  for (int i = 0; i < 2; ++i)
+    if (RegOpenKeyEx (i ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE,
+                     "SOFTWARE\\Cygwin\\Installations", 0,
+                     what == DELETE_KEY ? KEY_READ | KEY_WRITE : KEY_READ,
+                     &key)
+       == ERROR_SUCCESS)
+      {
+       char name[32], data[PATH_MAX];
+       DWORD nsize, dsize, type;
+       LONG ret;
+
+       for (DWORD index = 0;
+            (ret = RegEnumValue (key, index, name, (nsize = 32, &nsize), 0,
+                                 &type, (PBYTE) data,
+                                 (dsize = PATH_MAX, &dsize)))
+            != ERROR_NO_MORE_ITEMS; ++index)
+         if (ret == ERROR_SUCCESS && dsize > 5)
+           {
+             char *path = data + 4;
+             if (path[1] != ':')
+               *(path += 2) = '\\';
+             if (what == PRINT_KEY)
+               printf ("  %s Key: %s Path: %s", i ? "User:  " : "System:",
+                       name, path);
+             strcat (path, "\\bin\\cygwin1.dll");
+             if (what == PRINT_KEY)
+               printf ("%s\n", access (path, F_OK) ? " (ORPHANED)" : "");
+             else if (access (path, F_OK))
+               {
+                 RegDeleteValue (key, name);
+                 /* Start over since index is not reliable anymore. */
+                 --i;
+                 break;
+               }
+           }
+       RegCloseKey (key);
+      }
+  if (what == PRINT_KEY)
+    printf ("\n");
+}
+
+void
+print_reg_installations ()
+{
+  handle_reg_installation (PRINT_KEY);
+}
+
+void
+del_orphaned_reg_installations ()
+{
+  handle_reg_installation (DELETE_KEY);
+}
+
+/* Unfortunately neither mingw nor Windows know this function. */
+char *
+memmem (char *haystack, size_t haystacklen,
+       const char *needle, size_t needlelen)
+{
+  if (needlelen == 0)
+    return haystack;
+  while (needlelen <= haystacklen)
+    {
+      if (!memcmp (haystack, needle, needlelen))
+       return haystack;
+      haystack++;
+      haystacklen--;
+    }
+  return NULL;
+}
+
+extern "C" NTSTATUS NTAPI RtlGetVersion (PRTL_OSVERSIONINFOEXW);
+
 static void
 dump_sysinfo ()
 {
@@ -1030,127 +1356,253 @@ dump_sysinfo ()
   time_t now;
   char *found_cygwin_dll;
   bool is_nt = false;
+  char osname[128];
+  DWORD obcaseinsensitive = 1;
+  HKEY key;
+
+  /* MSVCRT popen (called by pretty_id and dump_sysinfo_services) SEGVs if
+     COMSPEC isn't set correctly.  Simply enforce it here.  Using
+     Get/SetEnvironmentVariable to set the dir does *not* help, btw.
+     Apparently MSVCRT keeps its own copy of the environment and changing
+     that requires to use _wputenv. */
+  if (!_wgetenv (L"COMSPEC"))
+    {
+      WCHAR comspec[MAX_PATH + 17];
+      wcscpy (comspec, L"COMSPEC=");
+      GetSystemDirectoryW (comspec + 8, MAX_PATH);
+      wcsncat (comspec, L"\\cmd.exe", sizeof comspec);
+      _wputenv (comspec);
+    }
 
   printf ("\nCygwin Configuration Diagnostics\n");
   time (&now);
   printf ("Current System Time: %s\n", ctime (&now));
 
-  OSVERSIONINFO osversion;
-  osversion.dwOSVersionInfoSize = sizeof (osversion);
-  if (!GetVersionEx (&osversion))
-    display_error ("dump_sysinfo: GetVersionEx()");
-  const char *osname = "unknown OS";
+  RTL_OSVERSIONINFOEXW osversion;
+  osversion.dwOSVersionInfoSize = sizeof (RTL_OSVERSIONINFOEXW);
+  RtlGetVersion (&osversion);
+
   switch (osversion.dwPlatformId)
     {
-    case VER_PLATFORM_WIN32s:
-      osname = "32s";
-      break;
-    case VER_PLATFORM_WIN32_WINDOWS:
-      switch (osversion.dwMinorVersion)
-       {
-       case 0:
-         if (strchr (osversion.szCSDVersion, 'C'))
-           osname = "95 OSR2";
-         else
-           osname = "95";
-         break;
-       case 10:
-         if (strchr (osversion.szCSDVersion, 'A'))
-           osname = "98 SE";
-         else
-           osname = "98";
-         break;
-       case 90:
-         osname = "ME";
-         break;
-       default:
-         osname = "9X";
-         break;
-       }
-      break;
     case VER_PLATFORM_WIN32_NT:
       is_nt = true;
-      if (osversion.dwMajorVersion == 6)
-       osname = "Longhorn/Vista (not yet supported!)";
-      else if (osversion.dwMajorVersion == 5)
+      if (osversion.dwMajorVersion >= 6)
        {
-         BOOL more_info = FALSE;
-         OSVERSIONINFOEX osversionex;
-         osversionex.dwOSVersionInfoSize = sizeof (osversionex);
-         if (GetVersionEx ((OSVERSIONINFO *) &osversionex))
-           more_info = TRUE;
-         if (osversion.dwMinorVersion == 0)
+         HMODULE k32 = GetModuleHandleW (L"kernel32.dll");
+         BOOL (WINAPI *GetProductInfo) (DWORD, DWORD, DWORD, DWORD, PDWORD) =
+                 (BOOL (WINAPI *)(DWORD, DWORD, DWORD, DWORD, PDWORD))
+                 GetProcAddress (k32, "GetProductInfo");
+         if (osversion.dwMajorVersion == 6)
+           switch (osversion.dwMinorVersion)
+             {
+             case 0:
+               strcpy (osname, osversion.wProductType == VER_NT_WORKSTATION
+                               ? "Vista" : "2008");
+               break;
+             case 1:
+               strcpy (osname, osversion.wProductType == VER_NT_WORKSTATION
+                               ? "7" : "2008 R2");
+               break;
+             case 2:
+               strcpy (osname, osversion.wProductType == VER_NT_WORKSTATION
+                               ? "8" : "2012");
+               break;
+             case 3:
+               strcpy (osname, osversion.wProductType == VER_NT_WORKSTATION
+                               ? "8.1" : "2012 R2");
+               break;
+             case 4:
+             default:
+               strcpy (osname, osversion.wProductType == VER_NT_WORKSTATION
+                               ? "10 Preview" : "2016 Preview");
+               break;
+             }
+         else if (osversion.dwMajorVersion == 10)
            {
-             if (!more_info)
-               osname = "2000";
-             else if (osversionex.wProductType == VER_NT_SERVER
-                      || osversionex.wProductType == VER_NT_DOMAIN_CONTROLLER)
-               {
-                 if (osversionex.wSuiteMask & VER_SUITE_DATACENTER)
-                   osname = "2000 Datacenter Server";
-                 else if (osversionex.wSuiteMask & VER_SUITE_ENTERPRISE)
-                   osname = "2000 Advanced Server";
-                 else
-                   osname = "2000 Server";
-               }
-             else
-               osname = "2000 Professional";
+             strcpy (osname, osversion.wProductType == VER_NT_WORKSTATION
+                             ? "10" : "2016");
            }
-         else if (osversion.dwMinorVersion == 1)
+         DWORD prod;
+         if (GetProductInfo (osversion.dwMajorVersion,
+                             osversion.dwMinorVersion,
+                             osversion.wServicePackMajor,
+                             osversion.wServicePackMinor,
+                             &prod))
            {
-             if (GetSystemMetrics (SM_MEDIACENTER))
-               osname = "XP Media Center Edition";
-             else if (GetSystemMetrics (SM_TABLETPC))
-               osname = "XP Tablet PC Edition";
-             else if (!more_info)
-               osname = "XP";
-             else if (osversionex.wSuiteMask & VER_SUITE_PERSONAL)
-               osname = "XP Home Edition";
+             const char *products[] =
+               {
+ /* 0x00000000 */ "",
+ /* 0x00000001 */ " Ultimate",
+ /* 0x00000002 */ " Home Basic",
+ /* 0x00000003 */ " Home Premium",
+ /* 0x00000004 */ " Enterprise",
+ /* 0x00000005 */ " Home Basic N",
+ /* 0x00000006 */ " Business",
+ /* 0x00000007 */ " Server Standard",
+ /* 0x00000008 */ " Server Datacenter",
+ /* 0x00000009 */ " Small Business Server",
+ /* 0x0000000a */ " Server Enterprise",
+ /* 0x0000000b */ " Starter",
+ /* 0x0000000c */ " Server Datacenter Core",
+ /* 0x0000000d */ " Server Standard Core",
+ /* 0x0000000e */ " Server Enterprise Core",
+ /* 0x0000000f */ " Server Enterprise for Itanium-based Systems",
+ /* 0x00000010 */ " Business N",
+ /* 0x00000011 */ " Web Server",
+ /* 0x00000012 */ " HPC Edition",
+ /* 0x00000013 */ " Home Server",
+ /* 0x00000014 */ " Storage Server Express",
+ /* 0x00000015 */ " Storage Server Standard",
+ /* 0x00000016 */ " Storage Server Workgroup",
+ /* 0x00000017 */ " Storage Server Enterprise",
+ /* 0x00000018 */ " for Windows Essential Server Solutions",
+ /* 0x00000019 */ " Small Business Server Premium",
+ /* 0x0000001a */ " Home Premium N",
+ /* 0x0000001b */ " Enterprise N",
+ /* 0x0000001c */ " Ultimate N",
+ /* 0x0000001d */ " Web Server Core",
+ /* 0x0000001e */ " Essential Business Server Management Server",
+ /* 0x0000001f */ " Essential Business Server Security Server",
+ /* 0x00000020 */ " Essential Business Server Messaging Server",
+ /* 0x00000021 */ " Server Foundation",
+ /* 0x00000022 */ " Home Server 2011",
+ /* 0x00000023 */ " without Hyper-V for Windows Essential Server Solutions",
+ /* 0x00000024 */ " Server Standard without Hyper-V",
+ /* 0x00000025 */ " Server Datacenter without Hyper-V",
+ /* 0x00000026 */ " Server Enterprise without Hyper-V",
+ /* 0x00000027 */ " Server Datacenter Core without Hyper-V",
+ /* 0x00000028 */ " Server Standard Core without Hyper-V",
+ /* 0x00000029 */ " Server Enterprise Core without Hyper-V",
+ /* 0x0000002a */ " Hyper-V Server",
+ /* 0x0000002b */ " Storage Server Express Core",
+ /* 0x0000002c */ " Storage Server Standard Core",
+ /* 0x0000002d */ " Storage Server Workgroup Core",
+ /* 0x0000002e */ " Storage Server Enterprise Core",
+ /* 0x0000002f */ " Starter N",
+ /* 0x00000030 */ " Professional",
+ /* 0x00000031 */ " Professional N",
+ /* 0x00000032 */ " Small Business Server 2011 Essentials",
+ /* 0x00000033 */ " Server For SB Solutions",
+ /* 0x00000034 */ " Server Solutions Premium",
+ /* 0x00000035 */ " Server Solutions Premium Core",
+ /* 0x00000036 */ " Server For SB Solutions EM", /* per MSDN, 2012-09-01 */
+ /* 0x00000037 */ " Server For SB Solutions EM", /* per MSDN, 2012-09-01 */
+ /* 0x00000038 */ " Multipoint Server",
+ /* 0x00000039 */ "",
+ /* 0x0000003a */ "",
+ /* 0x0000003b */ " Essential Server Solution Management",
+ /* 0x0000003c */ " Essential Server Solution Additional",
+ /* 0x0000003d */ " Essential Server Solution Management SVC",
+ /* 0x0000003e */ " Essential Server Solution Additional SVC",
+ /* 0x0000003f */ " Small Business Server Premium Core",
+ /* 0x00000040 */ " Server Hyper Core V",
+ /* 0x00000041 */ "",
+ /* 0x00000042 */ " Starter E",
+ /* 0x00000043 */ " Home Basic E",
+ /* 0x00000044 */ " Home Premium E",
+ /* 0x00000045 */ " Professional E",
+ /* 0x00000046 */ " Enterprise E",
+ /* 0x00000047 */ " Ultimate E",
+ /* 0x00000048 */ " Server Enterprise (Evaluation inst.)",
+ /* 0x00000049 */ "",
+ /* 0x0000004a */ "",
+ /* 0x0000004b */ "",
+ /* 0x0000004c */ " MultiPoint Server Standard",
+ /* 0x0000004d */ " MultiPoint Server Premium",
+ /* 0x0000004e */ "",
+ /* 0x0000004f */ " Server Standard (Evaluation inst.)",
+ /* 0x00000050 */ " Server Datacenter (Evaluation inst.)",
+ /* 0x00000051 */ "",
+ /* 0x00000052 */ "",
+ /* 0x00000053 */ "",
+ /* 0x00000054 */ " Enterprise N (Evaluation inst.)",
+ /* 0x00000055 */ "",
+ /* 0x00000056 */ "",
+ /* 0x00000057 */ "",
+ /* 0x00000058 */ "",
+ /* 0x00000059 */ "",
+ /* 0x0000005a */ "",
+ /* 0x0000005b */ "",
+ /* 0x0000005c */ "",
+ /* 0x0000005d */ "",
+ /* 0x0000005e */ "",
+ /* 0x0000005f */ " Storage Server Workgroup (Evaluation inst.)",
+ /* 0x00000060 */ " Storage Server Standard (Evaluation inst.)",
+ /* 0x00000061 */ "",
+ /* 0x00000062 */ " N",
+ /* 0x00000063 */ " China",
+ /* 0x00000064 */ " Single Language",
+ /* 0x00000065 */ " Home",
+ /* 0x00000066 */ "",
+ /* 0x00000067 */ " Professional with Media Center",
+ /* 0x00000068 */ " Mobile",
+ /* 0x00000069 */ "",
+ /* 0x0000006a */ "",
+ /* 0x0000006b */ "",
+ /* 0x0000006c */ "",
+ /* 0x0000006d */ "",
+ /* 0x0000006e */ "",
+ /* 0x0000006f */ "",
+ /* 0x00000070 */ "",
+ /* 0x00000071 */ "",
+ /* 0x00000072 */ "",
+ /* 0x00000073 */ "",
+ /* 0x00000074 */ "",
+ /* 0x00000075 */ "",
+ /* 0x00000076 */ "",
+ /* 0x00000077 */ "",
+ /* 0x00000078 */ "",
+ /* 0x00000079 */ " Education",
+ /* 0x0000007a */ " Education N",
+ /* 0x0000007b */ "",
+ /* 0x0000007c */ "",
+ /* 0x0000007d */ "",
+ /* 0x0000007e */ "",
+ /* 0x0000007f */ "",
+ /* 0x00000080 */ "",
+ /* 0x00000081 */ "",
+ /* 0x00000082 */ "",
+ /* 0x00000083 */ "",
+ /* 0x00000084 */ "",
+ /* 0x00000085 */ " Mobile Enterprise",
+               };
+             if (prod == PRODUCT_UNLICENSED)
+               strcat (osname, "Unlicensed");
+             else if (prod > 0x00000085)
+               strcat (osname, "");
              else
-               osname = "XP Professional";
+               strcat (osname, products[prod]);
            }
-         else if (osversion.dwMinorVersion == 2)
+         else
            {
-             if (!more_info)
-               osname = "2003 Server";
-             else if (osversionex.wSuiteMask & VER_SUITE_BLADE)
-               osname = "2003 Web Server";
-             else if (osversionex.wSuiteMask & VER_SUITE_DATACENTER)
-               osname = "2003 Datacenter Server";
-             else if (osversionex.wSuiteMask & VER_SUITE_ENTERPRISE)
-               osname = "2003 Enterprise Server";
-             else
-               osname = "2003 Server";
            }
        }
       else
-       osname = "NT";
+       strcpy (osname, "NT");
       break;
     default:
-      osname = "??";
+      strcpy (osname, "??");
       break;
     }
-  printf ("Windows %s Ver %lu.%lu Build %lu %s\n", osname,
+  printf ("Windows %s Ver %lu.%lu Build %lu %ls\n", osname,
          osversion.dwMajorVersion, osversion.dwMinorVersion,
          osversion.dwPlatformId == VER_PLATFORM_WIN32_NT ?
          osversion.dwBuildNumber : (osversion.dwBuildNumber & 0xffff),
          osversion.dwPlatformId == VER_PLATFORM_WIN32_NT ?
-         osversion.szCSDVersion : "");
+         osversion.szCSDVersion : L"");
 
-  HMODULE k32 = LoadLibrary ("kernel32.dll");
+  if (osversion.dwPlatformId == VER_PLATFORM_WIN32s
+      || osversion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS)
+    exit (EXIT_FAILURE);
 
-  BOOL (WINAPI *wow64_func) (HANDLE, PBOOL) = (BOOL (WINAPI *) (HANDLE, PBOOL))
-    GetProcAddress (k32, "IsWow64Process");
   BOOL is_wow64 = FALSE;
-  if (wow64_func && wow64_func (GetCurrentProcess (), &is_wow64) && is_wow64)
+  if (IsWow64Process (GetCurrentProcess (), &is_wow64) && is_wow64)
     {
-      void (WINAPI *nativinfo) (LPSYSTEM_INFO) = (void (WINAPI *)
-        (LPSYSTEM_INFO)) GetProcAddress (k32, "GetNativeSystemInfo");
       SYSTEM_INFO natinfo;
-      nativinfo (&natinfo);
+      GetNativeSystemInfo (&natinfo);
       fputs ("\nRunning under WOW64 on ", stdout);
       switch (natinfo.wProcessorArchitecture)
-        {
+       {
          case PROCESSOR_ARCHITECTURE_IA64:
            puts ("IA64");
            break;
@@ -1178,7 +1630,7 @@ dump_sysinfo ()
        {
          for (e = s; *e && *e != sep; e++);
          if (e-s)
-           printf ("\t%.*s\n", e - s, s);
+           printf ("\t%.*s\n", (int) (e - s), s);
          else
            puts ("\t.");
          count_path_items++;
@@ -1190,17 +1642,7 @@ dump_sysinfo ()
 
   fflush (stdout);
 
-  char *cygwin = getenv ("CYGWIN");
-  if (cygwin)
-    cygwin -= strlen ("CYGWIN=");
-  else
-    cygwin = const_cast <char *> ("CYGWIN=");
-  size_t cyglen = strlen (cygwin);
-  cygwin = strcpy ((char *) malloc (cyglen + sizeof (" nontsec")), cygwin);
-  pretty_id ("nontsec", cygwin, cyglen);
-  pretty_id ("ntsec", cygwin, cyglen);
-  cygwin[cyglen] = 0;
-  putenv (cygwin);
+  pretty_id ();
 
   if (!GetSystemDirectory (tmp, 4000))
     display_error ("dump_sysinfo: GetSystemDirectory()");
@@ -1261,24 +1703,29 @@ dump_sysinfo ()
   if (registry)
     {
       if (givehelp)
-       printf ("Scanning registry for keys with 'Cygnus' in them...\n");
-#if 0
-      /* big and not generally useful */
-      scan_registry (0, HKEY_CLASSES_ROOT, (char *) "HKEY_CLASSES_ROOT", 0);
-#endif
-      scan_registry (0, HKEY_CURRENT_CONFIG,
-                    (char *) "HKEY_CURRENT_CONFIG", 0);
-      scan_registry (0, HKEY_CURRENT_USER, (char *) "HKEY_CURRENT_USER", 0);
-      scan_registry (0, HKEY_LOCAL_MACHINE, (char *) "HKEY_LOCAL_MACHINE", 0);
-#if 0
-      /* the parts we need are duplicated in HKEY_CURRENT_USER anyway */
-      scan_registry (0, HKEY_USERS, (char *) "HKEY_USERS", 0);
-#endif
+       printf ("Scanning registry for keys with 'Cygwin' in them...\n");
+      scan_registry (0, HKEY_CURRENT_USER,
+                    (char *) "HKEY_CURRENT_USER", 0, false);
+      scan_registry (0, HKEY_LOCAL_MACHINE,
+                    (char *) "HKEY_LOCAL_MACHINE", 0, false);
       printf ("\n");
     }
   else
     printf ("Use '-r' to scan registry\n\n");
 
+  if (RegOpenKeyEx (HKEY_LOCAL_MACHINE,
+                 "SYSTEM\\CurrentControlSet\\Control\\Session Manager\\kernel",
+                 0, KEY_READ, &key) == ERROR_SUCCESS)
+    {
+      DWORD size;
+      RegQueryValueEx (key, "obcaseinsensitive", NULL, NULL,
+                      (LPBYTE) &obcaseinsensitive, &size);
+      RegCloseKey (key);
+    }
+  printf ("obcaseinsensitive set to %lu\n\n", obcaseinsensitive);
+
+  print_reg_installations ();
+
   if (givehelp)
     {
       printf ("Listing available drives...\n");
@@ -1288,17 +1735,13 @@ dump_sysinfo ()
     SetErrorMode (SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
   int drivemask = GetLogicalDrives ();
 
-  BOOL (WINAPI * gdfse) (LPCSTR, long long *, long long *, long long *) =
-    (BOOL (WINAPI *) (LPCSTR, long long *, long long *, long long *))
-    GetProcAddress (k32, "GetDiskFreeSpaceExA");
-
   for (i = 0; i < 26; i++)
     {
       if (!(drivemask & (1 << i)))
        continue;
       char drive[4], name[200], fsname[200];
       DWORD serno = 0, maxnamelen = 0, flags = 0;
-      name[0] = name[0] = fsname[0] = 0;
+      name[0] = fsname[0] = 0;
       sprintf (drive, "%c:\\", i + 'a');
       /* Report all errors, except if the Volume is ERROR_NOT_READY.
         ERROR_NOT_READY is returned when removeable media drives are empty
@@ -1341,11 +1784,14 @@ dump_sysinfo ()
       long capacity_mb = -1;
       int percent_full = -1;
 
-      long long free_me = 0ULL, free_bytes = 0ULL, total_bytes = 1ULL;
-      if (gdfse != NULL && gdfse (drive, &free_me, &total_bytes, &free_bytes))
+      ULARGE_INTEGER free_me, free_bytes, total_bytes;
+      free_me.QuadPart = free_bytes.QuadPart = 0ULL;
+      total_bytes.QuadPart = 1ULL;
+      if (GetDiskFreeSpaceEx (drive, &free_me, &total_bytes, &free_bytes))
        {
-         capacity_mb = total_bytes / (1024L * 1024L);
-         percent_full = 100 - (int) ((100.0 * free_me) / total_bytes);
+         capacity_mb = total_bytes.QuadPart / (1024L * 1024L);
+         percent_full = 100 - (int) ((100.0 * free_me.QuadPart)
+                                     / total_bytes.QuadPart);
        }
       else
        {
@@ -1362,25 +1808,17 @@ dump_sysinfo ()
        printf ("%7dMb %3d%% ", (int) capacity_mb, (int) percent_full);
       else
        printf ("    N/A    N/A ");
-      printf ("%s %s %s %s %s %s  %s\n",
+      printf ("%s %s %s %s %s %s %s  %s\n",
              flags & FS_CASE_IS_PRESERVED ? "CP" : "  ",
              flags & FS_CASE_SENSITIVE ? "CS" : "  ",
              flags & FS_UNICODE_STORED_ON_DISK ? "UN" : "  ",
              flags & FS_PERSISTENT_ACLS ? "PA" : "  ",
              flags & FS_FILE_COMPRESSION ? "FC" : "  ",
              flags & FS_VOL_IS_COMPRESSED ? "VC" : "  ",
-#if 0
-             flags & FILE_SUPPORTS_ENCRYPTION ? "EN" : "  ",
-             flags & FILE_SUPPORTS_OBJECT_IDS ? "OI" : "  ",
-             flags & FILE_SUPPORTS_REPARSE_POINTS ? "RP" : "  ",
-             flags & FILE_SUPPORTS_SPARSE_FILES ? "SP" : "  ",
              flags & FILE_VOLUME_QUOTAS ? "QU" : "  ",
-#endif
              name);
     }
 
-  if (!FreeLibrary (k32))
-    display_error ("dump_sysinfo: FreeLibrary()");
   SetErrorMode (prev_mode);
   if (givehelp)
     {
@@ -1429,13 +1867,11 @@ dump_sysinfo ()
     }
   printf ("\n");
 
-  add_path ((char *) "\\bin", 4);      /* just in case */
-
   if (givehelp)
     printf
       ("Looking to see where common programs can be found, if at all...\n");
   for (i = 0; common_apps[i].name; i++)
-    if (!find_on_path ((char *) common_apps[i].name, (char *) ".exe", 1, 0))
+    if (!find_app_on_path ((char *) common_apps[i].name, 1))
       {
        if (common_apps[i].missing_is_good)
          printf ("Not Found: %s (good!)\n", common_apps[i].name);
@@ -1447,31 +1883,38 @@ dump_sysinfo ()
   if (givehelp)
     printf ("Looking for various Cygwin DLLs...  (-v gives version info)\n");
   int cygwin_dll_count = 0;
-  for (i = 1; i < num_paths; i++)
+  char cygdll_path[32768];
+  for (pathlike *pth = paths; pth->dir; pth++)
     {
-      WIN32_FIND_DATA ffinfo;
-      sprintf (tmp, "%s/*.*", paths[i]);
-      HANDLE ff = FindFirstFile (tmp, &ffinfo);
+      WIN32_FIND_DATAW ffinfo;
+      sprintf (tmp, "%s*.*", pth->dir);
+      wide_path wpath (tmp);
+      HANDLE ff = FindFirstFileW (wpath, &ffinfo);
       int found = (ff != INVALID_HANDLE_VALUE);
       found_cygwin_dll = NULL;
       while (found)
        {
-         char *f = ffinfo.cFileName;
+         char f[FILENAME_MAX + 1];
+         wcstombs (f, ffinfo.cFileName, sizeof f);
          if (strcasecmp (f + strlen (f) - 4, ".dll") == 0)
            {
              if (strncasecmp (f, "cyg", 3) == 0)
                {
-                 sprintf (tmp, "%s\\%s", paths[i], f);
+                 sprintf (tmp, "%s%s", pth->dir, f);
                  if (strcasecmp (f, "cygwin1.dll") == 0)
                    {
-                     cygwin_dll_count++;
+                     if (!cygwin_dll_count)
+                       strcpy (cygdll_path, pth->dir);
+                     if (!cygwin_dll_count
+                         || strcasecmp (cygdll_path, pth->dir) != 0)
+                       cygwin_dll_count++;
                      found_cygwin_dll = strdup (tmp);
                    }
                  else
                    ls (tmp);
                }
            }
-         found = FindNextFile (ff, &ffinfo);
+         found = FindNextFileW (ff, &ffinfo);
        }
       if (found_cygwin_dll)
        {
@@ -1486,6 +1929,8 @@ dump_sysinfo ()
   if (!cygwin_dll_count)
     puts ("Warning: cygwin1.dll not found on your path");
 
+  dump_dodgy_apps (verbose);
+
   if (is_nt)
     dump_sysinfo_services ();
 }
@@ -1493,7 +1938,7 @@ dump_sysinfo ()
 static int
 check_keys ()
 {
-  HANDLE h = CreateFileA ("CONIN$", GENERIC_READ | GENERIC_WRITE,
+  HANDLE h = CreateFileW (L"CONIN$", GENERIC_READ | GENERIC_WRITE,
                          FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
                          OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
 
@@ -1519,14 +1964,14 @@ check_keys ()
   INPUT_RECORD in, prev_in;
 
   // Drop first <RETURN> key
-  ReadConsoleInput (h, &in, 1, &mode);
+  ReadConsoleInputW (h, &in, 1, &mode);
 
   memset (&in, 0, sizeof in);
 
   do
     {
       prev_in = in;
-      if (!ReadConsoleInput (h, &in, 1, &mode))
+      if (!ReadConsoleInputW (h, &in, 1, &mode))
        display_error ("check_keys: ReadConsoleInput()");
 
       if (!memcmp (&in, &prev_in, sizeof in))
@@ -1535,12 +1980,12 @@ check_keys ()
       switch (in.EventType)
        {
        case KEY_EVENT:
-         printf ("%s %ux VK: 0x%02x VS: 0x%02x A: 0x%02x CTRL: ",
+         printf ("%s %ux VK: 0x%04x VS: 0x%04x C: 0x%04x CTRL: ",
                  in.Event.KeyEvent.bKeyDown ? "Pressed " : "Released",
                  in.Event.KeyEvent.wRepeatCount,
                  in.Event.KeyEvent.wVirtualKeyCode,
                  in.Event.KeyEvent.wVirtualScanCode,
-                 (unsigned char) in.Event.KeyEvent.uChar.AsciiChar);
+                 (unsigned char) in.Event.KeyEvent.uChar.UnicodeChar);
          fputs (in.Event.KeyEvent.dwControlKeyState & CAPSLOCK_ON ?
                 "CL " : "-- ", stdout);
          fputs (in.Event.KeyEvent.dwControlKeyState & ENHANCED_KEY ?
@@ -1568,18 +2013,25 @@ check_keys ()
     }
   while (in.EventType != KEY_EVENT ||
         in.Event.KeyEvent.bKeyDown != FALSE ||
-        in.Event.KeyEvent.uChar.AsciiChar != 'q');
+        in.Event.KeyEvent.uChar.UnicodeChar != L'q');
 
   CloseHandle (h);
   return 0;
 }
 
-/* RFC1738 says that these do not need to be escaped.  */
-static const char safe_chars[] = "$-_.+!*'(),";
+/* These do not need to be escaped in application/x-www-form-urlencoded */
+static const char safe_chars[] = "$-_.!*'(),";
 
 /* the URL to query.  */
 static const char base_url[] =
-        "http://cygwin.com/cgi-bin2/package-grep.cgi?text=1&grep=";
+       "http://cygwin.com/cgi-bin2/package-grep.cgi?text=1&grep=";
+
+#ifdef __x86_64__
+#define ARCH_STR  "&arch=x86_64"
+#else
+#define ARCH_STR  "&arch=x86"
+#endif
+static const char *ARCH_str = ARCH_STR;
 
 /* Queries Cygwin web site for packages containing files matching a regexp.
    Return value is 1 if there was a problem, otherwise 0.  */
@@ -1589,25 +2041,26 @@ package_grep (char *search)
   char buf[1024];
 
   /* construct the actual URL by escaping  */
-  char *url = (char *) alloca (sizeof (base_url) + strlen (search) * 3);
+  char *url = (char *) alloca (sizeof (base_url) + strlen (ARCH_str)
+                              + strlen (search) * 3);
   strcpy (url, base_url);
 
   char *dest;
   for (dest = &url[sizeof (base_url) - 1]; *search; search++)
     {
       if (isalnum (*search)
-          || memchr (safe_chars, *search, sizeof (safe_chars) - 1))
-        {
-          *dest++ = *search;
-        }
+         || memchr (safe_chars, *search, sizeof (safe_chars) - 1))
+       {
+         *dest++ = *search;
+       }
       else
-        {
-          *dest++ = '%';
-          sprintf (dest, "%02x", (unsigned char) *search);
-          dest += 2;
-        }
+       {
+         *dest++ = '%';
+         sprintf (dest, "%02x", (unsigned char) *search);
+         dest += 2;
+       }
     }
-  *dest = 0;
+  strcpy (dest, ARCH_str);
 
   /* Connect to the net and open the URL.  */
   if (InternetAttemptConnect (0) != ERROR_SUCCESS)
@@ -1618,23 +2071,24 @@ package_grep (char *search)
 
   /* Initialize WinInet and attempt to fetch our URL.  */
   HINTERNET hi = NULL, hurl = NULL;
-  if (!(hi = InternetOpen ("cygcheck", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0)))
+  if (!(hi = InternetOpenA ("cygcheck", INTERNET_OPEN_TYPE_PRECONFIG,
+                           NULL, NULL, 0)))
     return display_internet_error ("InternetOpen() failed", NULL);
 
-  if (!(hurl = InternetOpenUrl (hi, url, NULL, 0, 0, 0)))
+  if (!(hurl = InternetOpenUrlA (hi, url, NULL, 0, 0, 0)))
     return display_internet_error ("unable to contact cygwin.com site, "
-                                   "InternetOpenUrl() failed", hi, NULL);
+                                  "InternetOpenUrl() failed", hi, NULL);
 
   /* Check the HTTP response code.  */
   DWORD rc = 0, rc_s = sizeof (DWORD);
-  if (!HttpQueryInfo (hurl, HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER,
-                      (void *) &rc, &rc_s, NULL))
+  if (!HttpQueryInfoA (hurl, HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER,
+                     (void *) &rc, &rc_s, NULL))
     return display_internet_error ("HttpQueryInfo() failed", hurl, hi, NULL);
 
   if (rc != HTTP_STATUS_OK)
     {
       sprintf (buf, "error retrieving results from cygwin.com site, "
-                    "HTTP status code %lu", rc);
+                   "HTTP status code %lu", rc);
       return display_internet_error (buf, hurl, hi, NULL);
     }
 
@@ -1643,9 +2097,9 @@ package_grep (char *search)
   do
     {
       if (!InternetReadFile (hurl, (void *) buf, sizeof (buf), &numread))
-        return display_internet_error ("InternetReadFile failed", hurl, hi, NULL);
+       return display_internet_error ("InternetReadFile failed", hurl, hi, NULL);
       if (numread)
-        fwrite ((void *) buf, (size_t) numread, 1, stdout);
+       fwrite ((void *) buf, (size_t) numread, 1, stdout);
     }
   while (numread);
 
@@ -1654,36 +2108,42 @@ package_grep (char *search)
   return 0;
 }
 
-static void
+static void __attribute__ ((__noreturn__))
 usage (FILE * stream, int status)
 {
   fprintf (stream, "\
-Usage: cygcheck PROGRAM [ -v ] [ -h ]\n\
-       cygcheck -c [ PACKAGE ] [ -d ]\n\
-       cygcheck -s [ -r ] [ -v ] [ -h ]\n\
+Usage: cygcheck [-v] [-h] PROGRAM\n\
+       cygcheck -c [-d] [PACKAGE]\n\
+       cygcheck -s [-r] [-v] [-h]\n\
        cygcheck -k\n\
-       cygcheck -f FILE [ FILE ... ]\n\
-       cygcheck -l [ PACKAGE ] [ PACKAGE ... ]\n\
+       cygcheck -f FILE [FILE]...\n\
+       cygcheck -l [PACKAGE]...\n\
        cygcheck -p REGEXP\n\
+       cygcheck --delete-orphaned-installation-keys\n\
+       cygcheck -h\n\n\
 List system information, check installed packages, or query package database.\n\
 \n\
 At least one command option or a PROGRAM is required, as shown above.\n\
 \n\
   PROGRAM              list library (DLL) dependencies of PROGRAM\n\
   -c, --check-setup    show installed version of PACKAGE and verify integrity\n\
-                       (or for all installed packages if none specified)\n\
+                      (or for all installed packages if none specified)\n\
   -d, --dump-only      just list packages, do not verify (with -c)\n\
-  -s, --sysinfo        produce diagnostic system information (implies -c -d)\n\
+  -s, --sysinfo        produce diagnostic system information (implies -c)\n\
   -r, --registry       also scan registry for Cygwin settings (with -s)\n\
   -k, --keycheck       perform a keyboard check session (must be run from a\n\
-                       plain console only, not from a pty/rxvt/xterm)\n\
-  -f, --find-package   find the package that FILE belongs to\n\
+                      plain console only, not from a pty/rxvt/xterm)\n\
+  -f, --find-package   find the package to which FILE belongs\n\
   -l, --list-package   list contents of PACKAGE (or all packages if none given)\n\
   -p, --package-query  search for REGEXP in the entire cygwin.com package\n\
-                       repository (requies internet connectivity)\n\
+                      repository (requires internet connectivity)\n\
+  --delete-orphaned-installation-keys\n\
+                      Delete installation keys of old, now unused\n\
+                      installations from the registry.  Requires the right\n\
+                      to change the registry.\n\
   -v, --verbose        produce more verbose output\n\
   -h, --help           annotate output with explanatory comments when given\n\
-                       with another command, otherwise print this help\n\
+                      with another command, otherwise print this help\n\
   -V, --version        print the version of cygcheck and exit\n\
 \n\
 Note: -c, -f, and -l only report on packages that are currently installed. To\n\
@@ -1703,6 +2163,7 @@ struct option longopts[] = {
   {"find-package", no_argument, NULL, 'f'},
   {"list-package", no_argument, NULL, 'l'},
   {"package-query", no_argument, NULL, 'p'},
+  {"delete-orphaned-installation-keys", no_argument, NULL, CO_DELETE_KEYS},
   {"help", no_argument, NULL, 'h'},
   {"version", no_argument, 0, 'V'},
   {0, no_argument, NULL, 0}
@@ -1713,38 +2174,32 @@ static char opts[] = "cdsrvkflphV";
 static void
 print_version ()
 {
-  const char *v = strchr (version, ':');
-  int len;
-  if (!v)
-    {
-      v = "?";
-      len = 1;
-    }
-  else
-    {
-      v += 2;
-      len = strchr (v, ' ') - v;
-    }
-  printf ("\
-cygcheck version %.*s\n\
-System Checker for Cygwin\n\
-Copyright 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 Red Hat, Inc.\n\
-Compiled on %s\n\
-", len, v, __DATE__);
+  printf ("cygcheck (cygwin) %d.%d.%d\n"
+         "System Checker for Cygwin\n"
+         "Copyright (C) 1998 - %s Cygwin Authors\n"
+         "This is free software; see the source for copying conditions.  "
+         "There is NO\n"
+         "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR "
+         "PURPOSE.\n",
+         CYGWIN_VERSION_DLL_MAJOR / 1000,
+         CYGWIN_VERSION_DLL_MAJOR % 1000,
+         CYGWIN_VERSION_DLL_MINOR,
+         strrchr (__DATE__, ' ') + 1);
 }
 
 void
 nuke (char *ev)
 {
   int n = 1 + strchr (ev, '=') - ev;
-  char *s = (char *) alloca (n + 1);
+  char *s = (char *) malloc (n + 1);
   memcpy (s, ev, n);
   s[n] = '\0';
   putenv (s);
 }
 
 extern "C" {
-unsigned long (*cygwin_internal) (int, ...);
+uintptr_t (*cygwin_internal) (int, ...);
+WCHAR cygwin_dll_path[32768];
 };
 
 static void
@@ -1754,35 +2209,49 @@ load_cygwin (int& argc, char **&argv)
 
   if (!(h = LoadLibrary ("cygwin1.dll")))
     return;
-  if (!(cygwin_internal = (DWORD (*) (int, ...)) GetProcAddress (h, "cygwin_internal")))
-    return;
+  GetModuleFileNameW (h, cygwin_dll_path, 32768);
+  if ((cygwin_internal = (uintptr_t (*) (int, ...))
+                        GetProcAddress (h, "cygwin_internal")))
+    {
+      char **av = (char **) cygwin_internal (CW_ARGV);
+      if (av && ((uintptr_t) av != (uintptr_t) -1))
+       {
+         /* Copy cygwin's idea of the argument list into this Window
+            application. */
+         for (argc = 0; av[argc]; argc++)
+           continue;
+         argv = (char **) calloc (argc + 1, sizeof (char *));
+         for (char **argvp = argv; *av; av++)
+           *argvp++ = strdup (*av);
+       }
+
 
-  char **av = (char **) cygwin_internal (CW_ARGV);
-  if (av && ((DWORD) av != (DWORD) -1))
-    for (argc = 0, argv = av; *av; av++)
-      argc++;
-
-  char **envp = (char **) cygwin_internal (CW_ENVP);
-  if (envp && ((DWORD) envp != (DWORD) -1))
-    {
-      /* Store path and revert to this value, otherwise path gets overwritten
-        by the POSIXy Cygwin variation, which breaks cygcheck.
-        Another approach would be to use the Cygwin PATH and convert it to
-        Win32 again. */
-      char *path = NULL;
-      char **env;
-      while (*(env = _environ))
+      char **envp = (char **) cygwin_internal (CW_ENVP);
+      if (envp && ((uintptr_t) envp != (uintptr_t) -1))
        {
-         if (strncmp (*env, "PATH=", 5) == 0)
-           path = strdup (*env);
-         nuke (*env);
+         /* Store path and revert to this value, otherwise path gets
+            overwritten by the POSIXy Cygwin variation, which breaks cygcheck.
+            Another approach would be to use the Cygwin PATH and convert it to
+            Win32 again. */
+         char *path = NULL;
+         char **env;
+         while (*(env = _environ))
+           {
+             if (strncmp (*env, "PATH=", 5) == 0)
+               path = strdup (*env);
+             nuke (*env);
+           }
+         for (char **ev = envp; *ev; ev++)
+           if (strncmp (*ev, "PATH=", 5) != 0)
+             putenv (strdup (*ev));
+         if (path)
+           putenv (path);
        }
-      for (char **ev = envp; *ev; ev++)
-       if (strncmp (*ev, "PATH=", 5) != 0)
-        putenv (*ev);
-      if (path)
-       putenv (path);
     }
+  /* GDB chokes when the DLL got unloaded and, for some reason, fails to set
+     any breakpoint after the fact. */
+  if (!IsDebuggerPresent ())
+    FreeLibrary (h);
 }
 
 int
@@ -1792,7 +2261,14 @@ main (int argc, char **argv)
   bool ok = true;
   load_cygwin (argc, argv);
 
-  (void) putenv("POSIXLY_CORRECT=1");
+  _setmode (1, _O_BINARY);
+  _setmode (2, _O_BINARY);
+
+  /* Need POSIX sorting while parsing args, but don't forget the
+     user's original environment.  */
+  char *posixly = getenv ("POSIXLY_CORRECT");
+  if (posixly == NULL)
+    (void) putenv ("POSIXLY_CORRECT=1");
   while ((i = getopt_long (argc, argv, opts, longopts, NULL)) != EOF)
     switch (i)
       {
@@ -1821,34 +2297,46 @@ main (int argc, char **argv)
        list_package = 1;
        break;
       case 'p':
-        grep_packages = 1;
-        break;
+       grep_packages = 1;
+       break;
       case 'h':
        givehelp = 1;
        break;
+      case CO_DELETE_KEYS:
+       del_orphaned_reg = 1;
+       break;
       case 'V':
        print_version ();
        exit (0);
       default:
-       usage (stderr, 1);
-       /*NOTREACHED*/}
+       fprintf (stderr, "Try `cygcheck --help' for more information.\n");
+       exit (1);
+       /*NOTREACHED*/
+    }
   argc -= optind;
   argv += optind;
+  if (posixly == NULL)
+    putenv ("POSIXLY_CORRECT=");
 
-  if (argc == 0 && !sysinfo && !keycheck && !check_setup && !list_package)
-    if (givehelp)
-      usage (stdout, 0);
-    else
-      usage (stderr, 1);
+  if ((argc == 0) && !sysinfo && !keycheck && !check_setup && !list_package
+      && !del_orphaned_reg)
+    {
+      if (givehelp)
+       usage (stdout, 0);
+      else
+       usage (stderr, 1);
+    }
 
-  if ((check_setup || sysinfo || find_package || list_package || grep_packages)
+  if ((check_setup || sysinfo || find_package || list_package || grep_packages
+       || del_orphaned_reg)
       && keycheck)
     usage (stderr, 1);
 
-  if ((find_package || list_package || grep_packages) && check_setup)
+  if ((find_package || list_package || grep_packages)
+      && (check_setup || del_orphaned_reg))
     usage (stderr, 1);
 
-  if (dump_only && !check_setup)
+  if (dump_only && !check_setup && !sysinfo)
     usage (stderr, 1);
 
   if (find_package + list_package + grep_packages > 1)
@@ -1856,6 +2344,8 @@ main (int argc, char **argv)
 
   if (keycheck)
     return check_keys ();
+  if (del_orphaned_reg)
+    del_orphaned_reg_installations ();
   if (grep_packages)
     return package_grep (*argv);
 
@@ -1892,7 +2382,7 @@ main (int argc, char **argv)
       if (!check_setup)
        {
          puts ("");
-         dump_setup (verbose, NULL, false);
+         dump_setup (verbose, NULL, !dump_only);
        }
 
       if (!givehelp)
This page took 0.072994 seconds and 5 git commands to generate.