#include <config.h>
-#define _GNU_SOURCE // for strchrnul
-
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include <signal.h>
#include <assert.h>
-#include <ctype.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <guestfs.h>
#include "fish.h"
+#include "c-ctype.h"
+#include "closeout.h"
#include "progname.h"
struct mp {
if (guestfs_is_config (g)) {
if (guestfs_launch (g) == -1)
return -1;
- if (guestfs_wait_ready (g) == -1)
- return -1;
}
return 0;
}
/* Set global program name that is not polluted with libtool artifacts. */
set_program_name (argv[0]);
+ atexit (close_stdout);
+
enum { HELP_OPTION = CHAR_MAX + 1 };
static const char *options = "a:Df:h::im:nrv?Vx";
/* Skip any initial whitespace before the command. */
again:
- while (*buf && isspace (*buf))
+ while (*buf && c_isspace (*buf))
buf++;
if (!*buf) continue;
r = do_more (cmd, argc, argv);
else if (strcasecmp (cmd, "reopen") == 0)
r = do_reopen (cmd, argc, argv);
+ else if (strcasecmp (cmd, "sparse") == 0)
+ r = do_sparse (cmd, argc, argv);
else if (strcasecmp (cmd, "time") == 0)
r = do_time (cmd, argc, argv);
else
printf ("%-20s %s\n",
"reopen", _("close and reopen libguestfs handle"));
printf ("%-20s %s\n",
+ "sparse", _("allocate a sparse image file"));
+ printf ("%-20s %s\n",
"time", _("measure time taken to run command"));
/* actions are printed after this (see list_commands) */
" <nn>K or <nn>KB number of kilobytes\n"
" <nn>M or <nn>MB number of megabytes\n"
" <nn>G or <nn>GB number of gigabytes\n"
+ " <nn>T or <nn>TB number of terabytes\n"
+ " <nn>P or <nn>PB number of petabytes\n"
+ " <nn>E or <nn>EB number of exabytes\n"
" <nn>sects number of 512 byte sectors\n"));
else if (strcasecmp (cmd, "echo") == 0)
printf (_("echo - display a line of text\n"
"Close and reopen the libguestfs handle. It is not necessary to use\n"
"this normally, because the handle is closed properly when guestfish\n"
"exits. However this is occasionally useful for testing.\n"));
+ else if (strcasecmp (cmd, "sparse") == 0)
+ printf (_("sparse - allocate a sparse image file\n"
+ " sparse <filename> <size>\n"
+ "\n"
+ " This creates an empty sparse file of the given size,\n"
+ " and then adds so it can be further examined.\n"
+ "\n"
+ " In all respects it works the same as the 'alloc'\n"
+ " command, except that the image file is allocated\n"
+ " sparsely, which means that disk blocks are not assigned\n"
+ " to the file until they are needed. Sparse disk files\n"
+ " only use space when written to, but they are slower\n"
+ " and there is a danger you could run out of real disk\n"
+ " space during a write operation.\n"
+ "\n"
+ " For more advanced image creation, see qemu-img utility.\n"
+ "\n"
+ " Size can be specified (where <nn> means a number):\n"
+ " <nn> number of kilobytes\n"
+ " eg: 1440 standard 3.5\" floppy\n"
+ " <nn>K or <nn>KB number of kilobytes\n"
+ " <nn>M or <nn>MB number of megabytes\n"
+ " <nn>G or <nn>GB number of gigabytes\n"
+ " <nn>T or <nn>TB number of terabytes\n"
+ " <nn>P or <nn>PB number of petabytes\n"
+ " <nn>E or <nn>EB number of exabytes\n"
+ " <nn>sects number of 512 byte sectors\n"));
else if (strcasecmp (cmd, "time") == 0)
printf (_("time - measure time taken to run command\n"
" time <command> [<args> ...]\n"
strcasecmp (str, "no") != 0;
}
-/* XXX We could improve list parsing. */
+/* Free strings from a non-NULL terminated char** */
+static void
+free_n_strings (char **str, size_t len)
+{
+ size_t i;
+
+ for (i = 0; i < len; i++) {
+ free (str[i]);
+ }
+ free (str);
+}
+
char **
parse_string_list (const char *str)
{
- char **argv;
- const char *p, *pend;
- int argc, i;
+ char **argv = NULL;
+ size_t argv_len = 0;
+
+ /* Current position pointer */
+ const char *p = str;
+
+ /* Token might be simple:
+ * Token
+ * or be quoted:
+ * 'This is a single token'
+ * or contain embedded single-quoted sections:
+ * This' is a sing'l'e to'ken
+ *
+ * The latter may seem over-complicated, but it's what a normal shell does.
+ * Not doing it risks surprising somebody.
+ *
+ * This outer loop is over complete tokens.
+ */
+ while (*p) {
+ char *tok = NULL;
+ size_t tok_len = 0;
- argc = 1;
- for (i = 0; str[i]; ++i)
- if (str[i] == ' ') argc++;
+ /* Skip leading whitespace */
+ p += strspn (p, " \t");
- argv = malloc (sizeof (char *) * (argc+1));
- if (argv == NULL) { perror ("malloc"); exit (1); }
+ char in_quote = 0;
- p = str;
- i = 0;
- while (*p) {
- pend = strchrnul (p, ' ');
- argv[i] = strndup (p, pend-p);
- i++;
- p = *pend == ' ' ? pend+1 : pend;
+ /* This loop is over token 'fragments'. A token can be in multiple bits if
+ * it contains single quotes. We also treat both sides of an escaped quote
+ * as separate fragments because we can't just copy it: we have to remove
+ * the \.
+ */
+ while (*p && (!c_isblank (*p) || in_quote)) {
+ const char *end = p;
+
+ /* Check if the fragment starts with a quote */
+ if ('\'' == *p) {
+ /* Toggle in_quote */
+ in_quote = !in_quote;
+
+ /* Skip the quote */
+ p++; end++;
+ }
+
+ /* If we're in a quote, look for an end quote */
+ if (in_quote) {
+ end += strcspn (end, "'");
+ }
+
+ /* Otherwise, look for whitespace or a quote */
+ else {
+ end += strcspn (end, " \t'");
+ }
+
+ /* Grow the token to accommodate the fragment */
+ size_t tok_end = tok_len;
+ tok_len += end - p;
+ char *tok_new = realloc (tok, tok_len + 1);
+ if (NULL == tok_new) {
+ perror ("realloc");
+ free_n_strings (argv, argv_len);
+ free (tok);
+ exit (1);
+ }
+ tok = tok_new;
+
+ /* Check if we stopped on an escaped quote */
+ if ('\'' == *end && end != p && *(end-1) == '\\') {
+ /* Add everything before \' to the token */
+ memcpy (&tok[tok_end], p, end - p - 1);
+
+ /* Add the quote */
+ tok[tok_len-1] = '\'';
+
+ /* Already processed the quote */
+ p = end + 1;
+ }
+
+ else {
+ /* Add the whole fragment */
+ memcpy (&tok[tok_end], p, end - p);
+
+ p = end;
+ }
+ }
+
+ /* We've reached the end of a token. We shouldn't still be in quotes. */
+ if (in_quote) {
+ fprintf (stderr, _("Runaway quote in string \"%s\"\n"), str);
+
+ free_n_strings (argv, argv_len);
+
+ return NULL;
+ }
+
+ /* Add this token if there is one. There might not be if there was
+ * whitespace at the end of the input string */
+ if (tok) {
+ /* Add the NULL terminator */
+ tok[tok_len] = '\0';
+
+ /* Add the argument to the argument list */
+ argv_len++;
+ char **argv_new = realloc (argv, sizeof (*argv) * argv_len);
+ if (NULL == argv_new) {
+ perror ("realloc");
+ free_n_strings (argv, argv_len-1);
+ free (tok);
+ exit (1);
+ }
+ argv = argv_new;
+
+ argv[argv_len-1] = tok;
+ }
}
- argv[i] = NULL;
+
+ /* NULL terminate the argument list */
+ argv_len++;
+ char **argv_new = realloc (argv, sizeof (*argv) * argv_len);
+ if (NULL == argv_new) {
+ perror ("realloc");
+ free_n_strings (argv, argv_len-1);
+ exit (1);
+ }
+ argv = argv_new;
+
+ argv[argv_len-1] = NULL;
return argv;
}
return 0;
}
+
+/* Resolve the special "win:..." form for Windows-specific paths.
+ * This always returns a newly allocated string which is freed by the
+ * caller function in "cmds.c".
+ */
+char *
+resolve_win_path (const char *path)
+{
+ char *ret;
+ size_t i;
+
+ if (strncasecmp (path, "win:", 4) != 0) {
+ ret = strdup (path);
+ if (ret == NULL)
+ perror ("strdup");
+ return ret;
+ }
+
+ path += 4;
+
+ /* Drop drive letter, if it's "C:". */
+ if (strncasecmp (path, "c:", 2) == 0)
+ path += 2;
+
+ if (!*path) {
+ ret = strdup ("/");
+ if (ret == NULL)
+ perror ("strdup");
+ return ret;
+ }
+
+ ret = strdup (path);
+ if (ret == NULL) {
+ perror ("strdup");
+ return NULL;
+ }
+
+ /* Blindly convert any backslashes into forward slashes. Is this good? */
+ for (i = 0; i < strlen (ret); ++i)
+ if (ret[i] == '\\')
+ ret[i] = '/';
+
+ char *t = guestfs_case_sensitive_path (g, ret);
+ free (ret);
+ ret = t;
+
+ return ret;
+}