OSDN Git Service

* cygcheck.cc (check_keys): Use UNICODE Win32 functions.
[pf3gnuchains/pf3gnuchains4x.git] / winsup / utils / cygcheck.cc
1 /* cygcheck.cc
2
3    Copyright 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
4    2006, 2007, 2008, 2009, 2010, 2011 Red Hat, Inc.
5
6    This file is part of Cygwin.
7
8    This software is a copyrighted work licensed under the terms of the
9    Cygwin license.  Please consult the file "CYGWIN_LICENSE" for
10    details. */
11
12 #define cygwin_internal cygwin_internal_dontuse
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <stdarg.h>
16 #include <string.h>
17 #include <sys/time.h>
18 #include <ctype.h>
19 #include <io.h>
20 #include <windows.h>
21 #include <wininet.h>
22 #include "path.h"
23 #include "wide_path.h"
24 #include <getopt.h>
25 #include "cygwin/include/sys/cygwin.h"
26 #include "cygwin/include/mntent.h"
27 #include "cygwin/cygprops.h"
28 #undef cygwin_internal
29 #include "loadlib.h"
30
31 #define alloca __builtin_alloca
32
33 int verbose = 0;
34 int registry = 0;
35 int sysinfo = 0;
36 int givehelp = 0;
37 int keycheck = 0;
38 int check_setup = 0;
39 int dump_only = 0;
40 int find_package = 0;
41 int list_package = 0;
42 int grep_packages = 0;
43 int del_orphaned_reg = 0;
44 int unique_object_name_opt = 0;
45
46 static char emptystr[] = "";
47
48 /* This is global because it's used in both internet_display_error as well
49    as package_grep.  */
50 BOOL (WINAPI *pInternetCloseHandle) (HINTERNET);
51
52 #ifdef __GNUC__
53 typedef long long longlong;
54 #else
55 typedef __int64 longlong;
56 #endif
57
58 /* In dump_setup.cc  */
59 void dump_setup (int, char **, bool);
60 void package_find (int, char **);
61 void package_list (int, char **);
62 /* In bloda.cc  */
63 void dump_dodgy_apps (int verbose);
64 /* Forward declaration */
65 static void usage (FILE *, int);
66
67 static const char version[] = "$Revision$";
68
69 static const char *known_env_vars[] = {
70   "c_include_path",
71   "compiler_path",
72   "cxx_include_path",
73   "cygwin",
74   "cygwin32",
75   "dejagnu",
76   "expect",
77   "gcc_default_options",
78   "gcc_exec_prefix",
79   "home",
80   "ld_library_path",
81   "library_path",
82   "login",
83   "lpath",
84   "make_mode",
85   "makeflags",
86   "path",
87   "pwd",
88   "strace",
89   "tcl_library",
90   "user",
91   0
92 };
93
94 struct
95 {
96   const char *name;
97   int missing_is_good;
98 }
99 static common_apps[] = {
100   {"awk", 0},
101   {"bash", 0},
102   {"cat", 0},
103   {"cp", 0},
104   {"cpp", 1},
105   {"crontab", 0},
106   {"find", 0},
107   {"gcc", 0},
108   {"gdb", 0},
109   {"grep", 0},
110   {"kill", 0},
111   {"ld", 0},
112   {"ls", 0},
113   {"make", 0},
114   {"mv", 0},
115   {"patch", 0},
116   {"perl", 0},
117   {"rm", 0},
118   {"sed", 0},
119   {"ssh", 0},
120   {"sh", 0},
121   {"tar", 0},
122   {"test", 0},
123   {"vi", 0},
124   {"vim", 0},
125   {0, 0}
126 };
127
128 /* Options without ASCII single char representation. */
129 enum
130 {
131   CO_DELETE_KEYS = 0x100,
132   CO_ENABLE_UON = 0x101,
133   CO_DISABLE_UON = 0x102,
134   CO_SHOW_UON = 0x103
135 };
136
137 static int num_paths, max_paths;
138 struct pathlike
139 {
140   char *dir;
141   bool issys;
142   void check_existence (const char *fn, int showall, int verbose,
143                         char* first, const char *ext1 = "",
144                         const char *ext2 = "");
145 };
146
147 pathlike *paths;
148 int first_nonsys_path;
149
150 void
151 eprintf (const char *format, ...)
152 {
153   va_list ap;
154   va_start (ap, format);
155   vfprintf (stderr, format, ap);
156   va_end (ap);
157 }
158
159 /*
160  * display_error() is used to report failure modes
161  */
162 static int
163 display_error (const char *name, bool show_error, bool print_failed)
164 {
165   fprintf (stderr, "cygcheck: %s", name);
166   if (show_error)
167     fprintf (stderr, "%s: %lu\n",
168         print_failed ? " failed" : "", GetLastError ());
169   else
170     fprintf (stderr, "%s\n",
171         print_failed ? " failed" : "");
172   return 1;
173 }
174
175 static int
176 display_error (const char *name)
177 {
178   return display_error (name, true, true);
179 }
180
181 static int
182 display_error (const char *fmt, const char *x)
183 {
184   char buf[4000];
185   snprintf (buf, sizeof buf, fmt, x);
186   return display_error (buf, false, false);
187 }
188
189 static int
190 display_error_fmt (const char *fmt, ...)
191 {
192   char buf[4000];
193   va_list va;
194
195   va_start (va, fmt);
196   vsnprintf (buf, sizeof buf, fmt, va);
197   return display_error (buf, false, false);
198 }
199
200 /* Display a WinInet error message, and close a variable number of handles.
201    (Passed a list of handles terminated by NULL.)  */
202 static int
203 display_internet_error (const char *message, ...)
204 {
205   DWORD err = GetLastError ();
206   TCHAR err_buf[256];
207   va_list hptr;
208   HINTERNET h;
209
210   /* in the case of a successful connection but 404 response, there is no
211      win32 error message, but we still get passed a message to display.  */
212   if (err)
213     {
214       if (FormatMessage (FORMAT_MESSAGE_FROM_HMODULE,
215           GetModuleHandle ("wininet.dll"), err, 0, err_buf,
216           sizeof (err_buf), NULL) == 0)
217         strcpy (err_buf, "(Unknown error)");
218
219       fprintf (stderr, "cygcheck: %s: %s (win32 error %lu)\n", message,
220                err_buf, err);
221     }
222   else
223     fprintf (stderr, "cygcheck: %s\n", message);
224
225   va_start (hptr, message);
226   while ((h = va_arg (hptr, HINTERNET)) != 0)
227     pInternetCloseHandle (h);
228   va_end (hptr);
229
230   return 1;
231 }
232
233 static void
234 add_path (char *s, int maxlen, bool issys)
235 {
236   if (num_paths >= max_paths)
237     {
238       max_paths += 10;
239       /* Extend path array */
240       paths = (pathlike *) realloc (paths, (1 + max_paths) * sizeof (paths[0]));
241     }
242
243   pathlike *pth = paths + num_paths;
244
245   /* Allocate space for directory in path list */
246   char *dir = (char *) calloc (maxlen + 2, sizeof (char));
247   if (dir == NULL)
248     {
249       display_error ("add_path: calloc() failed");
250       return;
251     }
252
253   /* Copy input directory to path list */
254   memcpy (dir, s, maxlen);
255
256   /* Add a trailing slash by default */
257   char *e = strchr (dir, '\0');
258   if (e != dir && e[-1] != '\\')
259     strcpy (e, "\\");
260
261   /* Fill out this element */
262   pth->dir = dir;
263   pth->issys = issys;
264   pth[1].dir = NULL;
265   num_paths++;
266 }
267
268 static void
269 init_paths ()
270 {
271   char tmp[4000], *sl;
272   add_path ((char *) ".", 1, true);     /* to be replaced later */
273
274   if (GetCurrentDirectory (4000, tmp))
275     add_path (tmp, strlen (tmp), true);
276   else
277     display_error ("init_paths: GetCurrentDirectory()");
278
279   if (GetSystemDirectory (tmp, 4000))
280     add_path (tmp, strlen (tmp), true);
281   else
282     display_error ("init_paths: GetSystemDirectory()");
283   sl = strrchr (tmp, '\\');
284   if (sl)
285     {
286       strcpy (sl, "\\SYSTEM");
287       add_path (tmp, strlen (tmp), true);
288     }
289   GetWindowsDirectory (tmp, 4000);
290   add_path (tmp, strlen (tmp), true);
291
292   char *wpath = getenv ("PATH");
293   if (!wpath)
294     display_error ("WARNING: PATH is not set\n", "");
295   else
296     {
297       char *b, *e;
298       b = wpath;
299       while (1)
300         {
301           for (e = b; *e && *e != ';'; e++)
302             continue;   /* loop terminates at first ';' or EOS */
303           if (strncmp(b, ".\\", 2) != 0)
304             add_path (b, e - b, false);
305           if (!*e)
306             break;
307           b = e + 1;
308         }
309     }
310 }
311
312 #define LINK_EXTENSION ".lnk"
313
314 void
315 pathlike::check_existence (const char *fn, int showall, int verbose,
316                            char* first, const char *ext1, const char *ext2)
317 {
318   char file[4000];
319   strcpy (file, dir);
320   strcat (file, fn);
321   strcat (file, ext1);
322   strcat (file, ext2);
323
324   wide_path wpath (file);
325   if (GetFileAttributesW (wpath) != (DWORD) - 1)
326     {
327       char *lastdot = strrchr (file, '.');
328       bool is_link = lastdot && !strcmp (lastdot, LINK_EXTENSION);
329       // If file is a link, fix up the extension before printing
330       if (is_link)
331         *lastdot = '\0';
332       if (showall)
333         printf ("Found: %s\n", file);
334       if (verbose && *first != '\0' && strcasecmp (first, file) != 0)
335         {
336           char *flastdot = strrchr (first, '.');
337           bool f_is_link = flastdot && !strcmp (flastdot, LINK_EXTENSION);
338           // if first is a link, fix up the extension before printing
339           if (f_is_link)
340             *flastdot = '\0';
341           printf ("Warning: %s hides %s\n", first, file);
342           if (f_is_link)
343             *flastdot = '.';
344         }
345       if (is_link)
346         *lastdot = '.';
347       if (!*first)
348         strcpy (first, file);
349     }
350 }
351
352 static const char *
353 find_on_path (const char *in_file, const char *ext, bool showall = false,
354               bool search_sys = false, bool checklinks = false)
355 {
356   static char rv[4000];
357
358   /* Sort of a kludge but we've already tested this once, so don't try it again */
359   if (in_file == rv)
360     return in_file;
361
362   static pathlike abspath[2] =
363   {
364     {emptystr, 0},
365     {NULL, 0}
366   };
367
368   *rv = '\0';
369   if (!in_file)
370     {
371       display_error ("internal error find_on_path: NULL pointer for file", false, false);
372       return 0;
373     }
374
375   if (!ext)
376     {
377       display_error ("internal error find_on_path: NULL pointer for default_extension", false, false);
378       return 0;
379     }
380
381   const char *file;
382   pathlike *search_paths;
383   if (!strpbrk (in_file, ":/\\"))
384     {
385       file = in_file;
386       search_paths = paths;
387     }
388   else
389     {
390       file = cygpath (in_file, NULL);
391       search_paths = abspath;
392       showall = false;
393     }
394
395   if (!file)
396     {
397       display_error ("internal error find_on_path: cygpath conversion failed for %s\n", in_file);
398       return 0;
399     }
400
401   char *hasext = strrchr (file, '.');
402   if (hasext && !strpbrk (hasext, "/\\"))
403     ext = "";
404
405   for (pathlike *pth = search_paths; pth->dir; pth++)
406     if (!pth->issys || search_sys)
407       {
408         pth->check_existence (file, showall, verbose, rv, ext);
409
410         if (checklinks)
411           pth->check_existence (file, showall, verbose, rv, ext, LINK_EXTENSION);
412
413         if (!*ext)
414           continue;
415
416         pth->check_existence (file, showall, verbose, rv);
417         if (checklinks)
418           pth->check_existence (file, showall, verbose, rv, LINK_EXTENSION);
419       }
420
421   return *rv ? rv : NULL;
422 }
423
424 #define DID_NEW         1
425 #define DID_ACTIVE      2
426 #define DID_INACTIVE    3
427
428 struct Did
429 {
430   Did *next;
431   char *file;
432   int state;
433 };
434 static Did *did = 0;
435
436 static Did *
437 already_did (const char *file)
438 {
439   Did *d;
440   for (d = did; d; d = d->next)
441     if (strcasecmp (d->file, file) == 0)
442       return d;
443   d = (Did *) malloc (sizeof (Did));
444   d->file = strdup (file);
445   d->next = did;
446   d->state = DID_NEW;
447   did = d;
448   return d;
449 }
450
451 struct Section
452 {
453   char name[8];
454   int virtual_size;
455   int virtual_address;
456   int size_of_raw_data;
457   int pointer_to_raw_data;
458 };
459
460 static int
461 rva_to_offset (int rva, char *sections, int nsections, int *sz)
462 {
463   int i;
464
465   if (sections == NULL)
466     {
467       display_error ("rva_to_offset: NULL passed for sections", true, false);
468       return 0;
469     }
470
471   for (i = 0; i < nsections; i++)
472     {
473       Section *s = (Section *) (sections + i * 40);
474 #if 0
475       printf ("%08x < %08x < %08x ? %08x\n",
476               s->virtual_address, rva,
477               s->virtual_address + s->virtual_size, s->pointer_to_raw_data);
478 #endif
479       if (rva >= s->virtual_address
480           && rva < s->virtual_address + s->virtual_size)
481         {
482           if (sz)
483             *sz = s->virtual_address + s->virtual_size - rva;
484           return rva - s->virtual_address + s->pointer_to_raw_data;
485         }
486     }
487   return 0;                     /* punt */
488 }
489
490 struct ExpDirectory
491 {
492   int flags;
493   int timestamp;
494   short major_ver;
495   short minor_ver;
496   int name_rva;
497 };
498
499 struct ImpDirectory
500 {
501   unsigned characteristics;
502   unsigned timestamp;
503   unsigned forwarder_chain;
504   unsigned name_rva;
505   unsigned iat_rva;
506 };
507
508 static bool track_down (const char *file, const char *suffix, int lvl);
509
510 #define CYGPREFIX (sizeof ("%%% Cygwin ") - 1)
511 static void
512 cygwin_info (HANDLE h)
513 {
514   char *buf, *bufend, *buf_start = NULL;
515   const char *hello = "    Cygwin DLL version info:\n";
516   DWORD size = GetFileSize (h, NULL);
517   DWORD n;
518
519   if (size == 0xffffffff)
520     return;
521
522   buf_start = buf = (char *) calloc (1, size + 1);
523   if (buf == NULL)
524     {
525       display_error ("cygwin_info: calloc()");
526       return;
527     }
528
529   (void) SetFilePointer (h, 0, NULL, FILE_BEGIN);
530   if (!ReadFile (h, buf, size, &n, NULL))
531     {
532       free (buf_start);
533       return;
534     }
535
536   static char dummy[] = "\0\0\0\0\0\0\0";
537   char *dll_major = dummy;
538   bufend = buf + size;
539   while (buf < bufend)
540     if ((buf = (char *) memchr (buf, '%', bufend - buf)) == NULL)
541       break;
542     else if (strncmp ("%%% Cygwin ", buf, CYGPREFIX) != 0)
543       buf++;
544     else
545       {
546         char *p = strchr (buf += CYGPREFIX, '\n');
547         if (!p)
548           break;
549         if (strncasecmp (buf, "dll major:", 10) == 0)
550           {
551             dll_major = buf + 11;
552             continue;
553           }
554         char *s, pbuf[80];
555         int len;
556         len = 1 + p - buf;
557         if (strncasecmp (buf, "dll minor:", 10) != 0)
558           s = buf;
559         else
560           {
561             char c = dll_major[1];
562             dll_major[1] = '\0';
563             int maj = atoi (dll_major);
564             dll_major[1] = c;
565             int min = atoi (dll_major + 1);
566             sprintf (pbuf, "DLL version: %d.%d.%.*s", maj, min, len - 11,
567                      buf + 11);
568             len = strlen (s = pbuf);
569           }
570         if (strncmp (s, "dll", 3) == 0)
571           memcpy (s, "DLL", 3);
572         else if (strncmp (s, "api", 3) == 0)
573           memcpy (s, "API", 3);
574         else if (islower (*s))
575           *s = toupper (*s);
576         fprintf (stdout, "%s        %.*s", hello, len, s);
577         hello = "";
578       }
579
580   if (!*hello)
581     puts ("");
582
583   free (buf_start);
584   return;
585 }
586
587 static void
588 dll_info (const char *path, HANDLE fh, int lvl, int recurse)
589 {
590   DWORD junk;
591   int i;
592   int pe_header_offset = get_dword (fh, 0x3c);
593   if (GetLastError () != NO_ERROR)
594     display_error ("get_dword");
595   int opthdr_ofs = pe_header_offset + 4 + 20;
596   unsigned short v[6];
597
598   if (path == NULL)
599     {
600       display_error ("dll_info: NULL passed for path", true, false);
601       return;
602     }
603
604   if (SetFilePointer (fh, opthdr_ofs + 40, 0, FILE_BEGIN) ==
605       INVALID_SET_FILE_POINTER && GetLastError () != NO_ERROR)
606     display_error ("dll_info: SetFilePointer()");
607
608   if (!ReadFile (fh, &v, sizeof (v), &junk, 0))
609     display_error ("dll_info: Readfile()");
610
611   if (verbose)
612     printf (" - os=%d.%d img=%d.%d sys=%d.%d\n",
613             v[0], v[1], v[2], v[3], v[4], v[5]);
614   else
615     printf ("\n");
616
617   int num_entries = get_dword (fh, opthdr_ofs + 92);
618   if (GetLastError () != NO_ERROR)
619     display_error ("get_dword");
620   int export_rva = get_dword (fh, opthdr_ofs + 96);
621   if (GetLastError () != NO_ERROR)
622     display_error ("get_dword");
623   int export_size = get_dword (fh, opthdr_ofs + 100);
624   if (GetLastError () != NO_ERROR)
625     display_error ("get_dword");
626   int import_rva = get_dword (fh, opthdr_ofs + 104);
627   if (GetLastError () != NO_ERROR)
628     display_error ("get_dword");
629   int import_size = get_dword (fh, opthdr_ofs + 108);
630   if (GetLastError () != NO_ERROR)
631     display_error ("get_dword");
632
633   int nsections = get_word (fh, pe_header_offset + 4 + 2);
634   if (nsections == -1)
635     display_error ("get_word");
636   char *sections = (char *) malloc (nsections * 40);
637
638   if (SetFilePointer (fh, pe_header_offset + 4 + 20 +
639                       get_word (fh, pe_header_offset + 4 + 16), 0,
640                       FILE_BEGIN) == INVALID_SET_FILE_POINTER
641       && GetLastError () != NO_ERROR)
642     display_error ("dll_info: SetFilePointer()");
643
644   if (!ReadFile (fh, sections, nsections * 40, &junk, 0))
645     display_error ("dll_info: Readfile()");
646
647   if (verbose && num_entries >= 1 && export_size > 0)
648     {
649       int expsz;
650       int expbase = rva_to_offset (export_rva, sections, nsections, &expsz);
651
652       if (expbase)
653         {
654           if (SetFilePointer (fh, expbase, 0, FILE_BEGIN) ==
655               INVALID_SET_FILE_POINTER && GetLastError () != NO_ERROR)
656             display_error ("dll_info: SetFilePointer()");
657
658           unsigned char *exp = (unsigned char *) malloc (expsz);
659
660           if (!ReadFile (fh, exp, expsz, &junk, 0))
661             display_error ("dll_info: Readfile()");
662
663           ExpDirectory *ed = (ExpDirectory *) exp;
664           int ofs = ed->name_rva - export_rva;
665           struct tm *tm = localtime ((const time_t *) &(ed->timestamp));
666           if (tm->tm_year < 60)
667             tm->tm_year += 2000;
668           if (tm->tm_year < 200)
669             tm->tm_year += 1900;
670           printf ("%*c", lvl + 2, ' ');
671           printf ("\"%s\" v%d.%d ts=", exp + ofs,
672                   ed->major_ver, ed->minor_ver);
673           printf ("%d/%d/%d %d:%02d\n",
674                   tm->tm_year, tm->tm_mon + 1, tm->tm_mday,
675                   tm->tm_hour, tm->tm_min);
676         }
677     }
678
679   if (num_entries >= 2 && import_size > 0 && recurse)
680     {
681       int impsz;
682       int impbase = rva_to_offset (import_rva, sections, nsections, &impsz);
683       if (impbase)
684         {
685           if (SetFilePointer (fh, impbase, 0, FILE_BEGIN) ==
686               INVALID_SET_FILE_POINTER && GetLastError () != NO_ERROR)
687             display_error ("dll_info: SetFilePointer()");
688
689           unsigned char *imp = (unsigned char *) malloc (impsz);
690           if (imp == NULL)
691             {
692               display_error ("dll_info: malloc()");
693               return;
694             }
695
696           if (!ReadFile (fh, imp, impsz, &junk, 0))
697             display_error ("dll_info: Readfile()");
698
699           ImpDirectory *id = (ImpDirectory *) imp;
700           for (i = 0; id[i].name_rva; i++)
701             {
702               /* int ofs = id[i].name_rva - import_rva; */
703               track_down ((char *) imp + id[i].name_rva - import_rva,
704                           (char *) ".dll", lvl + 2);
705             }
706         }
707     }
708   if (strstr (path, "\\cygwin1.dll"))
709     cygwin_info (fh);
710 }
711
712 // Return true on success, false if error printed
713 static bool
714 track_down (const char *file, const char *suffix, int lvl)
715 {
716   if (file == NULL)
717     {
718       display_error ("track_down: NULL passed for file", true, false);
719       return false;
720     }
721
722   if (suffix == NULL)
723     {
724       display_error ("track_down: NULL passed for suffix", false, false);
725       return false;
726     }
727
728   const char *path = find_on_path (file, suffix, false, true);
729   if (!path)
730     {
731       display_error ("track_down: could not find %s\n", file);
732       return false;
733     }
734
735   Did *d = already_did (file);
736   switch (d->state)
737     {
738     case DID_NEW:
739       break;
740     case DID_ACTIVE:
741       if (verbose)
742         {
743           if (lvl)
744             printf ("%*c", lvl, ' ');
745           printf ("%s", path);
746           printf (" (recursive)\n");
747         }
748       return true;
749     case DID_INACTIVE:
750       if (verbose)
751         {
752           if (lvl)
753             printf ("%*c", lvl, ' ');
754           printf ("%s", path);
755           printf (" (already done)\n");
756         }
757       return true;
758     default:
759       break;
760     }
761
762   if (lvl)
763     printf ("%*c", lvl, ' ');
764
765   if (!path)
766     {
767       display_error ("file not found - '%s'\n", file);
768       return false;
769     }
770
771   printf ("%s", path);
772
773   wide_path wpath (path);
774   HANDLE fh =
775     CreateFileW (wpath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
776                  NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
777   if (fh == INVALID_HANDLE_VALUE)
778     {
779       display_error ("cannot open - '%s'\n", path);
780       return false;
781     }
782
783   d->state = DID_ACTIVE;
784
785   if (is_exe (fh))
786     dll_info (path, fh, lvl, 1);
787   else if (is_symlink (fh))
788     display_error ("%s is a symlink instead of a DLL\n", path);
789   else
790     {
791       int magic = get_word (fh, 0x0);
792       if (magic == -1)
793         display_error ("get_word");
794       magic &= 0x00FFFFFF;
795       display_error_fmt ("%s is not a DLL: magic number %x (%d) '%s'\n",
796                          path, magic, magic, (char *)&magic);
797     }
798
799   d->state = DID_INACTIVE;
800   if (!CloseHandle (fh))
801     display_error ("track_down: CloseHandle()");
802   return true;
803 }
804
805 static void
806 ls (char *f)
807 {
808   wide_path wpath (f);
809   HANDLE h = CreateFileW (wpath, GENERIC_READ,
810                           FILE_SHARE_READ | FILE_SHARE_WRITE,
811                           0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
812   BY_HANDLE_FILE_INFORMATION info;
813
814   if (!GetFileInformationByHandle (h, &info))
815     display_error ("ls: GetFileInformationByHandle()");
816
817   SYSTEMTIME systime;
818
819   if (!FileTimeToSystemTime (&info.ftLastWriteTime, &systime))
820     display_error ("ls: FileTimeToSystemTime()");
821   printf ("%5dk %04d/%02d/%02d %s",
822           (((int) info.nFileSizeLow) + 512) / 1024,
823           systime.wYear, systime.wMonth, systime.wDay, f);
824   dll_info (f, h, 16, 0);
825   if (!CloseHandle (h))
826     display_error ("ls: CloseHandle()");
827 }
828
829 /* Remove filename from 's' and return directory name without trailing
830    backslash, or NULL if 's' doesn't seem to have a dirname.  */
831 static char *
832 dirname (const char *s)
833 {
834   static char buf[PATH_MAX];
835
836   if (!s)
837     return NULL;
838
839   strncpy (buf, s, PATH_MAX);
840   buf[PATH_MAX - 1] = '\0';   // in case strlen(s) > PATH_MAX
841   char *lastsep = strrchr (buf, '\\');
842   if (!lastsep)
843     return NULL;          // no backslash -> no dirname
844   else if (lastsep - buf <= 2 && buf[1] == ':')
845     lastsep[1] = '\0';    // can't remove backslash of "x:\"
846   else
847     *lastsep = '\0';
848   return buf;
849 }
850
851 // Find a real application on the path (possibly following symlinks)
852 static const char *
853 find_app_on_path (const char *app, bool showall = false)
854 {
855   const char *papp = find_on_path (app, ".exe", showall, false, true);
856
857   if (!papp)
858     return NULL;
859
860   wide_path wpath (papp);
861   HANDLE fh =
862     CreateFileW (wpath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
863                  NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
864   if (fh == INVALID_HANDLE_VALUE)
865     return NULL;
866
867   if (is_symlink (fh))
868     {
869       static char tmp[SYMLINK_MAX + 1];
870       if (!readlink (fh, tmp, SYMLINK_MAX))
871         display_error("readlink failed");
872       
873       /* Resolve the linkname relative to the directory of the link.  */
874       char *ptr = cygpath_rel (dirname (papp), tmp, NULL);
875       printf (" -> %s\n", ptr);
876       if (!strchr (ptr, '\\'))
877         {
878           char *lastsep;
879           strncpy (tmp, cygpath (papp, NULL), SYMLINK_MAX);
880           lastsep = strrchr (tmp, '\\');
881           strncpy (lastsep+1, ptr, SYMLINK_MAX - (lastsep-tmp));
882           ptr = tmp;
883         }
884       if (!CloseHandle (fh))
885         display_error ("find_app_on_path: CloseHandle()");
886       /* FIXME: We leak the ptr returned by cygpath() here which is a
887          malloc()d string.  */
888       return find_app_on_path (ptr, showall);
889     }
890
891   if (!CloseHandle (fh))
892     display_error ("find_app_on_path: CloseHandle()");
893   return papp;
894 }
895
896 // Return true on success, false if error printed
897 static bool
898 cygcheck (const char *app)
899 {
900   const char *papp = find_app_on_path (app, 1);
901   if (!papp)
902     {
903       display_error ("could not find '%s'\n", app);
904       return false;
905     }
906
907   char *s;
908   char *sep = strpbrk (papp, ":/\\");
909   if (!sep)
910     {
911       static char dot[] = ".";
912       s = dot;
913     }
914   else
915     {
916       int n = sep - papp;
917       s = (char *) malloc (n + 2);
918       memcpy ((char *) s, papp, n);
919       strcpy (s + n, "\\");
920     }
921
922   paths[0].dir = s;
923   did = NULL;
924   return track_down (papp, ".exe", 0);
925 }
926
927 struct RegInfo
928 {
929   RegInfo *prev;
930   char *name;
931   HKEY key;
932 };
933
934 static void
935 show_reg (RegInfo * ri, int nest)
936 {
937   if (!ri)
938     return;
939   show_reg (ri->prev, 1);
940   if (nest)
941     printf ("%s\\", ri->name);
942   else
943     printf ("%s\n", ri->name);
944 }
945
946 static void
947 scan_registry (RegInfo * prev, HKEY hKey, char *name, int cygwin, bool wow64)
948 {
949   RegInfo ri;
950   ri.prev = prev;
951   ri.name = name;
952   ri.key = hKey;
953
954   char *cp;
955   for (cp = name; *cp; cp++)
956     if (strncasecmp (cp, "Cygwin", 6) == 0)
957       cygwin = 1;
958
959   DWORD num_subkeys, max_subkey_len, num_values;
960   DWORD max_value_len, max_valdata_len, i;
961   if (RegQueryInfoKey (hKey, 0, 0, 0, &num_subkeys, &max_subkey_len, 0,
962                        &num_values, &max_value_len, &max_valdata_len, 0, 0)
963       != ERROR_SUCCESS)
964     {
965 #if 0
966       char tmp[400];
967       FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, 0, GetLastError (),
968                      MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), tmp, 400, 0);
969       printf ("RegQueryInfoKey: %s\n", tmp);
970 #endif
971       return;
972     }
973
974   if (cygwin)
975     {
976       show_reg (&ri, 0);
977
978       char *value_name = (char *) malloc (max_value_len + 1);
979       if (value_name == NULL)
980         {
981           display_error ("scan_registry: malloc()");
982           return;
983         }
984
985       char *value_data = (char *) malloc (max_valdata_len + 1);
986       if (value_data == NULL)
987         {
988           display_error ("scan_registry: malloc()");
989           return;
990         }
991
992       for (i = 0; i < num_values; i++)
993         {
994           DWORD dlen = max_valdata_len + 1;
995           DWORD nlen = max_value_len + 1;
996           DWORD type;
997           RegEnumValue (hKey, i, value_name, &nlen, 0,
998                         &type, (BYTE *) value_data, &dlen);
999           {
1000             printf ("  %s = ", i ? value_name : "(default)");
1001             switch (type)
1002               {
1003               case REG_DWORD:
1004                 printf ("0x%08x\n", *(unsigned *) value_data);
1005                 break;
1006               case REG_EXPAND_SZ:
1007               case REG_SZ:
1008                 printf ("'%s'\n", value_data);
1009                 break;
1010               default:
1011                 printf ("(unsupported type)\n");
1012                 break;
1013               }
1014           }
1015         }
1016       free (value_name);
1017       free (value_data);
1018     }
1019
1020   char *subkey_name = (char *) malloc (max_subkey_len + 1);
1021   for (i = 0; i < num_subkeys; i++)
1022     {
1023       if (RegEnumKey (hKey, i, subkey_name, max_subkey_len + 1) ==
1024           ERROR_SUCCESS)
1025         {
1026           HKEY sKey;
1027           /* Don't recurse more than one level into the WOW64 subkey since
1028              that would lead to an endless recursion. */
1029           if (!strcasecmp (subkey_name, "Wow6432Node"))
1030             {
1031               if (wow64)
1032                 continue;
1033               wow64 = true;
1034             }
1035           if (RegOpenKeyEx (hKey, subkey_name, 0, KEY_READ, &sKey)
1036               == ERROR_SUCCESS)
1037             {
1038               scan_registry (&ri, sKey, subkey_name, cygwin, wow64);
1039               if (RegCloseKey (sKey) != ERROR_SUCCESS)
1040                 display_error ("scan_registry: RegCloseKey()");
1041             }
1042         }
1043     }
1044   free (subkey_name);
1045 }
1046
1047 void
1048 pretty_id ()
1049 {
1050   char *groups[16384];
1051
1052   char *id = cygpath ("/bin/id.exe", NULL);
1053   for (char *p = id; (p = strchr (p, '/')); p++)
1054     *p = '\\';
1055
1056   if (access (id, X_OK))
1057     {
1058       fprintf (stderr, "'id' program not found\n");
1059       return;
1060     }
1061
1062   char buf[16384];
1063   snprintf (buf, sizeof (buf), "\"%s\"", id);
1064   FILE *f = popen (buf, "rt");
1065
1066   buf[0] = '\0';
1067   fgets (buf, sizeof (buf), f);
1068   pclose (f);
1069   char *uid = strtok (buf, ")");
1070   if (uid)
1071     uid += strlen ("uid=");
1072   else
1073     {
1074       fprintf (stderr, "garbled output from 'id' command - no uid= found\n");
1075       return;
1076     }
1077   char *gid = strtok (NULL, ")");
1078   if (gid)
1079     gid += strlen ("gid=") + 1;
1080   else
1081     {
1082       fprintf (stderr, "garbled output from 'id' command - no gid= found\n");
1083       return;
1084     }
1085
1086   char **ng = groups - 1;
1087   size_t len_uid = strlen ("UID: )") + strlen (uid);
1088   size_t len_gid = strlen ("GID: )") + strlen (gid);
1089   *++ng = groups[0] = (char *) alloca (len_uid + 1);
1090   *++ng = groups[1] = (char *) alloca (len_gid + 1);
1091   sprintf (groups[0], "UID: %s)", uid);
1092   sprintf (groups[1], "GID: %s)", gid);
1093   size_t sz = max (len_uid, len_gid);
1094   while ((*++ng = strtok (NULL, ",")))
1095     {
1096       char *p = strchr (*ng, '\n');
1097       if (p)
1098         *p = '\0';
1099       if (ng == groups + 2)
1100         *ng += strlen (" groups=");
1101       size_t len = strlen (*ng);
1102       if (sz < len)
1103         sz = len;
1104     }
1105   ng--;
1106
1107   printf ("\nOutput from %s\n", id);
1108   int n = 80 / (int) ++sz;
1109   int i = n > 2 ? n - 2 : 0;
1110   sz = -sz;
1111   for (char **g = groups; g <= ng; g++)
1112     if ((g != ng) && (++i < n))
1113       printf ("%*s", sz, *g);
1114     else
1115       {
1116         puts (*g);
1117         i = 0;
1118       }
1119 }
1120
1121 /* This dumps information about each installed cygwin service, if cygrunsrv
1122    is available.  */
1123 void
1124 dump_sysinfo_services ()
1125 {
1126   char buf[1024];
1127   char buf2[1024];
1128   FILE *f;
1129   bool no_services = false;
1130
1131   if (givehelp)
1132     printf ("\nChecking for any Cygwin services... %s\n\n",
1133                   verbose ? "" : "(use -v for more detail)");
1134   else
1135     fputc ('\n', stdout);
1136
1137   /* find the location of cygrunsrv.exe */
1138   char *cygrunsrv = cygpath ("/bin/cygrunsrv.exe", NULL);
1139   for (char *p = cygrunsrv; (p = strchr (p, '/')); p++)
1140     *p = '\\';
1141
1142   if (access (cygrunsrv, X_OK))
1143     {
1144       puts ("Can't find the cygrunsrv utility, skipping services check.\n");
1145       return;
1146     }
1147
1148   /* check for a recent cygrunsrv */
1149   snprintf (buf, sizeof (buf), "\"%s\" --version", cygrunsrv);
1150   if ((f = popen (buf, "rt")) == NULL)
1151     {
1152       printf ("Failed to execute '%s', skipping services check.\n", buf);
1153       return;
1154     }
1155   int maj, min;
1156   int ret = fscanf (f, "cygrunsrv V%u.%u", &maj, &min);
1157   if (ferror (f) || feof (f) || ret == EOF || maj < 1 || min < 10)
1158     {
1159       puts ("The version of cygrunsrv installed is too old to dump service info.\n");
1160       return;
1161     }
1162   fclose (f);
1163
1164   /* For verbose mode, just run cygrunsrv --list --verbose and copy output
1165      verbatim; otherwise run cygrunsrv --list and then cygrunsrv --query for
1166      each service.  */
1167   snprintf (buf, sizeof (buf), (verbose ? "\"%s\" --list --verbose" : "\"%s\" --list"),
1168             cygrunsrv);
1169   if ((f = popen (buf, "rt")) == NULL)
1170     {
1171       printf ("Failed to execute '%s', skipping services check.\n", buf);
1172       return;
1173     }
1174
1175   if (verbose)
1176     {
1177       /* copy output to stdout */
1178       size_t nchars = 0;
1179       while (!feof (f) && !ferror (f))
1180           nchars += fwrite ((void *) buf, 1,
1181                             fread ((void *) buf, 1, sizeof (buf), f), stdout);
1182
1183       /* cygrunsrv outputs nothing if there are no cygwin services found */
1184       if (nchars < 1)
1185         no_services = true;
1186       pclose (f);
1187     }
1188   else
1189     {
1190       /* read the output of --list, and then run --query for each service */
1191       size_t nchars = fread ((void *) buf, 1, sizeof (buf) - 1, f);
1192       buf[nchars] = 0;
1193       pclose (f);
1194
1195       if (nchars > 0)
1196         for (char *srv = strtok (buf, "\n"); srv; srv = strtok (NULL, "\n"))
1197           {
1198             snprintf (buf2, sizeof (buf2), "\"%s\" --query %s", cygrunsrv, srv);
1199             if ((f = popen (buf2, "rt")) == NULL)
1200               {
1201                 printf ("Failed to execute '%s', skipping services check.\n", buf2);
1202                 return;
1203               }
1204
1205             /* copy output to stdout */
1206             while (!feof (f) && !ferror (f))
1207               fwrite ((void *) buf2, 1,
1208                       fread ((void *) buf2, 1, sizeof (buf2), f), stdout);
1209             pclose (f);
1210           }
1211       else
1212         no_services = true;
1213     }
1214
1215   /* inform the user if nothing found */
1216   if (no_services)
1217     puts ("No Cygwin services found.\n");
1218 }
1219
1220 enum handle_reg_t
1221 {
1222   PRINT_KEY,
1223   DELETE_KEY
1224 };
1225
1226 void
1227 handle_reg_installation (handle_reg_t what)
1228 {
1229   HKEY key;
1230
1231   if (what == PRINT_KEY)
1232     printf ("Cygwin installations found in the registry:\n");
1233   for (int i = 0; i < 2; ++i)
1234     if (RegOpenKeyEx (i ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE,
1235                       "SOFTWARE\\Cygwin\\Installations", 0,
1236                       what == DELETE_KEY ? KEY_READ | KEY_WRITE : KEY_READ,
1237                       &key)
1238         == ERROR_SUCCESS)
1239       {
1240         char name[32], data[PATH_MAX];
1241         DWORD nsize, dsize, type;
1242         LONG ret;
1243
1244         for (DWORD index = 0;
1245              (ret = RegEnumValue (key, index, name, (nsize = 32, &nsize), 0,
1246                                   &type, (PBYTE) data,
1247                                   (dsize = PATH_MAX, &dsize)))
1248              != ERROR_NO_MORE_ITEMS; ++index)
1249           if (ret == ERROR_SUCCESS && dsize > 5)
1250             {
1251               char *path = data + 4;
1252               if (path[1] != ':')
1253                 *(path += 2) = '\\';
1254               if (what == PRINT_KEY)
1255                 printf ("  %s Key: %s Path: %s", i ? "User:  " : "System:",
1256                         name, path);
1257               strcat (path, "\\bin\\cygwin1.dll");
1258               if (what == PRINT_KEY)
1259                 printf ("%s\n", access (path, F_OK) ? " (ORPHANED)" : "");
1260               else if (access (path, F_OK))
1261                 {
1262                   RegDeleteValue (key, name);
1263                   /* Start over since index is not reliable anymore. */
1264                   --i;
1265                   break;
1266                 }
1267             }
1268         RegCloseKey (key);
1269       }
1270   if (what == PRINT_KEY)
1271     printf ("\n");
1272 }
1273
1274 void
1275 print_reg_installations ()
1276 {
1277   handle_reg_installation (PRINT_KEY);
1278 }
1279
1280 void
1281 del_orphaned_reg_installations ()
1282 {
1283   handle_reg_installation (DELETE_KEY);
1284 }
1285
1286 /* Unfortunately neither mingw nor Windows know this function. */
1287 char *
1288 memmem (char *haystack, size_t haystacklen,
1289         const char *needle, size_t needlelen)
1290 {
1291   if (needlelen == 0)
1292     return haystack;
1293   while (needlelen <= haystacklen)
1294     {
1295       if (!memcmp (haystack, needle, needlelen))
1296         return haystack;
1297       haystack++;
1298       haystacklen--;
1299     }
1300   return NULL;
1301 }
1302  
1303 int
1304 handle_unique_object_name (int opt, char *path)
1305 {
1306   HANDLE fh, fm;
1307   void *haystack = NULL;
1308
1309   if (!path || !*path)
1310     usage (stderr, 1);
1311
1312   DWORD access, share, protect, mapping;
1313
1314   if (opt == CO_SHOW_UON)
1315     {
1316       access = GENERIC_READ;
1317       share = FILE_SHARE_VALID_FLAGS;
1318       protect = PAGE_READONLY;
1319       mapping = FILE_MAP_READ;
1320     }
1321   else
1322     {
1323       access = GENERIC_READ | GENERIC_WRITE;
1324       share = 0;
1325       protect = PAGE_READWRITE;
1326       mapping = FILE_MAP_WRITE;
1327     }
1328
1329   fh = CreateFile (path, access, share, NULL, OPEN_EXISTING,
1330                    FILE_FLAG_BACKUP_SEMANTICS, NULL);
1331   if (fh == INVALID_HANDLE_VALUE)
1332     {
1333       DWORD err = GetLastError ();
1334       switch (err)
1335         {
1336         case ERROR_SHARING_VIOLATION:
1337           display_error ("%s still used by other Cygwin processes.\n"
1338                          "Please stop all of them and retry.", path);
1339           break;
1340         case ERROR_ACCESS_DENIED:
1341           display_error (
1342             "Your permissions are not sufficient to change the file \"%s\"",
1343             path);
1344           break;
1345         case ERROR_FILE_NOT_FOUND:
1346           display_error ("%s: No such file.", path);
1347           break;
1348         default:
1349           display_error (path, true, false);
1350           break;
1351         }
1352       return 1;
1353     }
1354   if (!(fm = CreateFileMapping (fh, NULL, protect, 0, 0, NULL)))
1355     display_error ("CreateFileMapping");
1356   else if (!(haystack = MapViewOfFile (fm, mapping, 0, 0, 0)))
1357     display_error ("MapViewOfFile");
1358   else
1359     {
1360       size_t haystacklen = GetFileSize (fh, NULL);
1361       cygwin_props_t *cygwin_props = (cygwin_props_t *) 
1362                memmem ((char *) haystack, haystacklen,
1363                        CYGWIN_PROPS_MAGIC, sizeof (CYGWIN_PROPS_MAGIC));
1364       if (!cygwin_props)
1365         display_error ("Can't find Cygwin properties in %s", path);
1366       else
1367         {
1368           if (opt != CO_SHOW_UON)
1369             cygwin_props->disable_key = opt - CO_ENABLE_UON;
1370           printf ("Unique object names are %s\n",
1371                   cygwin_props->disable_key ? "disabled" : "enabled");
1372           UnmapViewOfFile (haystack);
1373           CloseHandle (fm);
1374           CloseHandle (fh);
1375           return 0;
1376         }
1377     }
1378   if (haystack)
1379     UnmapViewOfFile (haystack);
1380   if (fm)
1381     CloseHandle (fm);
1382   CloseHandle (fh);
1383   return 1;
1384 }
1385
1386 static void
1387 dump_sysinfo ()
1388 {
1389   int i, j;
1390   char tmp[4000];
1391   time_t now;
1392   char *found_cygwin_dll;
1393   bool is_nt = false;
1394   bool more_info = true;
1395   char osname[128];
1396   DWORD obcaseinsensitive = 1;
1397   HKEY key;
1398
1399   printf ("\nCygwin Configuration Diagnostics\n");
1400   time (&now);
1401   printf ("Current System Time: %s\n", ctime (&now));
1402
1403   OSVERSIONINFOEX osversion;
1404   osversion.dwOSVersionInfoSize = sizeof (OSVERSIONINFOEX);
1405   if (!GetVersionEx (reinterpret_cast<LPOSVERSIONINFO>(&osversion)))
1406     {
1407       more_info = false;
1408       osversion.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
1409       if (!GetVersionEx (reinterpret_cast<LPOSVERSIONINFO>(&osversion)))
1410         display_error ("dump_sysinfo: GetVersionEx()");
1411     }
1412
1413   HMODULE k32 = GetModuleHandleW (L"kernel32.dll");
1414
1415   switch (osversion.dwPlatformId)
1416     {
1417     case VER_PLATFORM_WIN32s:
1418       strcpy (osname, "32s (not supported)");
1419       break;
1420     case VER_PLATFORM_WIN32_WINDOWS:
1421       strcpy (osname, "95/98/Me (not supported)");
1422       break;
1423     case VER_PLATFORM_WIN32_NT:
1424       is_nt = true;
1425       if (osversion.dwMajorVersion == 6)
1426         {
1427           BOOL (WINAPI *GetProductInfo) (DWORD, DWORD, DWORD, DWORD, PDWORD) =
1428                   (BOOL (WINAPI *)(DWORD, DWORD, DWORD, DWORD, PDWORD))
1429                   GetProcAddress (k32, "GetProductInfo");
1430           if (osversion.dwMinorVersion == 0)
1431             strcpy (osname, osversion.wProductType == VER_NT_WORKSTATION
1432                             ? "Vista" : "2008");
1433           else if (osversion.dwMinorVersion == 1)
1434             strcpy (osname, osversion.wProductType == VER_NT_WORKSTATION
1435                             ? "7" : "2008 R2");
1436           DWORD prod;
1437           if (GetProductInfo (osversion.dwMajorVersion,
1438                               osversion.dwMinorVersion,
1439                               osversion.wServicePackMajor,
1440                               osversion.wServicePackMinor,
1441                               &prod))
1442             {
1443 #define       PRODUCT_UNLICENSED 0xabcdabcd
1444 #define       PRODUCT_ULTIMATE_E 0x00000047
1445               const char *products[] =
1446                 {
1447  /* 0x00000000 */ "",
1448  /* 0x00000001 */ " Ultimate",
1449  /* 0x00000002 */ " Home Basic",
1450  /* 0x00000003 */ " Home Premium",
1451  /* 0x00000004 */ " Enterprise",
1452  /* 0x00000005 */ " Home Basic N",
1453  /* 0x00000006 */ " Business",
1454  /* 0x00000007 */ " Server Standard",
1455  /* 0x00000008 */ " Server Datacenter",
1456  /* 0x00000009 */ " Small Business Server",
1457  /* 0x0000000a */ " Server Enterprise",
1458  /* 0x0000000b */ " Starter",
1459  /* 0x0000000c */ " Server Datacenter Core",
1460  /* 0x0000000d */ " Server Standard Core",
1461  /* 0x0000000e */ " Server Enterprise Core",
1462  /* 0x0000000f */ " Server Enterprise for Itanium-based Systems",
1463  /* 0x00000000 */ " Business N",
1464  /* 0x00000011 */ " Web Server",
1465  /* 0x00000012 */ " HPC Edition",
1466  /* 0x00000013 */ " Home Server",
1467  /* 0x00000014 */ " Storage Server Express",
1468  /* 0x00000015 */ " Storage Server Standard",
1469  /* 0x00000016 */ " Storage Server Workgroup",
1470  /* 0x00000017 */ " Storage Server Enterprise",
1471  /* 0x00000018 */ " for Windows Essential Server Solutions",
1472  /* 0x00000019 */ "",
1473  /* 0x0000001a */ " Home Premium N",
1474  /* 0x0000001b */ " Enterprise N",
1475  /* 0x0000001c */ " Ultimate N",
1476  /* 0x0000001d */ " Web Server Core",
1477  /* 0x0000001e */ " Essential Business Server Management Server",
1478  /* 0x0000001f */ " Essential Business Server Security Server"
1479  /* 0x00000020 */ " Essential Business Server Messaging Server",
1480  /* 0x00000021 */ " Server Foundation",
1481  /* 0x00000022 */ "",
1482  /* 0x00000023 */ " without Hyper-V for Windows Essential Server Solutions",
1483  /* 0x00000024 */ " Server Standard without Hyper-V",
1484  /* 0x00000025 */ " Server Datacenter without Hyper-V",
1485  /* 0x00000026 */ " Server Enterprise without Hyper-V",
1486  /* 0x00000027 */ " Server Datacenter Core without Hyper-V",
1487  /* 0x00000028 */ " Server Standard Core without Hyper-V",
1488  /* 0x00000029 */ " Server Enterprise Core without Hyper-V",
1489  /* 0x0000002a */ " Hyper-V Server",
1490  /* 0x0000002b */ "",
1491  /* 0x0000002c */ "",
1492  /* 0x0000002d */ "",
1493  /* 0x0000002e */ "",
1494  /* 0x0000002f */ " Starter N",
1495  /* 0x00000030 */ " Professional",
1496  /* 0x00000031 */ " Professional N",
1497  /* 0x00000032 */ "",
1498  /* 0x00000033 */ "",
1499  /* 0x00000034 */ "",
1500  /* 0x00000035 */ "",
1501  /* 0x00000036 */ "",
1502  /* 0x00000037 */ "",
1503  /* 0x00000038 */ "",
1504  /* 0x00000039 */ "",
1505  /* 0x0000003a */ "",
1506  /* 0x0000003b */ "",
1507  /* 0x0000003c */ "",
1508  /* 0x0000003d */ "",
1509  /* 0x0000003e */ "",
1510  /* 0x0000003f */ "",
1511  /* 0x00000040 */ "",
1512  /* 0x00000041 */ "",
1513  /* 0x00000042 */ " Starter E",
1514  /* 0x00000043 */ " Home Basic E",
1515  /* 0x00000044 */ " Home Premium E",
1516  /* 0x00000045 */ " Professional E",
1517  /* 0x00000046 */ " Enterprise E",
1518  /* 0x00000047 */ " Ultimate E"
1519                 };
1520               if (prod == PRODUCT_UNLICENSED)
1521                 strcat (osname, "Unlicensed");
1522               else if (prod > PRODUCT_ULTIMATE_E)
1523                 strcat (osname, "");
1524               else
1525                 strcat (osname, products[prod]);
1526             }
1527           else
1528             {
1529             }
1530         }
1531       else if (osversion.dwMajorVersion == 5)
1532         {
1533           if (osversion.dwMinorVersion == 0)
1534             {
1535               strcpy (osname, "2000");
1536               if (osversion.wProductType == VER_NT_WORKSTATION)
1537                 strcat (osname, " Professional");
1538               else if (osversion.wSuiteMask & VER_SUITE_DATACENTER)
1539                 strcat (osname, " Datacenter Server");
1540               else if (osversion.wSuiteMask & VER_SUITE_ENTERPRISE)
1541                 strcat (osname, " Advanced Server");
1542               else
1543                 strcat (osname, " Server");
1544             }
1545           else if (osversion.dwMinorVersion == 1)
1546             {
1547               strcpy (osname, "XP");
1548               if (GetSystemMetrics (SM_MEDIACENTER))
1549                 strcat (osname, " Media Center Edition");
1550               else if (GetSystemMetrics (SM_TABLETPC))
1551                 strcat (osname, " Tablet PC Edition");
1552               else if (GetSystemMetrics (SM_STARTER))
1553                 strcat (osname, " Starter Edition");
1554               else if (osversion.wSuiteMask & VER_SUITE_PERSONAL)
1555                 strcat (osname, " Home Edition");
1556               else
1557                 strcat (osname, " Professional");
1558             }
1559           else if (osversion.dwMinorVersion == 2)
1560             {
1561               strcpy (osname, "2003 Server");
1562               if (GetSystemMetrics (SM_SERVERR2))
1563                 strcat (osname, " R2");
1564               if (osversion.wSuiteMask & VER_SUITE_BLADE)
1565                 strcat (osname, " Web Edition");
1566               else if (osversion.wSuiteMask & VER_SUITE_DATACENTER)
1567                 strcat (osname, " Datacenter Edition");
1568               else if (osversion.wSuiteMask & VER_SUITE_ENTERPRISE)
1569                 strcat (osname, " Enterprise Edition");
1570               else if (osversion.wSuiteMask & VER_SUITE_COMPUTE_SERVER)
1571                 strcat (osname, " Compute Cluster Edition");
1572             }
1573         }
1574       else if (osversion.dwMajorVersion == 4)
1575         {
1576           strcpy (osname, "NT 4");
1577           if (more_info)
1578             {
1579               if (osversion.wProductType == VER_NT_WORKSTATION)
1580                 strcat (osname, " Workstation");
1581               else
1582                 {
1583                   strcat (osname, " Server");
1584                   if (osversion.wSuiteMask & VER_SUITE_ENTERPRISE)
1585                     strcat (osname, " Enterprise Edition");
1586                 }
1587             }
1588         }
1589       else
1590         strcpy (osname, "NT");
1591       break;
1592     default:
1593       strcpy (osname, "??");
1594       break;
1595     }
1596   printf ("Windows %s Ver %lu.%lu Build %lu %s\n", osname,
1597           osversion.dwMajorVersion, osversion.dwMinorVersion,
1598           osversion.dwPlatformId == VER_PLATFORM_WIN32_NT ?
1599           osversion.dwBuildNumber : (osversion.dwBuildNumber & 0xffff),
1600           osversion.dwPlatformId == VER_PLATFORM_WIN32_NT ?
1601           osversion.szCSDVersion : "");
1602
1603   if (osversion.dwPlatformId == VER_PLATFORM_WIN32s
1604       || osversion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS)
1605     exit (EXIT_FAILURE);
1606
1607   BOOL (WINAPI *wow64_func) (HANDLE, PBOOL) = (BOOL (WINAPI *) (HANDLE, PBOOL))
1608     GetProcAddress (k32, "IsWow64Process");
1609   BOOL is_wow64 = FALSE;
1610   if (wow64_func && wow64_func (GetCurrentProcess (), &is_wow64) && is_wow64)
1611     {
1612       void (WINAPI *nativinfo) (LPSYSTEM_INFO) = (void (WINAPI *)
1613         (LPSYSTEM_INFO)) GetProcAddress (k32, "GetNativeSystemInfo");
1614       SYSTEM_INFO natinfo;
1615       nativinfo (&natinfo);
1616       fputs ("\nRunning under WOW64 on ", stdout);
1617       switch (natinfo.wProcessorArchitecture)
1618         {
1619           case PROCESSOR_ARCHITECTURE_IA64:
1620             puts ("IA64");
1621             break;
1622           case PROCESSOR_ARCHITECTURE_AMD64:
1623             puts ("AMD64");
1624             break;
1625           default:
1626             puts("??");
1627             break;
1628         }
1629     }
1630
1631   if (GetSystemMetrics (SM_REMOTESESSION))
1632     printf ("\nRunning in Terminal Service session\n");
1633
1634   printf ("\nPath:");
1635   char *s = getenv ("PATH"), *e;
1636   if (!s)
1637     puts ("");
1638   else
1639     {
1640       char sep = strchr (s, ';') ? ';' : ':';
1641       int count_path_items = 0;
1642       while (1)
1643         {
1644           for (e = s; *e && *e != sep; e++);
1645           if (e-s)
1646             printf ("\t%.*s\n", e - s, s);
1647           else
1648             puts ("\t.");
1649           count_path_items++;
1650           if (!*e)
1651             break;
1652           s = e + 1;
1653         }
1654     }
1655
1656   fflush (stdout);
1657
1658   pretty_id ();
1659
1660   if (!GetSystemDirectory (tmp, 4000))
1661     display_error ("dump_sysinfo: GetSystemDirectory()");
1662   printf ("\nSysDir: %s\n", tmp);
1663
1664   GetWindowsDirectory (tmp, 4000);
1665   printf ("WinDir: %s\n\n", tmp);
1666
1667
1668   if (givehelp)
1669     printf ("Here's some environment variables that may affect cygwin:\n");
1670   for (i = 0; environ[i]; i++)
1671     {
1672       char *eq = strchr (environ[i], '=');
1673       if (!eq)
1674         continue;
1675       /* int len = eq - environ[i]; */
1676       for (j = 0; known_env_vars[j]; j++)
1677         {
1678           *eq = 0;
1679           if (strcmp (environ[i], "PATH") == 0)
1680             continue;           /* we handle this one specially */
1681           if (strcasecmp (environ[i], known_env_vars[j]) == 0)
1682             printf ("%s = '%s'\n", environ[i], eq + 1);
1683           *eq = '=';
1684         }
1685     }
1686   printf ("\n");
1687
1688   if (verbose)
1689     {
1690       if (givehelp)
1691         printf ("Here's the rest of your environment variables:\n");
1692       for (i = 0; environ[i]; i++)
1693         {
1694           int found = 0;
1695           char *eq = strchr (environ[i], '=');
1696           if (!eq)
1697             continue;
1698           /* int len = eq - environ[i]; */
1699           for (j = 0; known_env_vars[j]; j++)
1700             {
1701               *eq = 0;
1702               if (strcasecmp (environ[i], known_env_vars[j]) == 0)
1703                 found = 1;
1704               *eq = '=';
1705             }
1706           if (!found)
1707             {
1708               *eq = 0;
1709               printf ("%s = '%s'\n", environ[i], eq + 1);
1710               *eq = '=';
1711             }
1712         }
1713       printf ("\n");
1714     }
1715
1716   if (registry)
1717     {
1718       if (givehelp)
1719         printf ("Scanning registry for keys with 'Cygwin' in them...\n");
1720       scan_registry (0, HKEY_CURRENT_USER,
1721                      (char *) "HKEY_CURRENT_USER", 0, false);
1722       scan_registry (0, HKEY_LOCAL_MACHINE,
1723                      (char *) "HKEY_LOCAL_MACHINE", 0, false);
1724       printf ("\n");
1725     }
1726   else
1727     printf ("Use '-r' to scan registry\n\n");
1728
1729   if (RegOpenKeyEx (HKEY_LOCAL_MACHINE,
1730                   "SYSTEM\\CurrentControlSet\\Control\\Session Manager\\kernel",
1731                   0, KEY_READ, &key) == ERROR_SUCCESS)
1732     {
1733       DWORD size;
1734       RegQueryValueEx (key, "obcaseinsensitive", NULL, NULL,
1735                        (LPBYTE) &obcaseinsensitive, &size);
1736       RegCloseKey (key);
1737     }
1738   printf ("obcaseinsensitive set to %lu\n\n", obcaseinsensitive);
1739
1740   print_reg_installations ();
1741
1742   if (givehelp)
1743     {
1744       printf ("Listing available drives...\n");
1745       printf ("Drv Type          Size   Used Flags              Name\n");
1746     }
1747   int prev_mode =
1748     SetErrorMode (SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
1749   int drivemask = GetLogicalDrives ();
1750
1751   BOOL (WINAPI * gdfse) (LPCSTR, long long *, long long *, long long *) =
1752     (BOOL (WINAPI *) (LPCSTR, long long *, long long *, long long *))
1753     GetProcAddress (k32, "GetDiskFreeSpaceExA");
1754
1755   for (i = 0; i < 26; i++)
1756     {
1757       if (!(drivemask & (1 << i)))
1758         continue;
1759       char drive[4], name[200], fsname[200];
1760       DWORD serno = 0, maxnamelen = 0, flags = 0;
1761       name[0] = fsname[0] = 0;
1762       sprintf (drive, "%c:\\", i + 'a');
1763       /* Report all errors, except if the Volume is ERROR_NOT_READY.
1764          ERROR_NOT_READY is returned when removeable media drives are empty
1765          (CD, floppy, etc.) */
1766       if (!GetVolumeInformation (drive, name, sizeof (name), &serno,
1767                                  &maxnamelen, &flags, fsname,
1768                                  sizeof (fsname))
1769           && GetLastError () != ERROR_NOT_READY)
1770         {
1771 #         define FMT "dump_sysinfo: GetVolumeInformation() for drive %c:"
1772           char buf[sizeof (FMT)];
1773           sprintf (buf, FMT, 'A' + i);
1774           display_error (buf);
1775 #         undef FMT
1776         }
1777
1778       int dtype = GetDriveType (drive);
1779       char drive_type[4] = "unk";
1780       switch (dtype)
1781         {
1782         case DRIVE_REMOVABLE:
1783           strcpy (drive_type, "fd ");
1784           break;
1785         case DRIVE_FIXED:
1786           strcpy (drive_type, "hd ");
1787           break;
1788         case DRIVE_REMOTE:
1789           strcpy (drive_type, "net");
1790           break;
1791         case DRIVE_CDROM:
1792           strcpy (drive_type, "cd ");
1793           break;
1794         case DRIVE_RAMDISK:
1795           strcpy (drive_type, "ram");
1796           break;
1797         default:
1798           strcpy (drive_type, "unk");
1799         }
1800
1801       long capacity_mb = -1;
1802       int percent_full = -1;
1803
1804       long long free_me = 0ULL, free_bytes = 0ULL, total_bytes = 1ULL;
1805       if (gdfse != NULL && gdfse (drive, &free_me, &total_bytes, &free_bytes))
1806         {
1807           capacity_mb = total_bytes / (1024L * 1024L);
1808           percent_full = 100 - (int) ((100.0 * free_me) / total_bytes);
1809         }
1810       else
1811         {
1812           DWORD spc = 0, bps = 0, fc = 0, tc = 1;
1813           if (GetDiskFreeSpace (drive, &spc, &bps, &fc, &tc))
1814             {
1815               capacity_mb = (spc * bps * tc) / (1024 * 1024);
1816               percent_full = 100 - (int) ((100.0 * fc) / tc);
1817             }
1818         }
1819
1820       printf ("%.2s  %s %-6s ", drive, drive_type, fsname);
1821       if (capacity_mb >= 0)
1822         printf ("%7dMb %3d%% ", (int) capacity_mb, (int) percent_full);
1823       else
1824         printf ("    N/A    N/A ");
1825       printf ("%s %s %s %s %s %s  %s\n",
1826               flags & FS_CASE_IS_PRESERVED ? "CP" : "  ",
1827               flags & FS_CASE_SENSITIVE ? "CS" : "  ",
1828               flags & FS_UNICODE_STORED_ON_DISK ? "UN" : "  ",
1829               flags & FS_PERSISTENT_ACLS ? "PA" : "  ",
1830               flags & FS_FILE_COMPRESSION ? "FC" : "  ",
1831               flags & FS_VOL_IS_COMPRESSED ? "VC" : "  ",
1832 #if 0
1833               flags & FILE_SUPPORTS_ENCRYPTION ? "EN" : "  ",
1834               flags & FILE_SUPPORTS_OBJECT_IDS ? "OI" : "  ",
1835               flags & FILE_SUPPORTS_REPARSE_POINTS ? "RP" : "  ",
1836               flags & FILE_SUPPORTS_SPARSE_FILES ? "SP" : "  ",
1837               flags & FILE_VOLUME_QUOTAS ? "QU" : "  ",
1838 #endif
1839               name);
1840     }
1841
1842   SetErrorMode (prev_mode);
1843   if (givehelp)
1844     {
1845       puts ("\n"
1846           "fd = floppy,          hd = hard drive,       cd = CD-ROM\n"
1847           "net= Network Share,   ram= RAM drive,        unk= Unknown\n"
1848           "CP = Case Preserving, CS = Case Sensitive,   UN = Unicode\n"
1849           "PA = Persistent ACLS, FC = File Compression, VC = Volume Compression");
1850     }
1851   printf ("\n");
1852
1853   unsigned ml_fsname = 4, ml_dir = 7, ml_type = 6;
1854   bool ml_trailing = false;
1855
1856   struct mntent *mnt;
1857   setmntent (0, 0);
1858   while ((mnt = getmntent (0)))
1859     {
1860       unsigned n = (int) strlen (mnt->mnt_fsname);
1861       ml_trailing |= (n > 1 && strchr ("\\/", mnt->mnt_fsname[n - 1]));
1862       if (ml_fsname < n)
1863         ml_fsname = n;
1864       n = (int) strlen (mnt->mnt_dir);
1865       ml_trailing |= (n > 1 && strchr ("\\/", mnt->mnt_dir[n - 1]));
1866       if (ml_dir < n)
1867         ml_dir = n;
1868     }
1869
1870   if (ml_trailing)
1871     puts ("Warning: Mount entries should not have a trailing (back)slash\n");
1872
1873   if (givehelp)
1874     {
1875       printf
1876         ("Mount entries: these map POSIX directories to your NT drives.\n");
1877       printf ("%-*s  %-*s  %-*s  %s\n", ml_fsname, "-NT-", ml_dir, "-POSIX-",
1878               ml_type, "-Type-", "-Flags-");
1879     }
1880
1881   setmntent (0, 0);
1882   while ((mnt = getmntent (0)))
1883     {
1884       printf ("%-*s  %-*s  %-*s  %s\n",
1885               ml_fsname, mnt->mnt_fsname,
1886               ml_dir, mnt->mnt_dir, ml_type, mnt->mnt_type, mnt->mnt_opts);
1887     }
1888   printf ("\n");
1889
1890   if (givehelp)
1891     printf
1892       ("Looking to see where common programs can be found, if at all...\n");
1893   for (i = 0; common_apps[i].name; i++)
1894     if (!find_app_on_path ((char *) common_apps[i].name, 1))
1895       {
1896         if (common_apps[i].missing_is_good)
1897           printf ("Not Found: %s (good!)\n", common_apps[i].name);
1898         else
1899           printf ("Not Found: %s\n", common_apps[i].name);
1900       }
1901   printf ("\n");
1902
1903   if (givehelp)
1904     printf ("Looking for various Cygwin DLLs...  (-v gives version info)\n");
1905   int cygwin_dll_count = 0;
1906   char cygdll_path[32768];
1907   for (pathlike *pth = paths; pth->dir; pth++)
1908     {
1909       WIN32_FIND_DATAW ffinfo;
1910       sprintf (tmp, "%s*.*", pth->dir);
1911       wide_path wpath (tmp);
1912       HANDLE ff = FindFirstFileW (wpath, &ffinfo);
1913       int found = (ff != INVALID_HANDLE_VALUE);
1914       found_cygwin_dll = NULL;
1915       while (found)
1916         {
1917           char f[FILENAME_MAX + 1];
1918           wcstombs (f, ffinfo.cFileName, sizeof f);
1919           if (strcasecmp (f + strlen (f) - 4, ".dll") == 0)
1920             {
1921               if (strncasecmp (f, "cyg", 3) == 0)
1922                 {
1923                   sprintf (tmp, "%s%s", pth->dir, f);
1924                   if (strcasecmp (f, "cygwin1.dll") == 0)
1925                     {
1926                       if (!cygwin_dll_count)
1927                         strcpy (cygdll_path, pth->dir);
1928                       if (!cygwin_dll_count
1929                           || strcasecmp (cygdll_path, pth->dir) != 0)
1930                         cygwin_dll_count++;
1931                       found_cygwin_dll = strdup (tmp);
1932                     }
1933                   else
1934                     ls (tmp);
1935                 }
1936             }
1937           found = FindNextFileW (ff, &ffinfo);
1938         }
1939       if (found_cygwin_dll)
1940         {
1941           ls (found_cygwin_dll);
1942           free (found_cygwin_dll);
1943         }
1944
1945       FindClose (ff);
1946     }
1947   if (cygwin_dll_count > 1)
1948     puts ("Warning: There are multiple cygwin1.dlls on your path");
1949   if (!cygwin_dll_count)
1950     puts ("Warning: cygwin1.dll not found on your path");
1951
1952   dump_dodgy_apps (verbose);
1953
1954   if (is_nt)
1955     dump_sysinfo_services ();
1956 }
1957
1958 static int
1959 check_keys ()
1960 {
1961   HANDLE h = CreateFileW (L"CONIN$", GENERIC_READ | GENERIC_WRITE,
1962                           FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
1963                           OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
1964
1965   if (h == INVALID_HANDLE_VALUE || h == NULL)
1966     return (display_error ("check_keys: Opening CONIN$"));
1967
1968   DWORD mode;
1969
1970   if (!GetConsoleMode (h, &mode))
1971     display_error ("check_keys: GetConsoleMode()");
1972   else
1973     {
1974       mode &= ~ENABLE_PROCESSED_INPUT;
1975       if (!SetConsoleMode (h, mode))
1976         display_error ("check_keys: SetConsoleMode()");
1977     }
1978
1979   fputs ("\nThis key check works only in a console window,", stderr);
1980   fputs (" _NOT_ in a terminal session!\n", stderr);
1981   fputs ("Abort with Ctrl+C if in a terminal session.\n\n", stderr);
1982   fputs ("Press 'q' to exit.\n", stderr);
1983
1984   INPUT_RECORD in, prev_in;
1985
1986   // Drop first <RETURN> key
1987   ReadConsoleInputW (h, &in, 1, &mode);
1988
1989   memset (&in, 0, sizeof in);
1990
1991   do
1992     {
1993       prev_in = in;
1994       if (!ReadConsoleInputW (h, &in, 1, &mode))
1995         display_error ("check_keys: ReadConsoleInput()");
1996
1997       if (!memcmp (&in, &prev_in, sizeof in))
1998         continue;
1999
2000       switch (in.EventType)
2001         {
2002         case KEY_EVENT:
2003           printf ("%s %ux VK: 0x%04x VS: 0x%04x C: 0x%04x CTRL: ",
2004                   in.Event.KeyEvent.bKeyDown ? "Pressed " : "Released",
2005                   in.Event.KeyEvent.wRepeatCount,
2006                   in.Event.KeyEvent.wVirtualKeyCode,
2007                   in.Event.KeyEvent.wVirtualScanCode,
2008                   (unsigned char) in.Event.KeyEvent.uChar.UnicodeChar);
2009           fputs (in.Event.KeyEvent.dwControlKeyState & CAPSLOCK_ON ?
2010                  "CL " : "-- ", stdout);
2011           fputs (in.Event.KeyEvent.dwControlKeyState & ENHANCED_KEY ?
2012                  "EK " : "-- ", stdout);
2013           fputs (in.Event.KeyEvent.dwControlKeyState & LEFT_ALT_PRESSED ?
2014                  "LA " : "-- ", stdout);
2015           fputs (in.Event.KeyEvent.dwControlKeyState & LEFT_CTRL_PRESSED ?
2016                  "LC " : "-- ", stdout);
2017           fputs (in.Event.KeyEvent.dwControlKeyState & NUMLOCK_ON ?
2018                  "NL " : "-- ", stdout);
2019           fputs (in.Event.KeyEvent.dwControlKeyState & RIGHT_ALT_PRESSED ?
2020                  "RA " : "-- ", stdout);
2021           fputs (in.Event.KeyEvent.dwControlKeyState & RIGHT_CTRL_PRESSED ?
2022                  "RC " : "-- ", stdout);
2023           fputs (in.Event.KeyEvent.dwControlKeyState & SCROLLLOCK_ON ?
2024                  "SL " : "-- ", stdout);
2025           fputs (in.Event.KeyEvent.dwControlKeyState & SHIFT_PRESSED ?
2026                  "SH " : "-- ", stdout);
2027           fputc ('\n', stdout);
2028           break;
2029
2030         default:
2031           break;
2032         }
2033     }
2034   while (in.EventType != KEY_EVENT ||
2035          in.Event.KeyEvent.bKeyDown != FALSE ||
2036          in.Event.KeyEvent.uChar.UnicodeChar != L'q');
2037
2038   CloseHandle (h);
2039   return 0;
2040 }
2041
2042 /* RFC1738 says that these do not need to be escaped.  */
2043 static const char safe_chars[] = "$-_.+!*'(),";
2044
2045 /* the URL to query.  */
2046 static const char base_url[] =
2047         "http://cygwin.com/cgi-bin2/package-grep.cgi?text=1&grep=";
2048
2049 /* Queries Cygwin web site for packages containing files matching a regexp.
2050    Return value is 1 if there was a problem, otherwise 0.  */
2051 static int
2052 package_grep (char *search)
2053 {
2054   char buf[1024];
2055
2056   /* Attempt to dynamically load the necessary WinInet API functions so that
2057      cygcheck can still function on older systems without IE.  */
2058   HMODULE hWinInet;
2059   if (!(hWinInet = LoadLibrary ("wininet.dll")))
2060     {
2061       fputs ("Unable to locate WININET.DLL.  This feature requires Microsoft "
2062              "Internet Explorer v3 or later to function.\n", stderr);
2063       return 1;
2064     }
2065
2066   /* InternetCloseHandle is used outside this function so it is declared
2067      global.  The rest of these functions are only used here, so declare them
2068      and call GetProcAddress for each of them with the following macro.  */
2069
2070   pInternetCloseHandle = (BOOL (WINAPI *) (HINTERNET))
2071                             GetProcAddress (hWinInet, "InternetCloseHandle");
2072 #define make_func_pointer(name, ret, args) ret (WINAPI * p##name) args = \
2073             (ret (WINAPI *) args) GetProcAddress (hWinInet, #name);
2074   make_func_pointer (InternetAttemptConnect, DWORD, (DWORD));
2075   make_func_pointer (InternetOpenA, HINTERNET, (LPCSTR, DWORD, LPCSTR, LPCSTR,
2076                                                 DWORD));
2077   make_func_pointer (InternetOpenUrlA, HINTERNET, (HINTERNET, LPCSTR, LPCSTR,
2078                                                    DWORD, DWORD, DWORD));
2079   make_func_pointer (InternetReadFile, BOOL, (HINTERNET, PVOID, DWORD, PDWORD));
2080   make_func_pointer (HttpQueryInfoA, BOOL, (HINTERNET, DWORD, PVOID, PDWORD,
2081                                             PDWORD));
2082 #undef make_func_pointer
2083
2084   if(!pInternetCloseHandle || !pInternetAttemptConnect || !pInternetOpenA
2085      || !pInternetOpenUrlA || !pInternetReadFile || !pHttpQueryInfoA)
2086     {
2087       fputs ("Unable to load one or more functions from WININET.DLL.  This "
2088              "feature requires Microsoft Internet Explorer v3 or later to "
2089              "function.\n", stderr);
2090       return 1;
2091     }
2092
2093   /* construct the actual URL by escaping  */
2094   char *url = (char *) alloca (sizeof (base_url) + strlen (search) * 3);
2095   strcpy (url, base_url);
2096
2097   char *dest;
2098   for (dest = &url[sizeof (base_url) - 1]; *search; search++)
2099     {
2100       if (isalnum (*search)
2101           || memchr (safe_chars, *search, sizeof (safe_chars) - 1))
2102         {
2103           *dest++ = *search;
2104         }
2105       else
2106         {
2107           *dest++ = '%';
2108           sprintf (dest, "%02x", (unsigned char) *search);
2109           dest += 2;
2110         }
2111     }
2112   *dest = 0;
2113
2114   /* Connect to the net and open the URL.  */
2115   if (pInternetAttemptConnect (0) != ERROR_SUCCESS)
2116     {
2117       fputs ("An internet connection is required for this function.\n", stderr);
2118       return 1;
2119     }
2120
2121   /* Initialize WinInet and attempt to fetch our URL.  */
2122   HINTERNET hi = NULL, hurl = NULL;
2123   if (!(hi = pInternetOpenA ("cygcheck", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0)))
2124     return display_internet_error ("InternetOpen() failed", NULL);
2125
2126   if (!(hurl = pInternetOpenUrlA (hi, url, NULL, 0, 0, 0)))
2127     return display_internet_error ("unable to contact cygwin.com site, "
2128                                    "InternetOpenUrl() failed", hi, NULL);
2129
2130   /* Check the HTTP response code.  */
2131   DWORD rc = 0, rc_s = sizeof (DWORD);
2132   if (!pHttpQueryInfoA (hurl, HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER,
2133                       (void *) &rc, &rc_s, NULL))
2134     return display_internet_error ("HttpQueryInfo() failed", hurl, hi, NULL);
2135
2136   if (rc != HTTP_STATUS_OK)
2137     {
2138       sprintf (buf, "error retrieving results from cygwin.com site, "
2139                     "HTTP status code %lu", rc);
2140       return display_internet_error (buf, hurl, hi, NULL);
2141     }
2142
2143   /* Fetch result and print to stdout.  */
2144   DWORD numread;
2145   do
2146     {
2147       if (!pInternetReadFile (hurl, (void *) buf, sizeof (buf), &numread))
2148         return display_internet_error ("InternetReadFile failed", hurl, hi, NULL);
2149       if (numread)
2150         fwrite ((void *) buf, (size_t) numread, 1, stdout);
2151     }
2152   while (numread);
2153
2154   pInternetCloseHandle (hurl);
2155   pInternetCloseHandle (hi);
2156   return 0;
2157 }
2158
2159 static void
2160 usage (FILE * stream, int status)
2161 {
2162   fprintf (stream, "\
2163 Usage: cygcheck [-v] [-h] PROGRAM\n\
2164        cygcheck -c [-d] [PACKAGE]\n\
2165        cygcheck -s [-r] [-v] [-h]\n\
2166        cygcheck -k\n\
2167        cygcheck -f FILE [FILE]...\n\
2168        cygcheck -l [PACKAGE]...\n\
2169        cygcheck -p REGEXP\n\
2170        cygcheck --delete-orphaned-installation-keys\n\
2171        cygcheck --enable-unique-object-names Cygwin-DLL\n\
2172        cygcheck --disable-unique-object-names Cygwin-DLL\n\
2173        cygcheck --show-unique-object-names Cygwin-DLL\n\
2174        cygcheck -h\n\n\
2175 List system information, check installed packages, or query package database.\n\
2176 \n\
2177 At least one command option or a PROGRAM is required, as shown above.\n\
2178 \n\
2179   PROGRAM              list library (DLL) dependencies of PROGRAM\n\
2180   -c, --check-setup    show installed version of PACKAGE and verify integrity\n\
2181                        (or for all installed packages if none specified)\n\
2182   -d, --dump-only      just list packages, do not verify (with -c)\n\
2183   -s, --sysinfo        produce diagnostic system information (implies -c)\n\
2184   -r, --registry       also scan registry for Cygwin settings (with -s)\n\
2185   -k, --keycheck       perform a keyboard check session (must be run from a\n\
2186                        plain console only, not from a pty/rxvt/xterm)\n\
2187   -f, --find-package   find the package to which FILE belongs\n\
2188   -l, --list-package   list contents of PACKAGE (or all packages if none given)\n\
2189   -p, --package-query  search for REGEXP in the entire cygwin.com package\n\
2190                        repository (requires internet connectivity)\n\
2191   --delete-orphaned-installation-keys\n\
2192                        Delete installation keys of old, now unused\n\
2193                        installations from the registry.  Requires the right\n\
2194                        to change the registry.\n\
2195   --enable-unique-object-names Cygwin-DLL\n\
2196   --disable-unique-object-names Cygwin-DLL\n\
2197   --show-unique-object-names Cygwin-DLL\n\
2198                        Enable, disable, or show the setting of the\n\
2199                        \"unique object names\" setting in the Cygwin DLL\n\
2200                        given as argument to this option.  The DLL path must\n\
2201                        be given as valid Windows(!) path.\n\
2202                        See the users guide for more information.\n\
2203                        If you don't know what this means, don't change it.\n\
2204   -v, --verbose        produce more verbose output\n\
2205   -h, --help           annotate output with explanatory comments when given\n\
2206                        with another command, otherwise print this help\n\
2207   -V, --version        print the version of cygcheck and exit\n\
2208 \n\
2209 Note: -c, -f, and -l only report on packages that are currently installed. To\n\
2210   search all official Cygwin packages use -p instead.  The -p REGEXP matches\n\
2211   package names, descriptions, and names of files/paths within all packages.\n\
2212 \n");
2213   exit (status);
2214 }
2215
2216 struct option longopts[] = {
2217   {"check-setup", no_argument, NULL, 'c'},
2218   {"dump-only", no_argument, NULL, 'd'},
2219   {"sysinfo", no_argument, NULL, 's'},
2220   {"registry", no_argument, NULL, 'r'},
2221   {"verbose", no_argument, NULL, 'v'},
2222   {"keycheck", no_argument, NULL, 'k'},
2223   {"find-package", no_argument, NULL, 'f'},
2224   {"list-package", no_argument, NULL, 'l'},
2225   {"package-query", no_argument, NULL, 'p'},
2226   {"delete-orphaned-installation-keys", no_argument, NULL, CO_DELETE_KEYS},
2227   {"enable-unique-object-names", no_argument, NULL, CO_ENABLE_UON},
2228   {"disable-unique-object-names", no_argument, NULL, CO_DISABLE_UON},
2229   {"show-unique-object-names", no_argument, NULL, CO_SHOW_UON},
2230   {"help", no_argument, NULL, 'h'},
2231   {"version", no_argument, 0, 'V'},
2232   {0, no_argument, NULL, 0}
2233 };
2234
2235 static char opts[] = "cdsrvkflphV";
2236
2237 static void
2238 print_version ()
2239 {
2240   const char *v = strchr (version, ':');
2241   int len;
2242   if (!v)
2243     {
2244       v = "?";
2245       len = 1;
2246     }
2247   else
2248     {
2249       v += 2;
2250       len = strchr (v, ' ') - v;
2251     }
2252   printf ("\
2253 cygcheck version %.*s\n\
2254 System Checker for Cygwin\n\
2255 Copyright (C) 1998 - 2008 Red Hat, Inc.\n\
2256 Compiled on %s\n\
2257 ", len, v, __DATE__);
2258 }
2259
2260 void
2261 nuke (char *ev)
2262 {
2263   int n = 1 + strchr (ev, '=') - ev;
2264   char *s = (char *) malloc (n + 1);
2265   memcpy (s, ev, n);
2266   s[n] = '\0';
2267   putenv (s);
2268 }
2269
2270 extern "C" {
2271 unsigned long (*cygwin_internal) (int, ...);
2272 WCHAR cygwin_dll_path[32768];
2273 };
2274
2275 static void
2276 load_cygwin (int& argc, char **&argv)
2277 {
2278   HMODULE h;
2279
2280   if (!(h = LoadLibrary ("cygwin1.dll")))
2281     return;
2282   GetModuleFileNameW (h, cygwin_dll_path, 32768);
2283   if ((cygwin_internal = (DWORD (*) (int, ...)) GetProcAddress (h, "cygwin_internal")))
2284     {
2285       char **av = (char **) cygwin_internal (CW_ARGV);
2286       if (av && ((DWORD) av != (DWORD) -1))
2287         {
2288           /* Copy cygwin's idea of the argument list into this Window application. */
2289           for (argc = 0; av[argc]; argc++)
2290             continue;
2291           argv = (char **) calloc (argc + 1, sizeof (char *));
2292           for (char **argvp = argv; *av; av++)
2293             *argvp++ = strdup (*av);
2294         }
2295
2296
2297       char **envp = (char **) cygwin_internal (CW_ENVP);
2298       if (envp && ((DWORD) envp != (DWORD) -1))
2299         {
2300           /* Store path and revert to this value, otherwise path gets overwritten
2301              by the POSIXy Cygwin variation, which breaks cygcheck.
2302              Another approach would be to use the Cygwin PATH and convert it to
2303              Win32 again. */
2304           char *path = NULL;
2305           char **env;
2306           while (*(env = _environ))
2307             {
2308               if (strncmp (*env, "PATH=", 5) == 0)
2309                 path = strdup (*env);
2310               nuke (*env);
2311             }
2312           for (char **ev = envp; *ev; ev++)
2313             if (strncmp (*ev, "PATH=", 5) != 0)
2314               putenv (strdup (*ev));
2315           if (path)
2316             putenv (path);
2317         }
2318     }
2319   FreeLibrary (h);
2320 }
2321
2322 int
2323 main (int argc, char **argv)
2324 {
2325   int i;
2326   bool ok = true;
2327   load_cygwin (argc, argv);
2328
2329   /* Need POSIX sorting while parsing args, but don't forget the
2330      user's original environment.  */
2331   char *posixly = getenv ("POSIXLY_CORRECT");
2332   if (posixly == NULL)
2333     (void) putenv ("POSIXLY_CORRECT=1");
2334   while ((i = getopt_long (argc, argv, opts, longopts, NULL)) != EOF)
2335     switch (i)
2336       {
2337       case 's':
2338         sysinfo = 1;
2339         break;
2340       case 'c':
2341         check_setup = 1;
2342         break;
2343       case 'd':
2344         dump_only = 1;
2345         break;
2346       case 'r':
2347         registry = 1;
2348         break;
2349       case 'v':
2350         verbose = 1;
2351         break;
2352       case 'k':
2353         keycheck = 1;
2354         break;
2355       case 'f':
2356         find_package = 1;
2357         break;
2358       case 'l':
2359         list_package = 1;
2360         break;
2361       case 'p':
2362         grep_packages = 1;
2363         break;
2364       case 'h':
2365         givehelp = 1;
2366         break;
2367       case CO_DELETE_KEYS:
2368         del_orphaned_reg = 1;
2369         break;
2370       case CO_ENABLE_UON:
2371       case CO_DISABLE_UON:
2372       case CO_SHOW_UON:
2373         unique_object_name_opt = i;
2374         break;
2375       case 'V':
2376         print_version ();
2377         exit (0);
2378       default:
2379         usage (stderr, 1);
2380        /*NOTREACHED*/}
2381   argc -= optind;
2382   argv += optind;
2383   if (posixly == NULL)
2384     putenv ("POSIXLY_CORRECT=");
2385
2386   if ((argc == 0) && !sysinfo && !keycheck && !check_setup && !list_package
2387       && !del_orphaned_reg)
2388     {
2389       if (givehelp)
2390         usage (stdout, 0);
2391       else
2392         usage (stderr, 1);
2393     }
2394
2395   if ((check_setup || sysinfo || find_package || list_package || grep_packages
2396        || del_orphaned_reg || unique_object_name_opt)
2397       && keycheck)
2398     usage (stderr, 1);
2399
2400   if ((find_package || list_package || grep_packages)
2401       && (check_setup || del_orphaned_reg))
2402     usage (stderr, 1);
2403
2404   if ((check_setup || sysinfo || find_package || list_package || grep_packages
2405        || del_orphaned_reg)
2406       && unique_object_name_opt)
2407     usage (stderr, 1);
2408
2409   if (dump_only && !check_setup && !sysinfo)
2410     usage (stderr, 1);
2411
2412   if (find_package + list_package + grep_packages > 1)
2413     usage (stderr, 1);
2414
2415   if (keycheck)
2416     return check_keys ();
2417   if (unique_object_name_opt)
2418     return handle_unique_object_name (unique_object_name_opt, *argv);
2419   if (del_orphaned_reg)
2420     del_orphaned_reg_installations ();
2421   if (grep_packages)
2422     return package_grep (*argv);
2423
2424   init_paths ();
2425
2426   /* FIXME: Add help for check_setup and {list,find}_package */
2427   if (argc >= 1 && givehelp && !check_setup && !find_package && !list_package)
2428     {
2429       printf("Here is where the OS will find your program%s, and which dlls\n",
2430              argc > 1 ? "s" : "");
2431       printf ("will be used for it.  Use -v to see DLL version info\n");
2432
2433       if (!sysinfo)
2434         printf ("\n");
2435     }
2436
2437   if (check_setup)
2438     dump_setup (verbose, argv, !dump_only);
2439   else if (find_package)
2440     package_find (verbose, argv);
2441   else if (list_package)
2442     package_list (verbose, argv);
2443   else
2444     for (i = 0; i < argc; i++)
2445       {
2446         if (i)
2447           puts ("");
2448        ok &= cygcheck (argv[i]);
2449       }
2450
2451   if (sysinfo)
2452     {
2453       dump_sysinfo ();
2454       if (!check_setup)
2455         {
2456           puts ("");
2457           dump_setup (verbose, NULL, !dump_only);
2458         }
2459
2460       if (!givehelp)
2461         puts ("Use -h to see help about each section");
2462     }
2463
2464   return ok ? EXIT_SUCCESS : EXIT_FAILURE;
2465 }