hivexsh: Add 'setval' and 'commit' commands.
[libguestfs.git] / hivex / hivexsh.c
1 /* hivexsh - Hive shell.
2  * Copyright (C) 2009 Red Hat Inc.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17  */
18
19 #include <config.h>
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <stdint.h>
25 #include <inttypes.h>
26 #include <fcntl.h>
27 #include <unistd.h>
28 #include <assert.h>
29 #include <errno.h>
30
31 #ifdef HAVE_LIBREADLINE
32 #include <readline/readline.h>
33 #include <readline/history.h>
34 #endif
35
36 #ifdef HAVE_GETTEXT
37 #include "gettext.h"
38 #define _(str) dgettext(PACKAGE, (str))
39 //#define N_(str) dgettext(PACKAGE, (str))
40 #else
41 #define _(str) str
42 //#define N_(str) str
43 #endif
44
45 #define STREQ(a,b) (strcmp((a),(b)) == 0)
46 #define STRCASEEQ(a,b) (strcasecmp((a),(b)) == 0)
47 #define STRNEQ(a,b) (strcmp((a),(b)) != 0)
48 //#define STRCASENEQ(a,b) (strcasecmp((a),(b)) != 0)
49 //#define STREQLEN(a,b,n) (strncmp((a),(b),(n)) == 0)
50 //#define STRCASEEQLEN(a,b,n) (strncasecmp((a),(b),(n)) == 0)
51 //#define STRNEQLEN(a,b,n) (strncmp((a),(b),(n)) != 0)
52 //#define STRCASENEQLEN(a,b,n) (strncasecmp((a),(b),(n)) != 0)
53 #define STRPREFIX(a,b) (strncmp((a),(b),strlen((b))) == 0)
54
55 #include "c-ctype.h"
56 #include "xstrtol.h"
57
58 #include "hivex.h"
59 #include "byte_conversions.h"
60
61 static int quit = 0;
62 static int is_tty;
63 static hive_h *h = NULL;
64 static char *prompt_string = NULL; /* Normal prompt string. */
65 static char *loaded = NULL;     /* Basename of loaded file, if any. */
66 static hive_node_h cwd;         /* Current node. */
67 static int open_flags = 0;      /* Flags used when loading a hive file. */
68
69 static void usage (void) __attribute__((noreturn));
70 static void print_node_path (hive_node_h, FILE *);
71 static void set_prompt_string (void);
72 static void initialize_readline (void);
73 static void cleanup_readline (void);
74 static void add_history_line (const char *);
75 static char *rl_gets (const char *prompt_string);
76 static void sort_strings (char **strings, int len);
77 static int get_xdigit (char c);
78 static int dispatch (char *cmd, char *args);
79 static int cmd_cd (char *path);
80 static int cmd_close (char *path);
81 static int cmd_commit (char *path);
82 static int cmd_help (char *args);
83 static int cmd_load (char *hivefile);
84 static int cmd_ls (char *args);
85 static int cmd_lsval (char *args);
86 static int cmd_setval (char *args);
87
88 static void
89 usage (void)
90 {
91   fprintf (stderr, "hivexsh [-dfw] [hivefile]\n");
92   exit (EXIT_FAILURE);
93 }
94
95 int
96 main (int argc, char *argv[])
97 {
98   setlocale (LC_ALL, "");
99   bindtextdomain (PACKAGE, LOCALEBASEDIR);
100   textdomain (PACKAGE);
101
102   int c;
103   const char *filename = NULL;
104
105   set_prompt_string ();
106
107   while ((c = getopt (argc, argv, "dfw")) != EOF) {
108     switch (c) {
109     case 'd':
110       open_flags |= HIVEX_OPEN_DEBUG;
111       break;
112     case 'f':
113       filename = optarg;
114       break;
115     case 'w':
116       open_flags |= HIVEX_OPEN_WRITE;
117       break;
118     default:
119       usage ();
120     }
121   }
122
123   if (optind < argc) {
124     if (optind + 1 != argc)
125       usage ();
126     if (cmd_load (argv[optind]) == -1)
127       exit (EXIT_FAILURE);
128   }
129
130   /* -f filename parameter */
131   if (filename) {
132     close (0);
133     if (open (filename, O_RDONLY) == -1) {
134       perror (filename);
135       exit (EXIT_FAILURE);
136     }
137   }
138
139   /* Main loop. */
140   is_tty = isatty (0);
141   initialize_readline ();
142
143   if (is_tty)
144     printf (_(
145 "\n"
146 "Welcome to hivexsh, the hivex interactive shell for examining\n"
147 "Windows Registry binary hive files.\n"
148 "\n"
149 "Type: 'help' for help summary\n"
150 "      'quit' to quit the shell\n"
151 "\n"));
152
153   while (!quit) {
154     char *buf = rl_gets (prompt_string);
155     if (!buf) {
156       quit = 1;
157       if (is_tty)
158         printf ("\n");
159       break;
160     }
161
162     while (*buf && c_isspace (*buf))
163       buf++;
164
165     /* Ignore blank line. */
166     if (!*buf) continue;
167
168     /* If the next character is '#' then this is a comment. */
169     if (*buf == '#') continue;
170
171     /* Parsing is very simple - much simpler than guestfish.  This is
172      * because Registry keys often contain spaces, and we don't want
173      * to bother with quoting.  Therefore here we just split at the
174      * first whitespace into "cmd<whitespace>arg(s)".  We let the
175      * command decide how to deal with arg(s), if at all.
176      */
177     size_t len = strcspn (buf, " \t");
178
179     if (len == 0) continue;
180
181     char *cmd = buf;
182     char *args;
183     size_t i = 0;
184
185     if (buf[len] == '\0') {
186       /* This is mostly safe.  Although the cmd_* functions do sometimes
187        * modify args, then shouldn't do so when args is "".
188        */
189       args = (char *) "";
190       goto got_command;
191     }
192
193     buf[len] = '\0';
194     args = buf + len + 1 + strspn (&buf[len+1], " \t");
195
196     len = strlen (args);
197     while (len > 0 && c_isspace (args[len-1])) {
198       args[len-1] = '\0';
199       len--;
200     }
201
202   got_command:
203     /*printf ("command: '%s'  args: '%s'\n", cmd, args)*/;
204     int r = dispatch (cmd, args);
205     if (!is_tty && r == -1)
206       exit (EXIT_FAILURE);
207   }
208
209   cleanup_readline ();
210   free (prompt_string);
211   free (loaded);
212   if (h) hivex_close (h);
213   exit (0);
214 }
215
216 /* Set the prompt string.  This is called whenever it could change, eg.
217  * after loading a file or changing directory.
218  */
219 static void
220 set_prompt_string (void)
221 {
222   free (prompt_string);
223   prompt_string = NULL;
224
225   FILE *fp;
226   char *ptr;
227   size_t size;
228   fp = open_memstream (&ptr, &size);
229   if (fp == NULL) {
230     perror ("open_memstream");
231     exit (EXIT_FAILURE);
232   }
233
234   if (h) {
235     assert (loaded != NULL);
236     assert (cwd != 0);
237
238     fputs (loaded, fp);
239     print_node_path (cwd, fp);
240   }
241
242   fprintf (fp, "> ");
243   fclose (fp);
244   prompt_string = ptr;
245 }
246
247 /* Print the \full\path of a node. */
248 static void
249 print_node_path (hive_node_h node, FILE *fp)
250 {
251   hive_node_h root = hivex_root (h);
252
253   if (node == root) {
254     fputc ('\\', fp);
255     return;
256   }
257
258   hive_node_h parent = hivex_node_parent (h, node);
259   if (parent == 0) {
260     fprintf (stderr, _("hivexsh: error getting parent of node %zu\n"), node);
261     return;
262   }
263   print_node_path (parent, fp);
264
265   if (parent != root)
266     fputc ('\\', fp);
267
268   char *name = hivex_node_name (h, node);
269   if (name == NULL) {
270     fprintf (stderr, _("hivexsh: error getting node name of node %zx\n"), node);
271     return;
272   }
273
274   fputs (name, fp);
275   free (name);
276 }
277
278 static char *line_read = NULL;
279
280 static char *
281 rl_gets (const char *prompt_string)
282 {
283 #ifdef HAVE_LIBREADLINE
284
285   if (is_tty) {
286     if (line_read) {
287       free (line_read);
288       line_read = NULL;
289     }
290
291     line_read = readline (prompt_string);
292
293     if (line_read && *line_read)
294       add_history_line (line_read);
295
296     return line_read;
297   }
298
299 #endif /* HAVE_LIBREADLINE */
300
301   static char buf[8192];
302   int len;
303
304   if (is_tty)
305     printf ("%s", prompt_string);
306   line_read = fgets (buf, sizeof buf, stdin);
307
308   if (line_read) {
309     len = strlen (line_read);
310     if (len > 0 && buf[len-1] == '\n') buf[len-1] = '\0';
311   }
312
313   return line_read;
314 }
315
316 #ifdef HAVE_LIBREADLINE
317 static char histfile[1024];
318 static int nr_history_lines = 0;
319 #endif
320
321 static void
322 initialize_readline (void)
323 {
324 #ifdef HAVE_LIBREADLINE
325   const char *home;
326
327   home = getenv ("HOME");
328   if (home) {
329     snprintf (histfile, sizeof histfile, "%s/.hivexsh", home);
330     using_history ();
331     (void) read_history (histfile);
332   }
333
334   rl_readline_name = "hivexsh";
335 #endif
336 }
337
338 static void
339 cleanup_readline (void)
340 {
341 #ifdef HAVE_LIBREADLINE
342   int fd;
343
344   if (histfile[0] != '\0') {
345     fd = open (histfile, O_WRONLY|O_CREAT, 0644);
346     if (fd == -1) {
347       perror (histfile);
348       return;
349     }
350     close (fd);
351
352     (void) append_history (nr_history_lines, histfile);
353   }
354 #endif
355 }
356
357 static void
358 add_history_line (const char *line)
359 {
360 #ifdef HAVE_LIBREADLINE
361   add_history (line);
362   nr_history_lines++;
363 #endif
364 }
365
366 static int
367 compare (const void *vp1, const void *vp2)
368 {
369   char * const *p1 = (char * const *) vp1;
370   char * const *p2 = (char * const *) vp2;
371   return strcasecmp (*p1, *p2);
372 }
373
374 static void
375 sort_strings (char **strings, int len)
376 {
377   qsort (strings, len, sizeof (char *), compare);
378 }
379
380 static int
381 get_xdigit (char c)
382 {
383   switch (c) {
384   case '0'...'9': return c - '0';
385   case 'a'...'f': return c - 'a' + 10;
386   case 'A'...'F': return c - 'A' + 10;
387   default: return -1;
388   }
389 }
390
391 static int
392 dispatch (char *cmd, char *args)
393 {
394   if (STRCASEEQ (cmd, "help"))
395     return cmd_help (args);
396   else if (STRCASEEQ (cmd, "load"))
397     return cmd_load (args);
398   else if (STRCASEEQ (cmd, "exit") ||
399            STRCASEEQ (cmd, "q") ||
400            STRCASEEQ (cmd, "quit")) {
401     quit = 1;
402     return 0;
403   }
404
405   /* If no hive file is loaded (!h) then only the small selection of
406    * commands above will work.
407    */
408   if (!h) {
409     fprintf (stderr, _("hivexsh: you must load a hive file first using 'load hivefile'\n"));
410     return -1;
411   }
412
413   if (STRCASEEQ (cmd, "cd"))
414     return cmd_cd (args);
415   else if (STRCASEEQ (cmd, "close") || STRCASEEQ (cmd, "unload"))
416     return cmd_close (args);
417   else if (STRCASEEQ (cmd, "commit"))
418     return cmd_commit (args);
419   else if (STRCASEEQ (cmd, "ls"))
420     return cmd_ls (args);
421   else if (STRCASEEQ (cmd, "lsval"))
422     return cmd_lsval (args);
423   else if (STRCASEEQ (cmd, "setval"))
424     return cmd_setval (args);
425   else {
426     fprintf (stderr, _("hivexsh: unknown command '%s', use 'help' for help summary\n"),
427              cmd);
428     return -1;
429   }
430 }
431
432 static int
433 cmd_load (char *hivefile)
434 {
435   if (STREQ (hivefile, "")) {
436     fprintf (stderr, _("hivexsh: load: no hive file name given to load\n"));
437     return -1;
438   }
439
440   if (h) hivex_close (h);
441   h = NULL;
442
443   free (loaded);
444   loaded = NULL;
445
446   cwd = 0;
447
448   h = hivex_open (hivefile, open_flags);
449   if (h == NULL) {
450     fprintf (stderr,
451              _(
452 "hivexsh: failed to open hive file: %s: %m\n"
453 "\n"
454 "If you think this file is a valid Windows binary hive file (_not_\n"
455 "a regedit *.reg file) then please run this command again using the\n"
456 "hivexsh option '-d' and attach the complete output _and_ the hive file\n"
457 "which fails into a bug report at https://bugzilla.redhat.com/\n"
458 "\n"),
459              hivefile);
460     return -1;
461   }
462
463   /* Get the basename of the file for the prompt. */
464   char *p = strrchr (hivefile, '/');
465   if (p)
466     loaded = strdup (p+1);
467   else
468     loaded = strdup (hivefile);
469   if (!loaded) {
470     perror ("strdup");
471     exit (EXIT_FAILURE);
472   }
473
474   cwd = hivex_root (h);
475
476   set_prompt_string ();
477
478   return 0;
479 }
480
481 static int
482 cmd_close (char *args)
483 {
484   if (STRNEQ (args, "")) {
485     fprintf (stderr, _("hivexsh: '%s' command should not be given arguments\n"),
486              "close");
487     return -1;
488   }
489
490   if (h) hivex_close (h);
491   h = NULL;
492
493   free (loaded);
494   loaded = NULL;
495
496   cwd = 0;
497
498   set_prompt_string ();
499
500   return 0;
501 }
502
503 static int
504 cmd_commit (char *path)
505 {
506   if (STREQ (path, ""))
507     path = NULL;
508
509   if (hivex_commit (h, path, 0) == -1) {
510     perror ("hivexsh: commit");
511     return -1;
512   }
513
514   return 0;
515 }
516
517 static int
518 cmd_cd (char *path)
519 {
520   if (STREQ (path, "")) {
521     print_node_path (cwd, stdout);
522     fputc ('\n', stdout);
523     return 0;
524   }
525
526   if (path[0] == '\\' && path[1] == '\\') {
527     fprintf (stderr, _("%s: %s: \\ characters in path are doubled - are you escaping the path parameter correctly?\n"), "hivexsh", path);
528     return -1;
529   }
530
531   hive_node_h new_cwd = cwd;
532   hive_node_h root = hivex_root (h);
533
534   if (path[0] == '\\') {
535     new_cwd = root;
536     path++;
537   }
538
539   while (path[0]) {
540     size_t len = strcspn (path, "\\");
541     if (len == 0) {
542       path++;
543       continue;
544     }
545
546     char *elem = path;
547     path = path[len] == '\0' ? &path[len] : &path[len+1];
548     elem[len] = '\0';
549
550     if (len == 1 && STREQ (elem, "."))
551       continue;
552
553     if (len == 2 && STREQ (elem, "..")) {
554       if (new_cwd != root)
555         new_cwd = hivex_node_parent (h, new_cwd);
556       continue;
557     }
558
559     new_cwd = hivex_node_get_child (h, new_cwd, elem);
560     if (new_cwd == 0) {
561       fprintf (stderr, _("hivexsh: cd: subkey '%s' not found\n"),
562                elem);
563       return -1;
564     }
565   }
566
567   if (new_cwd != cwd) {
568     cwd = new_cwd;
569     set_prompt_string ();
570   }
571
572   return 0;
573 }
574
575 static int
576 cmd_help (char *args)
577 {
578   printf (_(
579 "Navigate through the hive's keys using the 'cd' command, as if it\n"
580 "contained a filesystem, and use 'ls' to list the subkeys of the\n"
581 "current key.  Full documentation is in the hivexsh(1) manual page.\n"));
582
583   return 0;
584 }
585
586 static int
587 cmd_ls (char *args)
588 {
589   if (STRNEQ (args, "")) {
590     fprintf (stderr, _("hivexsh: '%s' command should not be given arguments\n"),
591              "ls");
592     return -1;
593   }
594
595   /* Get the subkeys. */
596   hive_node_h *children = hivex_node_children (h, cwd);
597   if (children == NULL) {
598     perror ("ls");
599     return -1;
600   }
601
602   /* Get names for each subkey. */
603   size_t len;
604   for (len = 0; children[len] != 0; ++len)
605     ;
606
607   char **names = calloc (len, sizeof (char *));
608   if (names == NULL) {
609     perror ("malloc");
610     exit (EXIT_FAILURE);
611   }
612
613   int ret = -1;
614   size_t i;
615   for (i = 0; i < len; ++i) {
616     names[i] = hivex_node_name (h, children[i]);
617     if (names[i] == NULL) {
618       perror ("hivex_node_name");
619       goto error;
620     }
621   }
622
623   /* Sort the names. */
624   sort_strings (names, len);
625
626   for (i = 0; i < len; ++i)
627     printf ("%s\n", names[i]);
628
629   ret = 0;
630  error:
631   free (children);
632   for (i = 0; i < len; ++i)
633     free (names[i]);
634   free (names);
635   return ret;
636 }
637
638 static int
639 cmd_lsval (char *key)
640 {
641   if (STRNEQ (key, "")) {
642     hive_value_h value;
643
644     errno = 0;
645     if (STREQ (key, "@"))       /* default key written as "@" */
646       value = hivex_node_get_value (h, cwd, "");
647     else
648       value = hivex_node_get_value (h, cwd, key);
649
650     if (value == 0) {
651       if (errno)
652         goto error;
653       /* else key not found */
654       fprintf (stderr, _("%s: %s: key not found\n"), "hivexsh", key);
655       return -1;
656     }
657
658     /* Print the value. */
659     hive_type t;
660     size_t len;
661     if (hivex_value_type (h, value, &t, &len) == -1)
662       goto error;
663
664     switch (t) {
665     case hive_t_string:
666     case hive_t_expand_string:
667     case hive_t_link: {
668       char *str = hivex_value_string (h, value);
669       if (!str)
670         goto error;
671
672       puts (str); /* note: this adds a single \n character */
673       free (str);
674       break;
675     }
676
677     case hive_t_dword:
678     case hive_t_dword_be: {
679       int32_t j = hivex_value_dword (h, value);
680       printf ("%" PRIi32 "\n", j);
681       break;
682     }
683
684     case hive_t_qword: {
685       int64_t j = hivex_value_qword (h, value);
686       printf ("%" PRIi64 "\n", j);
687       break;
688     }
689
690     case hive_t_multiple_strings: {
691       char **strs = hivex_value_multiple_strings (h, value);
692       if (!strs)
693         goto error;
694       size_t j;
695       for (j = 0; strs[j] != NULL; ++j) {
696         puts (strs[j]);
697         free (strs[j]);
698       }
699       free (strs);
700       break;
701     }
702
703     case hive_t_none:
704     case hive_t_binary:
705     case hive_t_resource_list:
706     case hive_t_full_resource_description:
707     case hive_t_resource_requirements_list:
708     default: {
709       char *data = hivex_value_value (h, value, &t, &len);
710       if (!data)
711         goto error;
712
713       if (fwrite (data, 1, len, stdout) != len)
714         goto error;
715
716       free (data);
717       break;
718     }
719     } /* switch */
720   } else {
721     /* No key specified, so print all keys in this node.  We do this
722      * in a format which looks like the output of regedit, although
723      * this isn't a particularly useful format.
724      */
725     hive_value_h *values;
726
727     values = hivex_node_values (h, cwd);
728     if (values == NULL)
729       goto error;
730
731     size_t i;
732     for (i = 0; values[i] != 0; ++i) {
733       char *key = hivex_value_key (h, values[i]);
734       if (!key) goto error;
735
736       if (*key) {
737         putchar ('"');
738         size_t j;
739         for (j = 0; key[j] != 0; ++j) {
740           if (key[j] == '"' || key[j] == '\\')
741             putchar ('\\');
742           putchar (key[j]);
743         }
744         putchar ('"');
745       } else
746         printf ("\"@\"");       /* default key in regedit files */
747       putchar ('=');
748       free (key);
749
750       hive_type t;
751       size_t len;
752       if (hivex_value_type (h, values[i], &t, &len) == -1)
753         goto error;
754
755       switch (t) {
756       case hive_t_string:
757       case hive_t_expand_string:
758       case hive_t_link: {
759         char *str = hivex_value_string (h, values[i]);
760         if (!str)
761           goto error;
762
763         if (t != hive_t_string)
764           printf ("str(%d):", t);
765         putchar ('"');
766         size_t j;
767         for (j = 0; str[j] != 0; ++j) {
768           if (str[j] == '"' || str[j] == '\\')
769             putchar ('\\');
770           putchar (str[j]);
771         }
772         putchar ('"');
773         free (str);
774         break;
775       }
776
777       case hive_t_dword:
778       case hive_t_dword_be: {
779         int32_t j = hivex_value_dword (h, values[i]);
780         printf ("dword:%08" PRIx32 "\"", j);
781         break;
782       }
783
784       case hive_t_qword: /* sic */
785       case hive_t_none:
786       case hive_t_binary:
787       case hive_t_multiple_strings:
788       case hive_t_resource_list:
789       case hive_t_full_resource_description:
790       case hive_t_resource_requirements_list:
791       default: {
792         char *data = hivex_value_value (h, values[i], &t, &len);
793         if (!data)
794           goto error;
795
796         printf ("hex(%d):", t);
797         size_t j;
798         for (j = 0; j < len; ++j) {
799           if (j > 0)
800             putchar (',');
801           printf ("%02x", data[j]);
802         }
803         break;
804       }
805       } /* switch */
806
807       putchar ('\n');
808     } /* for */
809
810     free (values);
811   }
812
813   return 0;
814
815  error:
816   perror ("hivexsh: lsval");
817   return -1;
818 }
819
820 static int
821 cmd_setval (char *nrvals_str)
822 {
823   strtol_error xerr;
824
825   /* Parse number of values. */
826   long nrvals;
827   xerr = xstrtol (nrvals_str, NULL, 0, &nrvals, "");
828   if (xerr != LONGINT_OK) {
829     fprintf (stderr, _("%s: %s: invalid integer parameter (%s returned %d)\n"),
830              "setval", "nrvals", "xstrtol", xerr);
831     return -1;
832   }
833   if (nrvals < 0 || nrvals > 1000) {
834     fprintf (stderr, _("%s: %s: integer out of range\n"),
835              "setval", "nrvals");
836     return -1;
837   }
838
839   struct hive_set_value *values =
840     calloc (nrvals, sizeof (struct hive_set_value));
841   if (values == NULL) {
842     perror ("calloc");
843     exit (EXIT_FAILURE);
844   }
845
846   int ret = -1;
847
848   /* Read nrvals * 2 lines of input, nrvals * (key, value) pairs, as
849    * explained in the man page.
850    */
851   int prompt = isatty (0) ? 2 : 0;
852   int i, j;
853   for (i = 0; i < nrvals; ++i) {
854     /* Read key. */
855     char *buf = rl_gets ("  key> ");
856     if (!buf) {
857       fprintf (stderr, _("hivexsh: setval: unexpected end of input\n"));
858       quit = 1;
859       goto error;
860     }
861
862     /* Note that buf will be overwritten by the next call to rl_gets. */
863     if (STREQ (buf, "@"))
864       values[i].key = strdup ("");
865     else
866       values[i].key = strdup (buf);
867     if (values[i].key == NULL) {
868       perror ("strdup");
869       exit (EXIT_FAILURE);
870     }
871
872     /* Read value. */
873     buf = rl_gets ("value> ");
874     if (!buf) {
875       fprintf (stderr, _("hivexsh: setval: unexpected end of input\n"));
876       quit = 1;
877       goto error;
878     }
879
880     if (STREQ (buf, "none")) {
881       values[i].t = hive_t_none;
882       values[i].len = 0;
883     }
884     else if (STRPREFIX (buf, "string:")) {
885       buf += 7;
886       values[i].t = hive_t_string;
887       int nr_chars = strlen (buf);
888       values[i].len = 2 * (nr_chars + 1);
889       values[i].value = malloc (values[i].len);
890       if (!values[i].value) {
891         perror ("malloc");
892         exit (EXIT_FAILURE);
893       }
894       for (j = 0; j <= /* sic */ nr_chars; ++j) {
895         if (buf[j] & 0x80) {
896           fprintf (stderr, _("hivexsh: string(utf16le): only 7 bit ASCII strings are supported for input\n"));
897           goto error;
898         }
899         values[i].value[2*j] = buf[j];
900         values[i].value[2*j+1] = '\0';
901       }
902     }
903     else if (STRPREFIX (buf, "expandstring:")) {
904       buf += 13;
905       values[i].t = hive_t_string;
906       int nr_chars = strlen (buf);
907       values[i].len = 2 * (nr_chars + 1);
908       values[i].value = malloc (values[i].len);
909       if (!values[i].value) {
910         perror ("malloc");
911         exit (EXIT_FAILURE);
912       }
913       for (j = 0; j <= /* sic */ nr_chars; ++j) {
914         if (buf[j] & 0x80) {
915           fprintf (stderr, _("hivexsh: string(utf16le): only 7 bit ASCII strings are supported for input\n"));
916           goto error;
917         }
918         values[i].value[2*j] = buf[j];
919         values[i].value[2*j+1] = '\0';
920       }
921     }
922     else if (STRPREFIX (buf, "dword:")) {
923       buf += 6;
924       values[i].t = hive_t_dword;
925       values[i].len = 4;
926       values[i].value = malloc (4);
927       if (!values[i].value) {
928         perror ("malloc");
929         exit (EXIT_FAILURE);
930       }
931       long n;
932       xerr = xstrtol (buf, NULL, 0, &n, "");
933       if (xerr != LONGINT_OK) {
934         fprintf (stderr, _("%s: %s: invalid integer parameter (%s returned %d)\n"),
935                  "setval", "dword", "xstrtol", xerr);
936         goto error;
937       }
938       if (n < 0 || n > UINT32_MAX) {
939         fprintf (stderr, _("%s: %s: integer out of range\n"),
940                  "setval", "dword");
941         goto error;
942       }
943       uint32_t u32 = htole32 (n);
944       memcpy (values[i].value, &u32, 4);
945     }
946     else if (STRPREFIX (buf, "qword:")) {
947       buf += 6;
948       values[i].t = hive_t_qword;
949       values[i].len = 8;
950       values[i].value = malloc (8);
951       if (!values[i].value) {
952         perror ("malloc");
953         exit (EXIT_FAILURE);
954       }
955       long long n;
956       xerr = xstrtoll (buf, NULL, 0, &n, "");
957       if (xerr != LONGINT_OK) {
958         fprintf (stderr, _("%s: %s: invalid integer parameter (%s returned %d)\n"),
959                  "setval", "dword", "xstrtoll", xerr);
960         goto error;
961       }
962 #if 0
963       if (n < 0 || n > UINT64_MAX) {
964         fprintf (stderr, _("%s: %s: integer out of range\n"),
965                  "setval", "dword");
966         goto error;
967       }
968 #endif
969       uint64_t u64 = htole64 (n);
970       memcpy (values[i].value, &u64, 4);
971     }
972     else if (STRPREFIX (buf, "hex:")) {
973       /* Read the type. */
974       buf += 4;
975       size_t len = strcspn (buf, ":");
976       char *nextbuf;
977       if (buf[len] == '\0')     /* "hex:t" */
978         nextbuf = &buf[len];
979       else {                    /* "hex:t:..." */
980         buf[len] = '\0';
981         nextbuf = &buf[len+1];
982       }
983
984       long t;
985       xerr = xstrtol (buf, NULL, 0, &t, "");
986       if (xerr != LONGINT_OK) {
987         fprintf (stderr, _("%s: %s: invalid integer parameter (%s returned %d)\n"),
988                  "setval", "hex", "xstrtol", xerr);
989         goto error;
990       }
991       if (t < 0 || t > UINT32_MAX) {
992         fprintf (stderr, _("%s: %s: integer out of range\n"),
993                  "setval", "hex");
994         goto error;
995       }
996       values[i].t = t;
997
998       /* Read the hex data. */
999       buf = nextbuf;
1000
1001       /* The allocation length is an overestimate, but it doesn't matter. */
1002       values[i].value = malloc (1 + strlen (buf) / 2);
1003       if (!values[i].value) {
1004         perror ("malloc");
1005         exit (EXIT_FAILURE);
1006       }
1007       values[i].len = 0;
1008
1009       while (*buf) {
1010         int c = 0;
1011
1012         for (j = 0; *buf && j < 2; buf++) {
1013           if (c_isxdigit (*buf)) { /* NB: ignore non-hex digits. */
1014             c <<= 4;
1015             c |= get_xdigit (*buf);
1016             j++;
1017           }
1018         }
1019
1020         if (j == 2) values[i].value[values[i].len++] = c;
1021         else if (j == 1) {
1022           fprintf (stderr, _("hivexsh: setval: trailing garbage after hex string\n"));
1023           goto error;
1024         }
1025       }
1026     }
1027     else {
1028       fprintf (stderr,
1029                _("hivexsh: setval: cannot parse value string, please refer to the man page hivexsh(1) for help: %s\n"),
1030                buf);
1031       goto error;
1032     }
1033   }
1034
1035   ret = hivex_node_set_values (h, cwd, nrvals, values, 0);
1036
1037  error:
1038   /* Free values array. */
1039   for (i = 0; i < nrvals; ++i) {
1040     free (values[i].key);
1041     free (values[i].value);
1042   }
1043   free (values);
1044
1045   return ret;
1046 }