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