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