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