OSDN Git Service

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