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