OSDN Git Service

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