OSDN Git Service

* loadlib.h: New header implementing safe LoadLibrary calls.
[pf3gnuchains/sourceware.git] / winsup / utils / regtool.cc
1 /* regtool.cc
2
3    Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008,
4    2009, 2010 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 #include <stdio.h>
13 #include <stdlib.h>
14 #include <errno.h>
15 #include <ctype.h>
16 #include <wchar.h>
17 #include <getopt.h>
18 #include <locale.h>
19 #define WINVER 0x0502
20 #include <windows.h>
21 #include <sys/cygwin.h>
22 #include "loadlib.h"
23
24 #define DEFAULT_KEY_SEPARATOR '\\'
25
26 #define REG_AUTO -1
27
28 int value_type = REG_AUTO;
29
30 char key_sep = DEFAULT_KEY_SEPARATOR;
31
32 #define LIST_KEYS       0x01
33 #define LIST_VALS       0x02
34 #define LIST_ALL        (LIST_KEYS | LIST_VALS)
35
36 static const char version[] = "$Revision: 1.8 $";
37 static char *prog_name;
38
39 static struct option longopts[] =
40 {
41   {"binary", no_argument, NULL, 'b' },
42   {"dword", no_argument, NULL, 'd' },
43   {"dword-be", no_argument, NULL, 'D' },
44   {"expand-string", no_argument, NULL, 'e' },
45   {"help", no_argument, NULL, 'h' },
46   {"integer", no_argument, NULL, 'i' },
47   {"keys", no_argument, NULL, 'k'},
48   {"list", no_argument, NULL, 'l'},
49   {"multi-string", no_argument, NULL, 'm'},
50   {"none", no_argument, NULL, 'n' },
51   {"postfix", no_argument, NULL, 'p'},
52   {"quiet", no_argument, NULL, 'q'},
53   {"qword", no_argument, NULL, 'Q' },
54   {"string", no_argument, NULL, 's'},
55   {"verbose", no_argument, NULL, 'v'},
56   {"version", no_argument, NULL, 'V'},
57   {"wow64", no_argument, NULL, 'w'},
58   {"wow32", no_argument, NULL, 'W'},
59   {"hex", no_argument, NULL, 'x'},
60   {"key-separator", required_argument, NULL, 'K'},
61   {NULL, 0, NULL, 0}
62 };
63
64 static char opts[] = "bdDehiklmnpqQsvVwWxK:";
65
66 const char *types[] =
67 {
68   "REG_NONE",
69   "REG_SZ",
70   "REG_EXPAND_SZ",
71   "REG_BINARY",
72   "REG_DWORD",
73   "REG_DWORD_BIG_ENDIAN",
74   "REG_LINK",
75   "REG_MULTI_SZ",
76   "REG_RESOURCE_LIST",
77   "REG_FULL_RESOURCE_DESCRIPTOR",
78   "REG_RESOURCE_REQUIREMENTS_LIST",
79   "REG_QWORD",
80 };
81
82 int listwhat = 0;
83 int postfix = 0;
84 int verbose = 0;
85 int quiet = 0;
86 int hex = 0;
87 DWORD wow64 = 0;
88 char **argv;
89
90 HKEY key;
91 wchar_t *value;
92
93 static void
94 usage (FILE *where = stderr)
95 {
96   fprintf (where, ""
97   "Usage: %s [OPTION] ACTION KEY [data...]\n"
98   "View or edit the Win32 registry\n"
99   "\n", prog_name);
100   if (where == stdout)
101     {
102       fprintf (where, ""
103       "Actions:\n"
104       " add KEY\\SUBKEY             add new SUBKEY\n"
105       " check KEY                  exit 0 if KEY exists, 1 if not\n"
106       " get KEY\\VALUE              prints VALUE to stdout\n"
107       " list KEY                   list SUBKEYs and VALUEs\n"
108       " remove KEY                 remove KEY\n"
109       " set KEY\\VALUE [data ...]   set VALUE\n"
110       " unset KEY\\VALUE            removes VALUE from KEY\n"
111       " load KEY\\SUBKEY PATH       load hive from PATH into new SUBKEY\n"
112       " unload KEY\\SUBKEY          unload hive and remove SUBKEY\n"
113       " save KEY\\SUBKEY PATH       save SUBKEY into new hive PATH\n"
114       "\n");
115       fprintf (where, ""
116       "Options for 'list' Action:\n"
117       " -k, --keys           print only KEYs\n"
118       " -l, --list           print only VALUEs\n"
119       " -p, --postfix        like ls -p, appends '\\' postfix to KEY names\n"
120       "\n"
121       "Options for 'get' Action:\n"
122       " -b, --binary         print data as printable hex bytes\n"
123       " -n, --none           print data as stream of bytes as stored in registry\n"
124       " -x, --hex            print numerical data as hex numbers\n"
125       "\n"
126       "Options for 'set' Action:\n"
127       " -b, --binary         set type to REG_BINARY (hex args or '-')\n"
128       " -d, --dword          set type to REG_DWORD\n"
129       " -D, --dword-be       set type to REG_DWORD_BIG_ENDIAN\n"
130       " -e, --expand-string  set type to REG_EXPAND_SZ\n"
131       " -i, --integer        set type to REG_DWORD\n"
132       " -m, --multi-string   set type to REG_MULTI_SZ\n"
133       " -n, --none           set type to REG_NONE\n"
134       " -Q, --qword          set type to REG_QWORD\n"
135       " -s, --string         set type to REG_SZ\n"
136       "\n"
137       "Options for 'set' and 'unset' Actions:\n"
138       " -K<c>, --key-separator[=]<c>  set key-value separator to <c> instead of '\\'\n"
139       "\n"
140       "Other Options:\n"
141       " -h, --help     output usage information and exit\n"
142       " -q, --quiet    no error output, just nonzero return if KEY/VALUE missing\n"
143       " -v, --verbose  verbose output, including VALUE contents when applicable\n"
144       " -w, --wow64    access 64 bit registry view (ignored on 32 bit Windows)\n"
145       " -W, --wow32    access 32 bit registry view (ignored on 32 bit Windows)\n"
146       " -V, --version  output version information and exit\n"
147       "\n");
148       fprintf (where, ""
149       "KEY is in the format [host]\\prefix\\KEY\\KEY\\VALUE, where host is optional\n"
150       "remote host in either \\\\hostname or hostname: format and prefix is any of:\n"
151       "  root     HKCR  HKEY_CLASSES_ROOT (local only)\n"
152       "  config   HKCC  HKEY_CURRENT_CONFIG (local only)\n"
153       "  user     HKCU  HKEY_CURRENT_USER (local only)\n"
154       "  machine  HKLM  HKEY_LOCAL_MACHINE\n"
155       "  users    HKU   HKEY_USERS\n"
156       "\n"
157       "If the keyname starts with a forward slash ('/'), the forward slash is used\n"
158       "as separator and the backslash can be used as escape character.\n");
159       fprintf (where, ""
160       "Example:\n"
161       "%s list '/machine/SOFTWARE/Classes/MIME/Database/Content Type/audio\\/wav'\n", prog_name);
162     }
163   if (where == stderr)
164     fprintf (where,
165     "ACTION is one of add, check, get, list, remove, set, unset, load, unload, save\n"
166     "\n"
167     "Try '%s --help' for more information.\n", prog_name);
168   exit (where == stderr ? 1 : 0);
169 }
170
171 static void
172 print_version ()
173 {
174   const char *v = strchr (version, ':');
175   int len;
176   if (!v)
177     {
178       v = "?";
179       len = 1;
180     }
181   else
182     {
183       v += 2;
184       len = strchr (v, ' ') - v;
185     }
186   printf ("\
187 %s (cygwin) %.*s\n\
188 Registry Tool\n\
189 Copyright 2000-2009 Red Hat, Inc.\n\
190 Compiled on %s\n\
191 ", prog_name, len, v, __DATE__);
192 }
193
194 void
195 Fail (DWORD rv)
196 {
197   char *buf;
198   if (!quiet)
199     {
200       FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER
201                      | FORMAT_MESSAGE_FROM_SYSTEM,
202                      0, rv, 0, (CHAR *) & buf, 0, 0);
203       fprintf (stderr, "Error (%ld): %s\n", rv, buf);
204       LocalFree (buf);
205     }
206   exit (1);
207 }
208
209 static struct
210 {
211   const char *string;
212   HKEY key;
213 } wkprefixes[] =
214 {
215   {"root", HKEY_CLASSES_ROOT},
216   {"HKCR", HKEY_CLASSES_ROOT},
217   {"HKEY_CLASSES_ROOT", HKEY_CLASSES_ROOT},
218   {"config", HKEY_CURRENT_CONFIG},
219   {"HKCC", HKEY_CURRENT_CONFIG},
220   {"HKEY_CURRENT_CONFIG", HKEY_CURRENT_CONFIG},
221   {"user", HKEY_CURRENT_USER},
222   {"HKCU", HKEY_CURRENT_USER},
223   {"HKEY_CURRENT_USER", HKEY_CURRENT_USER},
224   {"machine", HKEY_LOCAL_MACHINE},
225   {"HKLM", HKEY_LOCAL_MACHINE},
226   {"HKEY_LOCAL_MACHINE", HKEY_LOCAL_MACHINE},
227   {"users", HKEY_USERS},
228   {"HKU", HKEY_USERS},
229   {"HKEY_USERS", HKEY_USERS},
230   {0, 0}
231 };
232
233 void
234 translate (char *key)
235 {
236 #define isodigit(c) (strchr("01234567", c))
237 #define tooct(c)    ((c)-'0')
238 #define tohex(c)    (strchr(_hs,tolower(c))-_hs)
239   static char _hs[] = "0123456789abcdef";
240
241   char *d = key;
242   char *s = key;
243   char c;
244
245   while (*s)
246     {
247       if (*s == '\\')
248         switch (*++s)
249           {
250           case 'a':
251             *d++ = '\007';
252             break;
253           case 'b':
254             *d++ = '\b';
255             break;
256           case 'e':
257             *d++ = '\033';
258             break;
259           case 'f':
260             *d++ = '\f';
261             break;
262           case 'n':
263             *d++ = '\n';
264             break;
265           case 'r':
266             *d++ = '\r';
267             break;
268           case 't':
269             *d++ = '\t';
270             break;
271           case 'v':
272             *d++ = '\v';
273             break;
274           case '0':
275           case '1':
276           case '2':
277           case '3':
278           case '4':
279           case '5':
280           case '6':
281           case '7':
282             c = tooct (*s);
283             if (isodigit (s[1]))
284               {
285                 c = (c << 3) | tooct (*++s);
286                 if (isodigit (s[1]))
287                   c = (c << 3) | tooct (*++s);
288               }
289             *d++ = c;
290             break;
291           case 'x':
292             if (!isxdigit (s[1]))
293               c = '0';
294             else
295               {
296                 c = tohex (*++s);
297                 if (isxdigit (s[1]))
298                   c = (c << 4) | tohex (*++s);
299               }
300             *d++ = c;
301             break;
302           default:              /* before non-special char: just add the char */
303             *d++ = *s;
304             break;
305           }
306       else if (*s == '/')
307         *d++ = '\\';
308       else
309         *d++ = *s;
310       ++s;
311     }
312   *d = '\0';
313 }
314
315 void
316 find_key (int howmanyparts, REGSAM access, int option = 0)
317 {
318   HKEY base;
319   int rv;
320   char *n = argv[0], *e, *h, c;
321   char* host = NULL;
322   int i;
323   size_t len;
324
325   if (*n == '/')
326     translate (n);
327   if (*n != '\\')
328     {
329       /* expect host:/key/value format */
330       host = (char*) malloc (strlen (n) + 1);
331       host[0] = host [1] = '\\';
332       for (e = n, h = host + 2; *e && *e != ':'; e++, h++)
333         *h = *e;
334       *h = 0;
335       n = e + 1;
336       if (*n == '/')
337         translate (n);
338     }
339   else if (n[0] == '\\' && n[1] == '\\')
340     {
341       /* expect //host/key/value format */
342       host = (char*) malloc (strlen (n) + 1);
343       host[0] = host[1] = '\\';
344       for (e = n + 2, h = host + 2; *e && *e != '\\'; e++, h++)
345         *h = *e;
346       *h = 0;
347       n = e;
348     }
349   while (*n != '\\')
350     n++;
351   *n++ = 0;
352   for (e = n; *e && *e != '\\'; e++);
353   c = *e;
354   *e = 0;
355   for (i = 0; wkprefixes[i].string; i++)
356     if (strcmp (wkprefixes[i].string, n) == 0)
357       break;
358   if (!wkprefixes[i].string)
359     {
360       fprintf (stderr, "Unknown key prefix.  Valid prefixes are:\n");
361       for (i = 0; wkprefixes[i].string; i++)
362         fprintf (stderr, "\t%s\n", wkprefixes[i].string);
363       exit (1);
364     }
365
366   n = e;
367   *e = c;
368   while (*n && *n == '\\')
369     n++;
370   e = n + strlen (n);
371   if (howmanyparts > 1)
372     {
373       while (n < e && *e != key_sep)
374         e--;
375       if (*e != key_sep)
376         {
377           key = wkprefixes[i].key;
378           if (value)
379             free (value);
380           len = mbstowcs (NULL, n, 0) + 1;
381           value = (wchar_t *) malloc (len * sizeof (wchar_t));
382           mbstowcs (value, n, len);
383           return;
384         }
385       else
386         {
387           *e = 0;
388           if (value)
389             free (value);
390           len = mbstowcs (NULL, e + 1, 0) + 1;
391           value = (wchar_t *) malloc (len * sizeof (wchar_t));
392           mbstowcs (value, e + 1, len);
393         }
394     }
395   if (host)
396     {
397       rv = RegConnectRegistry (host, wkprefixes[i].key, &base);
398       if (rv != ERROR_SUCCESS)
399         Fail (rv);
400       free (host);
401     }
402   else
403     base = wkprefixes[i].key;
404
405   if (n[0] == 0)
406     key = base;
407   else
408     {
409       len = mbstowcs (NULL, n, 0) + 1;
410       wchar_t name[len];
411       mbstowcs (name, n, len);
412       if (access)
413         {
414           rv = RegOpenKeyExW (base, name, 0, access | wow64, &key);
415           if (option && (rv == ERROR_SUCCESS || rv == ERROR_ACCESS_DENIED))
416             {
417               /* reopen with desired option due to missing option support in
418                  RegOpenKeyE */
419               /* FIXME: may create the key in rare cases (e.g. access denied
420                  in parent) */
421               HKEY key2;
422               if (RegCreateKeyExW (base, name, 0, NULL, option, access | wow64,
423                                   NULL, &key2, NULL)
424                   == ERROR_SUCCESS)
425                 {
426                   if (rv == ERROR_SUCCESS)
427                     RegCloseKey (key);
428                   key = key2;
429                   rv = ERROR_SUCCESS;
430                 }
431             }
432           if (rv != ERROR_SUCCESS)
433             Fail (rv);
434         }
435       else if (argv[1])
436         { 
437           ssize_t len = cygwin_conv_path (CCP_POSIX_TO_WIN_W, argv[1], NULL, 0);
438           wchar_t win32_path[len];
439           cygwin_conv_path (CCP_POSIX_TO_WIN_W, argv[1], win32_path, len);
440           rv = RegLoadKeyW (base, name, win32_path);
441           if (rv != ERROR_SUCCESS)
442             Fail (rv);
443           if (verbose)
444             printf ("key %ls loaded from file %ls\n", name, win32_path);
445         }
446       else
447         { 
448           rv = RegUnLoadKeyW (base, name);
449           if (rv != ERROR_SUCCESS)
450             Fail (rv);
451           if (verbose)
452             printf ("key %ls unloaded\n", name);
453         }
454     }
455 }
456
457
458 int
459 cmd_list ()
460 {
461   DWORD num_subkeys, maxsubkeylen, num_values, maxvalnamelen, maxvaluelen;
462   DWORD maxclasslen;
463   wchar_t *subkey_name, *value_name, *class_name, *vd;
464   unsigned char *value_data;
465   DWORD i, j, m, n, t;
466   int v;
467
468   find_key (1, KEY_READ);
469   RegQueryInfoKeyW (key, 0, 0, 0, &num_subkeys, &maxsubkeylen, &maxclasslen,
470                     &num_values, &maxvalnamelen, &maxvaluelen, 0, 0);
471
472   subkey_name = (wchar_t *) malloc ((maxsubkeylen + 1) * sizeof (wchar_t));
473   class_name = (wchar_t *) malloc ((maxclasslen + 1) * sizeof (wchar_t));
474   value_name = (wchar_t *) malloc ((maxvalnamelen + 1) * sizeof (wchar_t));
475   value_data = (unsigned char *) malloc (maxvaluelen + 1);
476
477   if (!listwhat)
478     listwhat = LIST_ALL;
479
480   if (listwhat & LIST_KEYS)
481     for (i = 0; i < num_subkeys; i++)
482       {
483         m = (maxsubkeylen + 1) * sizeof (wchar_t);
484         n = (maxclasslen + 1) * sizeof (wchar_t);
485         RegEnumKeyExW (key, i, subkey_name, &m, 0, class_name, &n, 0);
486         printf ("%ls", subkey_name);
487         if (postfix || verbose)
488           fputc (key_sep, stdout);
489
490         if (verbose)
491           printf (" (%ls)", class_name);
492
493         puts ("");
494       }
495
496   if (listwhat & LIST_VALS)
497     for (i = 0; i < num_values; i++)
498       {
499         m = (maxvalnamelen + 1) * sizeof (wchar_t);
500         n = maxvaluelen + 1;
501         RegEnumValueW (key, i, value_name, &m, 0, &t, (BYTE *) value_data, &n);
502         value_data[n] = 0;
503         if (!verbose)
504           printf ("%ls\n", value_name);
505         else
506           {
507             printf ("%ls (%s) = ", value_name, types[t]);
508             switch (t)
509               {
510               case REG_NONE:
511               case REG_BINARY:
512                 for (j = 0; j < 8 && j < n; j++)
513                   printf ("%02x ", value_data[j]);
514                 printf ("\n");
515                 break;
516               case REG_DWORD:
517                 printf ("0x%08lx (%lu)\n", *(DWORD *) value_data,
518                         *(DWORD *) value_data);
519                 break;
520               case REG_DWORD_BIG_ENDIAN:
521                 v = ((value_data[0] << 24)
522                      | (value_data[1] << 16)
523                      | (value_data[2] << 8)
524                      | (value_data[3]));
525                 printf ("0x%08x (%d)\n", v, v);
526                 break;
527               case REG_QWORD:
528                 printf ("0x%016llx (%llu)\n",
529                         *(unsigned long long *) value_data,
530                         *(unsigned long long *) value_data);
531                 break;
532               case REG_EXPAND_SZ:
533               case REG_SZ:
534               case REG_LINK:
535                 printf ("\"%ls\"\n", (wchar_t *) value_data);
536                 break;
537               case REG_MULTI_SZ:
538                 vd = (wchar_t *) value_data;
539                 while (vd && *vd)
540                   {
541                     printf ("\"%ls\"", vd);
542                     vd = vd + wcslen (vd) + 1;
543                     if (*vd)
544                       printf (", ");
545                   }
546                 printf ("\n");
547                 break;
548               default:
549                 printf ("?\n");
550                 break;
551               }
552           }
553       }
554   return 0;
555 }
556
557 int
558 cmd_add ()
559 {
560   find_key (2, KEY_ALL_ACCESS);
561   HKEY newkey;
562   DWORD newtype;
563   int rv = RegCreateKeyExW (key, value, 0, NULL, REG_OPTION_NON_VOLATILE,
564                             KEY_ALL_ACCESS | wow64, 0, &newkey, &newtype);
565   if (rv != ERROR_SUCCESS)
566     Fail (rv);
567
568   if (verbose)
569     {
570       if (newtype == REG_OPENED_EXISTING_KEY)
571         printf ("Key %ls already exists\n", value);
572       else
573         printf ("Key %ls created\n", value);
574     }
575   return 0;
576 }
577
578 extern "C" {
579 WINADVAPI LONG WINAPI (*regDeleteKeyEx)(HKEY, LPCWSTR, REGSAM, DWORD);
580 }
581
582 int
583 cmd_remove ()
584 {
585   DWORD rv;
586
587   find_key (2, KEY_ALL_ACCESS);
588   if (wow64)
589     {
590       HMODULE mod = LoadLibrary ("advapi32.dll");
591       if (mod)
592         regDeleteKeyEx = (WINADVAPI LONG WINAPI (*)(HKEY, LPCWSTR, REGSAM, DWORD)) GetProcAddress (mod, "RegDeleteKeyExW");
593     }
594   if (regDeleteKeyEx)
595     rv = (*regDeleteKeyEx) (key, value, wow64, 0);
596   else
597     rv = RegDeleteKeyW (key, value);
598   if (rv != ERROR_SUCCESS)
599     Fail (rv);
600   if (verbose)
601     printf ("subkey %ls deleted\n", value);
602   return 0;
603 }
604
605 int
606 cmd_check ()
607 {
608   find_key (1, KEY_READ);
609   if (verbose)
610     printf ("key %s exists\n", argv[0]);
611   return 0;
612 }
613
614 int
615 cmd_set ()
616 {
617   int i, n, max_n;
618   DWORD v, rv;
619   unsigned long long llval;
620   char *a = argv[1], *data = 0;
621   find_key (2, KEY_ALL_ACCESS);
622
623   if (!a)
624     usage ();
625   if (value_type == REG_AUTO)
626     {
627       char *e;
628       llval = strtoull (a, &e, 0);
629       if (a[0] == '%')
630         value_type = REG_EXPAND_SZ;
631       else if (a[0] && !*e)
632         value_type = llval > 0xffffffffULL ? REG_QWORD : REG_DWORD;
633       else if (argv[2])
634         value_type = REG_MULTI_SZ;
635       else
636         value_type = REG_SZ;
637     }
638
639   switch (value_type)
640     {
641     case REG_NONE:
642     case REG_BINARY:
643       for (n = 0; argv[n+1]; n++)
644         ;
645       if (n == 1 && strcmp (argv[1], "-") == 0)
646         { /* read from stdin */
647           i = n = 0;
648           for (;;)
649             {
650               if (i <= n)
651                 {
652                   i = n + BUFSIZ;
653                   data = (char *) realloc (data, i);
654                 }
655               int r = fread (data+n, 1, i-n, stdin);
656               if (r <= 0)
657                 break;
658               n += r;
659             }
660         }
661       else if (n > 0)
662         { /* parse hex from argv */
663           data = (char *) malloc (n);
664           for (i = 0; i < n; i++)
665             {
666               char *e;
667               errno = 0;
668               v = strtoul (argv[i+1], &e, 16);
669               if (errno || v > 0xff || *e)
670                 {
671                   fprintf (stderr, "Invalid hex constant `%s'\n", argv[i+1]);
672                   exit (1);
673                 }
674               data[i] = (char) v;
675             }
676         }
677       rv = RegSetValueExW (key, value, 0, value_type, (const BYTE *) data, n);
678       break;
679     case REG_DWORD:
680       v = strtoul (a, 0, 0);
681       rv = RegSetValueExW (key, value, 0, REG_DWORD, (const BYTE *) &v,
682                           sizeof (v));
683       break;
684     case REG_DWORD_BIG_ENDIAN:
685       v = strtoul (a, 0, 0);
686       v = (((v & 0xff) << 24)
687            | ((v & 0xff00) << 8)
688            | ((v & 0xff0000) >> 8)
689            | ((v & 0xff000000) >> 24));
690       rv = RegSetValueExW (key, value, 0, REG_DWORD_BIG_ENDIAN,
691                           (const BYTE *) &v, sizeof (v));
692       break;
693     case REG_QWORD:
694       llval = strtoul (a, 0, 0);
695       rv = RegSetValueExW (key, value, 0, REG_QWORD, (const BYTE *) &llval,
696                           sizeof (llval));
697       break;
698     case REG_SZ:
699     case REG_EXPAND_SZ:
700       n = mbstowcs (NULL, a, 0);
701       wchar_t w[n + 1];
702       mbstowcs (w, a, n + 1);
703       rv = RegSetValueExW (key, value, 0, value_type,
704                            (const BYTE *) w, (n + 1) * sizeof (wchar_t));
705       break;
706     case REG_MULTI_SZ:
707       for (i = 1, max_n = 1; argv[i]; i++)
708         max_n += mbstowcs (NULL, argv[i], 0) + 1;
709       data = (char *) malloc (max_n * sizeof (wchar_t));
710       for (i = 1, n = 0; argv[i]; i++)
711         n += mbstowcs ((wchar_t *) data + n, argv[i], max_n - n) + 1;
712       ((wchar_t *)data)[n] = L'\0';
713       rv = RegSetValueExW (key, value, 0, REG_MULTI_SZ, (const BYTE *) data,
714                            (max_n + 1) * sizeof (wchar_t));
715       break;
716     case REG_AUTO:
717       rv = ERROR_SUCCESS;
718       break;
719     default:
720       rv = ERROR_INVALID_CATEGORY;
721       break;
722     }
723  
724   if (data)
725     free(data);
726
727   if (rv != ERROR_SUCCESS)
728     Fail (rv);
729
730   return 0;
731 }
732
733 int
734 cmd_unset ()
735 {
736   find_key (2, KEY_ALL_ACCESS);
737   DWORD rv = RegDeleteValueW (key, value);
738   if (rv != ERROR_SUCCESS)
739     Fail (rv);
740   if (verbose)
741     printf ("value %ls deleted\n", value);
742   return 0;
743 }
744
745 int
746 cmd_get ()
747 {
748   find_key (2, KEY_READ);
749   DWORD vtype, dsize, rv;
750   PBYTE data;
751   wchar_t *vd;
752
753   rv = RegQueryValueExW (key, value, 0, &vtype, 0, &dsize);
754   if (rv != ERROR_SUCCESS)
755     Fail (rv);
756   data = (PBYTE) malloc (dsize + 1);
757   rv = RegQueryValueExW (key, value, 0, &vtype, data, &dsize);
758   if (rv != ERROR_SUCCESS)
759     Fail (rv);
760   if (value_type == REG_BINARY)
761     {
762       for (unsigned i = 0; i < dsize; i++)
763         printf ("%02x%c", (unsigned char)data[i],
764           (i < dsize-1 ? ' ' : '\n'));
765     }
766   else if (value_type == REG_NONE)
767     fwrite (data, dsize, 1, stdout);
768   else
769     switch (vtype)
770       {
771       case REG_NONE:
772       case REG_BINARY:
773         fwrite (data, dsize, 1, stdout);
774         break;
775       case REG_DWORD:
776         printf (hex ? "0x%08lx\n" : "%lu\n", *(DWORD *) data);
777         break;
778       case REG_DWORD_BIG_ENDIAN:
779         rv = ((data[0] << 24)
780               | (data[1] << 16)
781               | (data[2] << 8)
782               | (data[3]));
783         printf (hex ? "0x%08lx\n" : "%lu\n", rv);
784         break;
785       case REG_QWORD:
786         printf (hex ? "0x%016llx\n" : "%llu\n", *(unsigned long long *) data);
787         break;
788       case REG_SZ:
789       case REG_LINK:
790         printf ("%ls\n", (wchar_t *) data);
791         break;
792       case REG_EXPAND_SZ:
793         if (value_type == REG_EXPAND_SZ)        // hack
794           {
795             wchar_t *buf;
796             DWORD bufsize;
797             bufsize = ExpandEnvironmentStringsW ((wchar_t *) data, 0, 0);
798             buf = (wchar_t *) malloc (bufsize + 1);
799             ExpandEnvironmentStringsW ((wchar_t *) data, buf, bufsize + 1);
800             free (data);
801             data = (PBYTE) buf;
802           }
803         printf ("%ls\n", (wchar_t *) data);
804         break;
805       case REG_MULTI_SZ:
806         vd = (wchar_t *) data;
807         while (vd && *vd)
808           {
809             printf ("%ls\n", vd);
810             vd = vd + wcslen (vd) + 1;
811           }
812         break;
813       }
814   return 0;
815 }
816
817 int
818 cmd_load ()
819 {
820   if (!argv[1])
821     {
822       usage ();
823       return 1;
824     }
825   find_key (1, 0);
826   return 0;
827 }
828
829 int
830 cmd_unload ()
831 {
832   if (argv[1])
833     {
834       usage ();
835       return 1;
836     }
837   find_key (1, 0);
838   return 0;
839 }
840
841 DWORD
842 set_privilege (const char *name)
843 {
844   TOKEN_PRIVILEGES tp;
845   if (!LookupPrivilegeValue (NULL, name, &tp.Privileges[0].Luid))
846     return GetLastError ();
847   tp.PrivilegeCount = 1;
848   tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
849   HANDLE t;
850   /* OpenProcessToken does not work here, because main thread has its own
851      impersonation token */
852   if (!OpenThreadToken (GetCurrentThread (), TOKEN_ADJUST_PRIVILEGES, FALSE, &t))
853     return GetLastError ();
854   AdjustTokenPrivileges (t, FALSE, &tp, 0, NULL, NULL);
855   DWORD rv = GetLastError ();
856   CloseHandle (t);
857   return rv;
858 }
859
860 int
861 cmd_save ()
862 {
863   if (!argv[1])
864     {
865       usage ();
866       return 1;
867     }
868   /* try to set SeBackupPrivilege, let RegSaveKey report the error */
869   set_privilege (SE_BACKUP_NAME);
870   /* REG_OPTION_BACKUP_RESTORE is necessary to save /HKLM/SECURITY */
871   find_key (1, KEY_QUERY_VALUE, REG_OPTION_BACKUP_RESTORE);
872   ssize_t len = cygwin_conv_path (CCP_POSIX_TO_WIN_W, argv[1], NULL, 0);
873   wchar_t win32_path[len];
874   cygwin_conv_path (CCP_POSIX_TO_WIN_W, argv[1], win32_path, len);
875   DWORD rv = RegSaveKeyW (key, win32_path, NULL);
876   if (rv != ERROR_SUCCESS)
877     Fail (rv);
878   if (verbose)
879     printf ("key saved to %ls\n", win32_path);
880   return 0;
881 }
882
883 static struct
884 {
885   const char *name;
886   int (*func) ();
887 } commands[] =
888 {
889   {"list", cmd_list},
890   {"add", cmd_add},
891   {"remove", cmd_remove},
892   {"check", cmd_check},
893   {"set", cmd_set},
894   {"unset", cmd_unset},
895   {"get", cmd_get},
896   {"load", cmd_load},
897   {"unload", cmd_unload},
898   {"save", cmd_save},
899   {0, 0}
900 };
901
902 int
903 main (int argc, char **_argv)
904 {
905   int g;
906
907   setlocale (LC_ALL, "");
908   prog_name = strrchr (_argv[0], '/');
909   if (prog_name == NULL)
910     prog_name = strrchr (_argv[0], '\\');
911   if (prog_name == NULL)
912     prog_name = _argv[0];
913   else
914     prog_name++;
915
916   while ((g = getopt_long (argc, _argv, opts, longopts, NULL)) != EOF)
917     switch (g)
918         {
919         case 'b':
920           value_type = REG_BINARY;
921           break;
922         case 'd':
923           value_type = REG_DWORD;
924           break;
925         case 'D':
926           value_type = REG_DWORD_BIG_ENDIAN;
927           break;
928         case 'e':
929           value_type = REG_EXPAND_SZ;
930           break;
931         case 'k':
932           listwhat |= LIST_KEYS;
933           break;
934         case 'h':
935           usage (stdout);
936         case 'i':
937           value_type = REG_DWORD;
938           break;
939         case 'l':
940           listwhat |= LIST_VALS;
941           break;
942         case 'm':
943           value_type = REG_MULTI_SZ;
944           break;
945         case 'n':
946           value_type = REG_NONE;
947           break;
948         case 'p':
949           postfix++;
950           break;
951         case 'q':
952           quiet++;
953           break;
954         case 'Q':
955           value_type = REG_QWORD;
956           break;
957         case 's':
958           value_type = REG_SZ;
959           break;
960         case 'v':
961           verbose++;
962           break;
963         case 'V':
964           print_version ();
965           exit (0);
966         case 'w':
967           wow64 = KEY_WOW64_64KEY;
968           break;
969         case 'W':
970           wow64 = KEY_WOW64_32KEY;
971           break;
972         case 'x':
973           hex++;
974           break;
975         case 'K':
976           key_sep = *optarg;
977           break;
978         default :
979           usage ();
980         }
981
982   if ((_argv[optind] == NULL) || (_argv[optind+1] == NULL))
983     usage ();
984
985   argv = _argv + optind;
986   int i;
987   for (i = 0; commands[i].name; i++)
988     if (strcmp (commands[i].name, argv[0]) == 0)
989       {
990         argv++;
991         return commands[i].func ();
992       }
993   usage ();
994
995   return 0;
996 }