OSDN Git Service

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