Guestfish: Add 'reopen' command to reopen the libguestfs handle.
[libguestfs.git] / fish / fish.c
1 /* guestfish - the filesystem interactive 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
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18
19 #include <config.h>
20
21 #define _GNU_SOURCE // for strchrnul
22
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <unistd.h>
27 #include <fcntl.h>
28 #include <getopt.h>
29 #include <signal.h>
30 #include <assert.h>
31 #include <ctype.h>
32 #include <sys/types.h>
33 #include <sys/wait.h>
34
35 #ifdef HAVE_LIBREADLINE
36 #include <readline/readline.h>
37 #include <readline/history.h>
38 #endif
39
40 #include <guestfs.h>
41
42 #include "fish.h"
43
44 struct mp {
45   struct mp *next;
46   char *device;
47   char *mountpoint;
48 };
49
50 struct drv {
51   struct drv *next;
52   char *filename;
53 };
54
55 static void add_drives (struct drv *drv);
56 static void mount_mps (struct mp *mp);
57 static void interactive (void);
58 static void shell_script (void);
59 static void script (int prompt);
60 static void cmdline (char *argv[], int optind, int argc);
61 static void initialize_readline (void);
62 static void cleanup_readline (void);
63 static void add_history_line (const char *);
64
65 /* Currently open libguestfs handle. */
66 guestfs_h *g;
67
68 int read_only = 0;
69 int quit = 0;
70 int verbose = 0;
71
72 int
73 launch (guestfs_h *_g)
74 {
75   assert (_g == g);
76
77   if (guestfs_is_config (g)) {
78     if (guestfs_launch (g) == -1)
79       return -1;
80     if (guestfs_wait_ready (g) == -1)
81       return -1;
82   }
83   return 0;
84 }
85
86 static void
87 usage (void)
88 {
89   fprintf (stderr,
90            _("guestfish: guest filesystem shell\n"
91              "guestfish lets you edit virtual machine filesystems\n"
92              "Copyright (C) 2009 Red Hat Inc.\n"
93              "Usage:\n"
94              "  guestfish [--options] cmd [: cmd : cmd ...]\n"
95              "  guestfish -i libvirt-domain\n"
96              "  guestfish -i disk-image(s)\n"
97              "or for interactive use:\n"
98              "  guestfish\n"
99              "or from a shell script:\n"
100              "  guestfish <<EOF\n"
101              "  cmd\n"
102              "  ...\n"
103              "  EOF\n"
104              "Options:\n"
105              "  -h|--cmd-help        List available commands\n"
106              "  -h|--cmd-help cmd    Display detailed help on 'cmd'\n"
107              "  -a|--add image       Add image\n"
108              "  -D|--no-dest-paths   Don't tab-complete paths from guest fs\n"
109              "  -f|--file file       Read commands from file\n"
110              "  -i|--inspector       Run virt-inspector to get disk mountpoints\n"
111              "  -m|--mount dev[:mnt] Mount dev on mnt (if omitted, /)\n"
112              "  -n|--no-sync         Don't autosync\n"
113              "  -r|--ro              Mount read-only\n"
114              "  -v|--verbose         Verbose messages\n"
115              "  -V|--version         Display version and exit\n"
116              "For more information,  see the manpage guestfish(1).\n"));
117 }
118
119 int
120 main (int argc, char *argv[])
121 {
122   static const char *options = "a:f:h::im:nrv?V";
123   static struct option long_options[] = {
124     { "add", 1, 0, 'a' },
125     { "cmd-help", 2, 0, 'h' },
126     { "file", 1, 0, 'f' },
127     { "help", 0, 0, '?' },
128     { "inspector", 0, 0, 'i' },
129     { "mount", 1, 0, 'm' },
130     { "no-dest-paths", 0, 0, 'D' },
131     { "no-sync", 0, 0, 'n' },
132     { "ro", 0, 0, 'r' },
133     { "verbose", 0, 0, 'v' },
134     { "version", 0, 0, 'V' },
135     { 0, 0, 0, 0 }
136   };
137   struct drv *drvs = NULL;
138   struct drv *drv;
139   struct mp *mps = NULL;
140   struct mp *mp;
141   char *p, *file = NULL;
142   int c, inspector = 0;
143   struct sigaction sa;
144
145   initialize_readline ();
146
147   memset (&sa, 0, sizeof sa);
148   sa.sa_handler = SIG_IGN;
149   sa.sa_flags = SA_RESTART;
150   sigaction (SIGPIPE, &sa, NULL);
151
152   /* guestfs_create is meant to be a lightweight operation, so
153    * it's OK to do it early here.
154    */
155   g = guestfs_create ();
156   if (g == NULL) {
157     fprintf (stderr, _("guestfs_create: failed to create handle\n"));
158     exit (1);
159   }
160
161   guestfs_set_autosync (g, 1);
162
163   /* If developing, add ./appliance to the path.  Note that libtools
164    * interferes with this because uninstalled guestfish is a shell
165    * script that runs the real program with an absolute path.  Detect
166    * that too.
167    *
168    * BUT if LIBGUESTFS_PATH environment variable is already set by
169    * the user, then don't override it.
170    */
171   if (getenv ("LIBGUESTFS_PATH") == NULL &&
172       argv[0] &&
173       (argv[0][0] != '/' || strstr (argv[0], "/.libs/lt-") != NULL))
174     guestfs_set_path (g, "appliance:" GUESTFS_DEFAULT_PATH);
175
176   for (;;) {
177     c = getopt_long (argc, argv, options, long_options, NULL);
178     if (c == -1) break;
179
180     switch (c) {
181     case 'a':
182       if (access (optarg, R_OK) != 0) {
183         perror (optarg);
184         exit (1);
185       }
186       drv = malloc (sizeof (struct drv));
187       if (!drv) {
188         perror ("malloc");
189         exit (1);
190       }
191       drv->filename = optarg;
192       drv->next = drvs;
193       drvs = drv;
194       break;
195
196     case 'D':
197       complete_dest_paths = 0;
198       break;
199
200     case 'f':
201       if (file) {
202         fprintf (stderr, _("guestfish: only one -f parameter can be given\n"));
203         exit (1);
204       }
205       file = optarg;
206       break;
207
208     case 'h':
209       if (optarg)
210         display_command (optarg);
211       else if (argv[optind] && argv[optind][0] != '-')
212         display_command (argv[optind++]);
213       else
214         list_commands ();
215       exit (0);
216
217     case 'i':
218       inspector = 1;
219       break;
220
221     case 'm':
222       mp = malloc (sizeof (struct mp));
223       if (!mp) {
224         perror ("malloc");
225         exit (1);
226       }
227       p = strchr (optarg, ':');
228       if (p) {
229         *p = '\0';
230         mp->mountpoint = p+1;
231       } else
232         mp->mountpoint = "/";
233       mp->device = optarg;
234       mp->next = mps;
235       mps = mp;
236       break;
237
238     case 'n':
239       guestfs_set_autosync (g, 0);
240       break;
241
242     case 'r':
243       read_only = 1;
244       break;
245
246     case 'v':
247       verbose++;
248       guestfs_set_verbose (g, verbose);
249       break;
250
251     case 'V':
252       printf ("guestfish %s\n", PACKAGE_VERSION);
253       exit (0);
254
255     case '?':
256       usage ();
257       exit (0);
258
259     default:
260       fprintf (stderr, _("guestfish: unexpected command line option 0x%x\n"),
261                c);
262       exit (1);
263     }
264   }
265
266   /* Inspector mode invalidates most of the other arguments. */
267   if (inspector) {
268     char cmd[1024];
269     int r;
270
271     if (drvs || mps) {
272       fprintf (stderr, _("guestfish: cannot use -i option with -a or -m\n"));
273       exit (1);
274     }
275     if (optind >= argc) {
276       fprintf (stderr, _("guestfish -i requires a libvirt domain or path(s) to disk image(s)\n"));
277       exit (1);
278     }
279
280     strcpy (cmd, "a=`virt-inspector");
281     while (optind < argc) {
282       if (strlen (cmd) + strlen (argv[optind]) + strlen (argv[0]) + 60
283           >= sizeof cmd) {
284         fprintf (stderr, _("guestfish: virt-inspector command too long for fixed-size buffer\n"));
285         exit (1);
286       }
287       strcat (cmd, " '");
288       strcat (cmd, argv[optind]);
289       strcat (cmd, "'");
290       optind++;
291     }
292
293     if (read_only)
294       strcat (cmd, " --ro-fish");
295     else
296       strcat (cmd, " --fish");
297
298     sprintf (&cmd[strlen(cmd)], "` && %s $a", argv[0]);
299
300     if (guestfs_get_verbose (g))
301       strcat (cmd, " -v");
302     if (!guestfs_get_autosync (g))
303       strcat (cmd, " -n");
304
305     /*printf ("%s\n", cmd);*/
306
307     r = system (cmd);
308     if (r == -1) {
309       perror ("system");
310       exit (1);
311     }
312     exit (WEXITSTATUS (r));
313   }
314
315   /* If we've got drives to add, add them now. */
316   add_drives (drvs);
317
318   /* If we've got mountpoints, we must launch the guest and mount them. */
319   if (mps != NULL) {
320     if (launch (g) == -1) exit (1);
321     mount_mps (mps);
322   }
323
324   /* -f (file) parameter? */
325   if (file) {
326     close (0);
327     if (open (file, O_RDONLY) == -1) {
328       perror (file);
329       exit (1);
330     }
331   }
332
333   /* Interactive, shell script, or command(s) on the command line? */
334   if (optind >= argc) {
335     if (isatty (0))
336       interactive ();
337     else
338       shell_script ();
339   }
340   else
341     cmdline (argv, optind, argc);
342
343   cleanup_readline ();
344
345   exit (0);
346 }
347
348 void
349 pod2text (const char *heading, const char *str)
350 {
351   FILE *fp;
352
353   fp = popen ("pod2text", "w");
354   if (fp == NULL) {
355     /* pod2text failed, maybe not found, so let's just print the
356      * source instead, since that's better than doing nothing.
357      */
358     printf ("%s\n\n%s\n", heading, str);
359     return;
360   }
361   fputs ("=head1 ", fp);
362   fputs (heading, fp);
363   fputs ("\n\n", fp);
364   fputs (str, fp);
365   pclose (fp);
366 }
367
368 /* List is built in reverse order, so mount them in reverse order. */
369 static void
370 mount_mps (struct mp *mp)
371 {
372   int r;
373
374   if (mp) {
375     mount_mps (mp->next);
376     if (!read_only)
377       r = guestfs_mount (g, mp->device, mp->mountpoint);
378     else
379       r = guestfs_mount_ro (g, mp->device, mp->mountpoint);
380     if (r == -1)
381       exit (1);
382   }
383 }
384
385 static void
386 add_drives (struct drv *drv)
387 {
388   int r;
389
390   if (drv) {
391     add_drives (drv->next);
392     if (!read_only)
393       r = guestfs_add_drive (g, drv->filename);
394     else
395       r = guestfs_add_drive_ro (g, drv->filename);
396     if (r == -1)
397       exit (1);
398   }
399 }
400
401 static void
402 interactive (void)
403 {
404   script (1);
405 }
406
407 static void
408 shell_script (void)
409 {
410   script (0);
411 }
412
413 #define FISH "><fs> "
414
415 static char *line_read = NULL;
416
417 static char *
418 rl_gets (int prompt)
419 {
420 #ifdef HAVE_LIBREADLINE
421
422   if (prompt) {
423     if (line_read) {
424       free (line_read);
425       line_read = NULL;
426     }
427
428     line_read = readline (prompt ? FISH : "");
429
430     if (line_read && *line_read)
431       add_history_line (line_read);
432
433     return line_read;
434   }
435
436 #endif /* HAVE_LIBREADLINE */
437
438   static char buf[8192];
439   int len;
440
441   if (prompt) printf (FISH);
442   line_read = fgets (buf, sizeof buf, stdin);
443
444   if (line_read) {
445     len = strlen (line_read);
446     if (len > 0 && buf[len-1] == '\n') buf[len-1] = '\0';
447   }
448
449   return line_read;
450 }
451
452 static void
453 script (int prompt)
454 {
455   char *buf;
456   char *cmd;
457   char *p, *pend;
458   char *argv[64];
459   int i, len;
460   int global_exit_on_error = !prompt;
461   int exit_on_error;
462
463   if (prompt)
464     printf (_("\n"
465               "Welcome to guestfish, the libguestfs filesystem interactive shell for\n"
466               "editing virtual machine filesystems.\n"
467               "\n"
468               "Type: 'help' for help with commands\n"
469               "      'quit' to quit the shell\n"
470               "\n"));
471
472   while (!quit) {
473     char *pipe = NULL;
474
475     exit_on_error = global_exit_on_error;
476
477     buf = rl_gets (prompt);
478     if (!buf) {
479       quit = 1;
480       break;
481     }
482
483     /* Skip any initial whitespace before the command. */
484   again:
485     while (*buf && isspace (*buf))
486       buf++;
487
488     if (!*buf) continue;
489
490     /* If the next character is '#' then this is a comment. */
491     if (*buf == '#') continue;
492
493     /* If the next character is '!' then pass the whole lot to system(3). */
494     if (*buf == '!') {
495       int r;
496
497       r = system (buf+1);
498       if (exit_on_error) {
499         if (r == -1 ||
500             (WIFSIGNALED (r) &&
501              (WTERMSIG (r) == SIGINT || WTERMSIG (r) == SIGQUIT)) ||
502             WEXITSTATUS (r) != 0)
503           exit (1);
504       }
505       continue;
506     }
507
508     /* If the next character is '-' allow the command to fail without
509      * exiting on error (just for this one command though).
510      */
511     if (*buf == '-') {
512       exit_on_error = 0;
513       buf++;
514       goto again;
515     }
516
517     /* Get the command (cannot be quoted). */
518     len = strcspn (buf, " \t");
519
520     if (len == 0) continue;
521
522     cmd = buf;
523     i = 0;
524     if (buf[len] == '\0') {
525       argv[0] = NULL;
526       goto got_command;
527     }
528
529     buf[len] = '\0';
530     p = &buf[len+1];
531     p += strspn (p, " \t");
532
533     /* Get the parameters. */
534     while (*p && i < sizeof argv / sizeof argv[0]) {
535       /* Parameters which start with quotes or pipes are treated
536        * specially.  Bare parameters are delimited by whitespace.
537        */
538       if (*p == '"') {
539         p++;
540         len = strcspn (p, "\"");
541         if (p[len] == '\0') {
542           fprintf (stderr, _("guestfish: unterminated double quote\n"));
543           if (exit_on_error) exit (1);
544           goto next_command;
545         }
546         if (p[len+1] && (p[len+1] != ' ' && p[len+1] != '\t')) {
547           fprintf (stderr, _("guestfish: command arguments not separated by whitespace\n"));
548           if (exit_on_error) exit (1);
549           goto next_command;
550         }
551         p[len] = '\0';
552         pend = p[len+1] ? &p[len+2] : &p[len+1];
553       } else if (*p == '\'') {
554         p++;
555         len = strcspn (p, "'");
556         if (p[len] == '\0') {
557           fprintf (stderr, _("guestfish: unterminated single quote\n"));
558           if (exit_on_error) exit (1);
559           goto next_command;
560         }
561         if (p[len+1] && (p[len+1] != ' ' && p[len+1] != '\t')) {
562           fprintf (stderr, _("guestfish: command arguments not separated by whitespace\n"));
563           if (exit_on_error) exit (1);
564           goto next_command;
565         }
566         p[len] = '\0';
567         pend = p[len+1] ? &p[len+2] : &p[len+1];
568       } else if (*p == '|') {
569         *p = '\0';
570         pipe = p+1;
571         continue;
572         /*
573       } else if (*p == '[') {
574         int c = 1;
575         p++;
576         pend = p;
577         while (*pend && c != 0) {
578           if (*pend == '[') c++;
579           else if (*pend == ']') c--;
580           pend++;
581         }
582         if (c != 0) {
583           fprintf (stderr, _("guestfish: unterminated \"[...]\" sequence\n"));
584           if (exit_on_error) exit (1);
585           goto next_command;
586         }
587         if (*pend && (*pend != ' ' && *pend != '\t')) {
588           fprintf (stderr, _("guestfish: command arguments not separated by whitespace\n"));
589           if (exit_on_error) exit (1);
590           goto next_command;
591         }
592         *(pend-1) = '\0';
593         */
594       } else if (*p != ' ' && *p != '\t') {
595         len = strcspn (p, " \t");
596         if (p[len]) {
597           p[len] = '\0';
598           pend = &p[len+1];
599         } else
600           pend = &p[len];
601       } else {
602         fprintf (stderr, _("guestfish: internal error parsing string at '%s'\n"),
603                  p);
604         abort ();
605       }
606
607       argv[i++] = p;
608       p = pend;
609
610       if (*p)
611         p += strspn (p, " \t");
612     }
613
614     if (i == sizeof argv / sizeof argv[0]) {
615       fprintf (stderr, _("guestfish: too many arguments\n"));
616       if (exit_on_error) exit (1);
617       goto next_command;
618     }
619
620     argv[i] = NULL;
621
622   got_command:
623     if (issue_command (cmd, argv, pipe) == -1) {
624       if (exit_on_error) exit (1);
625     }
626
627   next_command:;
628   }
629   if (prompt) printf ("\n");
630 }
631
632 static void
633 cmdline (char *argv[], int optind, int argc)
634 {
635   const char *cmd;
636   char **params;
637
638   if (optind >= argc) return;
639
640   cmd = argv[optind++];
641   if (strcmp (cmd, ":") == 0) {
642     fprintf (stderr, _("guestfish: empty command on command line\n"));
643     exit (1);
644   }
645   params = &argv[optind];
646
647   /* Search for end of command list or ":" ... */
648   while (optind < argc && strcmp (argv[optind], ":") != 0)
649     optind++;
650
651   if (optind == argc) {
652     if (issue_command (cmd, params, NULL) == -1) exit (1);
653   } else {
654     argv[optind] = NULL;
655     if (issue_command (cmd, params, NULL) == -1) exit (1);
656     cmdline (argv, optind+1, argc);
657   }
658 }
659
660 int
661 issue_command (const char *cmd, char *argv[], const char *pipecmd)
662 {
663   int argc;
664   int stdout_saved_fd = -1;
665   int pid = 0;
666   int r;
667
668   /* For | ... commands.  Annoyingly we can't use popen(3) here. */
669   if (pipecmd) {
670     int fd[2];
671
672     fflush (stdout);
673     pipe (fd);
674     pid = fork ();
675     if (pid == -1) {
676       perror ("fork");
677       return -1;
678     }
679
680     if (pid == 0) {             /* Child process. */
681       close (fd[1]);
682       dup2 (fd[0], 0);
683
684       r = system (pipecmd);
685       if (r == -1) {
686         perror (pipecmd);
687         _exit (1);
688       }
689       _exit (WEXITSTATUS (r));
690     }
691
692     stdout_saved_fd = dup (1);
693     close (fd[0]);
694     dup2 (fd[1], 1);
695     close (fd[1]);
696   }
697
698   for (argc = 0; argv[argc] != NULL; ++argc)
699     ;
700
701   if (strcasecmp (cmd, "help") == 0) {
702     if (argc == 0)
703       list_commands ();
704     else
705       display_command (argv[0]);
706     r = 0;
707   }
708   else if (strcasecmp (cmd, "quit") == 0 ||
709            strcasecmp (cmd, "exit") == 0 ||
710            strcasecmp (cmd, "q") == 0) {
711     quit = 1;
712     r = 0;
713   }
714   else if (strcasecmp (cmd, "alloc") == 0 ||
715            strcasecmp (cmd, "allocate") == 0)
716     r = do_alloc (cmd, argc, argv);
717   else if (strcasecmp (cmd, "echo") == 0)
718     r = do_echo (cmd, argc, argv);
719   else if (strcasecmp (cmd, "edit") == 0 ||
720            strcasecmp (cmd, "vi") == 0 ||
721            strcasecmp (cmd, "emacs") == 0)
722     r = do_edit (cmd, argc, argv);
723   else if (strcasecmp (cmd, "lcd") == 0)
724     r = do_lcd (cmd, argc, argv);
725   else if (strcasecmp (cmd, "glob") == 0)
726     r = do_glob (cmd, argc, argv);
727   else if (strcasecmp (cmd, "more") == 0 ||
728            strcasecmp (cmd, "less") == 0)
729     r = do_more (cmd, argc, argv);
730   else if (strcasecmp (cmd, "reopen") == 0)
731     r = do_reopen (cmd, argc, argv);
732   else if (strcasecmp (cmd, "time") == 0)
733     r = do_time (cmd, argc, argv);
734   else
735     r = run_action (cmd, argc, argv);
736
737   /* Always flush stdout after every command, so that messages, results
738    * etc appear immediately.
739    */
740   fflush (stdout);
741
742   if (pipecmd) {
743     close (1);
744     dup2 (stdout_saved_fd, 1);
745     close (stdout_saved_fd);
746     waitpid (pid, NULL, 0);
747   }
748
749   return r;
750 }
751
752 void
753 list_builtin_commands (void)
754 {
755   /* help and quit should appear at the top */
756   printf ("%-20s %s\n",
757           "help", _("display a list of commands or help on a command"));
758   printf ("%-20s %s\n",
759           "quit", _("quit guestfish"));
760
761   printf ("%-20s %s\n",
762           "alloc", _("allocate an image"));
763   printf ("%-20s %s\n",
764           "echo", _("display a line of text"));
765   printf ("%-20s %s\n",
766           "edit", _("edit a file in the image"));
767   printf ("%-20s %s\n",
768           "lcd", _("local change directory"));
769   printf ("%-20s %s\n",
770           "glob", _("expand wildcards in command"));
771   printf ("%-20s %s\n",
772           "more", _("view a file in the pager"));
773   printf ("%-20s %s\n",
774           "reopen", _("close and reopen libguestfs handle"));
775   printf ("%-20s %s\n",
776           "time", _("measure time taken to run command"));
777
778   /* actions are printed after this (see list_commands) */
779 }
780
781 void
782 display_builtin_command (const char *cmd)
783 {
784   /* help for actions is auto-generated, see display_command */
785
786   if (strcasecmp (cmd, "alloc") == 0 ||
787       strcasecmp (cmd, "allocate") == 0)
788     printf (_("alloc - allocate an image\n"
789               "     alloc <filename> <size>\n"
790               "\n"
791               "    This creates an empty (zeroed) file of the given size,\n"
792               "    and then adds so it can be further examined.\n"
793               "\n"
794               "    For more advanced image creation, see qemu-img utility.\n"
795               "\n"
796               "    Size can be specified (where <nn> means a number):\n"
797               "    <nn>             number of kilobytes\n"
798               "      eg: 1440       standard 3.5\" floppy\n"
799               "    <nn>K or <nn>KB  number of kilobytes\n"
800               "    <nn>M or <nn>MB  number of megabytes\n"
801               "    <nn>G or <nn>GB  number of gigabytes\n"
802               "    <nn>sects        number of 512 byte sectors\n"));
803   else if (strcasecmp (cmd, "echo") == 0)
804     printf (_("echo - display a line of text\n"
805               "     echo [<params> ...]\n"
806               "\n"
807               "    This echos the parameters to the terminal.\n"));
808   else if (strcasecmp (cmd, "edit") == 0 ||
809            strcasecmp (cmd, "vi") == 0 ||
810            strcasecmp (cmd, "emacs") == 0)
811     printf (_("edit - edit a file in the image\n"
812               "     edit <filename>\n"
813               "\n"
814               "    This is used to edit a file.\n"
815               "\n"
816               "    It is the equivalent of (and is implemented by)\n"
817               "    running \"cat\", editing locally, and then \"write-file\".\n"
818               "\n"
819               "    Normally it uses $EDITOR, but if you use the aliases\n"
820               "    \"vi\" or \"emacs\" you will get those editors.\n"
821               "\n"
822               "    NOTE: This will not work reliably for large files\n"
823               "    (> 2 MB) or binary files containing \\0 bytes.\n"));
824   else if (strcasecmp (cmd, "lcd") == 0)
825     printf (_("lcd - local change directory\n"
826               "    lcd <directory>\n"
827               "\n"
828               "    Change guestfish's current directory. This command is\n"
829               "    useful if you want to download files to a particular\n"
830               "    place.\n"));
831   else if (strcasecmp (cmd, "glob") == 0)
832     printf (_("glob - expand wildcards in command\n"
833               "    glob <command> [<args> ...]\n"
834               "\n"
835               "    Glob runs <command> with wildcards expanded in any\n"
836               "    command args.  Note that the command is run repeatedly\n"
837               "    once for each expanded argument.\n"));
838   else if (strcasecmp (cmd, "help") == 0)
839     printf (_("help - display a list of commands or help on a command\n"
840               "     help cmd\n"
841               "     help\n"));
842   else if (strcasecmp (cmd, "more") == 0 ||
843            strcasecmp (cmd, "less") == 0)
844     printf (_("more - view a file in the pager\n"
845               "     more <filename>\n"
846               "\n"
847               "    This is used to view a file in the pager.\n"
848               "\n"
849               "    It is the equivalent of (and is implemented by)\n"
850               "    running \"cat\" and using the pager.\n"
851               "\n"
852               "    Normally it uses $PAGER, but if you use the alias\n"
853               "    \"less\" then it always uses \"less\".\n"
854               "\n"
855               "    NOTE: This will not work reliably for large files\n"
856               "    (> 2 MB) or binary files containing \\0 bytes.\n"));
857   else if (strcasecmp (cmd, "quit") == 0 ||
858            strcasecmp (cmd, "exit") == 0 ||
859            strcasecmp (cmd, "q") == 0)
860     printf (_("quit - quit guestfish\n"
861               "     quit\n"));
862   else if (strcasecmp (cmd, "reopen") == 0)
863     printf (_("reopen - close and reopen the libguestfs handle\n"
864               "     reopen\n"
865               "\n"
866               "Close and reopen the libguestfs handle.  It is not necessary to use\n"
867               "this normally, because the handle is closed properly when guestfish\n"
868               "exits.  However this is occasionally useful for testing.\n"));
869   else if (strcasecmp (cmd, "time") == 0)
870     printf (_("time - measure time taken to run command\n"
871               "    time <command> [<args> ...]\n"
872               "\n"
873               "    This runs <command> as usual, and prints the elapsed\n"
874               "    time afterwards.\n"));
875   else
876     fprintf (stderr, _("%s: command not known, use -h to list all commands\n"),
877              cmd);
878 }
879
880 void
881 free_strings (char **argv)
882 {
883   int argc;
884
885   for (argc = 0; argv[argc] != NULL; ++argc)
886     free (argv[argc]);
887   free (argv);
888 }
889
890 int
891 count_strings (char * const * const argv)
892 {
893   int c;
894
895   for (c = 0; argv[c]; ++c)
896     ;
897   return c;
898 }
899
900 void
901 print_strings (char * const * const argv)
902 {
903   int argc;
904
905   for (argc = 0; argv[argc] != NULL; ++argc)
906     printf ("%s\n", argv[argc]);
907 }
908
909 void
910 print_table (char * const * const argv)
911 {
912   int i;
913
914   for (i = 0; argv[i] != NULL; i += 2)
915     printf ("%s: %s\n", argv[i], argv[i+1]);
916 }
917
918 int
919 is_true (const char *str)
920 {
921   return
922     strcasecmp (str, "0") != 0 &&
923     strcasecmp (str, "f") != 0 &&
924     strcasecmp (str, "false") != 0 &&
925     strcasecmp (str, "n") != 0 &&
926     strcasecmp (str, "no") != 0;
927 }
928
929 /* XXX We could improve list parsing. */
930 char **
931 parse_string_list (const char *str)
932 {
933   char **argv;
934   const char *p, *pend;
935   int argc, i;
936
937   argc = 1;
938   for (i = 0; str[i]; ++i)
939     if (str[i] == ' ') argc++;
940
941   argv = malloc (sizeof (char *) * (argc+1));
942   if (argv == NULL) { perror ("malloc"); exit (1); }
943
944   p = str;
945   i = 0;
946   while (*p) {
947     pend = strchrnul (p, ' ');
948     argv[i] = strndup (p, pend-p);
949     i++;
950     p = *pend == ' ' ? pend+1 : pend;
951   }
952   argv[i] = NULL;
953
954   return argv;
955 }
956
957 #ifdef HAVE_LIBREADLINE
958 static char histfile[1024];
959 static int nr_history_lines = 0;
960 #endif
961
962 static void
963 initialize_readline (void)
964 {
965 #ifdef HAVE_LIBREADLINE
966   const char *home;
967
968   home = getenv ("HOME");
969   if (home) {
970     snprintf (histfile, sizeof histfile, "%s/.guestfish", home);
971     using_history ();
972     (void) read_history (histfile);
973   }
974
975   rl_readline_name = "guestfish";
976   rl_attempted_completion_function = do_completion;
977 #endif
978 }
979
980 static void
981 cleanup_readline (void)
982 {
983 #ifdef HAVE_LIBREADLINE
984   int fd;
985
986   if (histfile[0] != '\0') {
987     fd = open (histfile, O_WRONLY|O_CREAT, 0644);
988     if (fd == -1) {
989       perror (histfile);
990       return;
991     }
992     close (fd);
993
994     (void) append_history (nr_history_lines, histfile);
995   }
996 #endif
997 }
998
999 static void
1000 add_history_line (const char *line)
1001 {
1002 #ifdef HAVE_LIBREADLINE
1003   add_history (line);
1004   nr_history_lines++;
1005 #endif
1006 }
1007
1008 int
1009 xwrite (int fd, const void *buf, size_t len)
1010 {
1011   int r;
1012
1013   while (len > 0) {
1014     r = write (fd, buf, len);
1015     if (r == -1) {
1016       perror ("write");
1017       return -1;
1018     }
1019     buf += r;
1020     len -= r;
1021   }
1022
1023   return 0;
1024 }