Add 'glob' command for guestfish.
[libguestfs.git] / fish / glob.c
diff --git a/fish/glob.c b/fish/glob.c
new file mode 100644 (file)
index 0000000..827e062
--- /dev/null
@@ -0,0 +1,163 @@
+/* guestfish - the filesystem interactive shell
+ * Copyright (C) 2009 Red Hat Inc. 
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "fish.h"
+
+/* A bit tricky because in the case where there are multiple
+ * paths we have to perform a Cartesian product.
+ */
+static void glob_issue (char *cmd, int argc, char ***globs, int *posn, int *count, int *r);
+
+int
+do_glob (const char *cmd, int argc, char *argv[])
+{
+  /* For 'glob cmd foo /s* /usr/s*' this could be:
+   *
+   * (globs[0]) globs[1]  globs[1]  globs[2]
+   * (cmd)      foo       /sbin     /usr/sbin
+   *                      /srv      /usr/share
+   *                      /sys      /usr/src
+   *
+   * and then we call every combination (ie. 1x3x3) of
+   * argv[1-].
+   */
+  char **globs[argc];
+  int posn[argc];
+  int count[argc];
+  int i, r = 0;
+
+  if (argc < 1) {
+    fprintf (stderr, _("use 'glob command [args...]'\n"));
+    return -1;
+  }
+
+  /* This array will record the current execution position
+   * in the Cartesian product.
+   * NB. globs[0], posn[0], count[0] are ignored.
+   */
+  for (i = 1; i < argc; ++i)
+    posn[i] = 0;
+  for (i = 1; i < argc; ++i)
+    globs[i] = NULL;
+
+  for (i = 1; i < argc; ++i) {
+    char **pp;
+
+    /* Only if it begins with '/' can it possibly be a globbable path. */
+    if (argv[i][0] == '/') {
+      pp = guestfs_glob_expand (g, argv[i]);
+      if (pp == NULL) {                /* real error in glob_expand */
+       fprintf (stderr, _("glob: guestfs_glob_expand call failed: %s\n"),
+                argv[i]);
+       goto error0;
+      }
+
+      /* If there were no matches, then we add a single element list
+       * containing just the original argv[i] string.
+       */
+      if (pp[0] == NULL) {
+       char **pp2;
+
+       pp2 = realloc (pp, sizeof (char *) * 2);
+       if (pp2 == NULL) {
+         perror ("realloc");
+         free (pp);
+         goto error0;
+       }
+       pp = pp2;
+
+       pp[0] = strdup (argv[i]);
+       if (pp[0] == NULL) {
+         perror ("strdup");
+         free (pp);
+         goto error0;
+       }
+       pp[1] = NULL;
+      }
+    }
+    /* Doesn't begin with '/' */
+    else {
+      pp = malloc (sizeof (char *) * 2);
+      if (pp == NULL) {
+       perror ("malloc");
+       goto error0;
+      }
+      pp[0] = strdup (argv[i]);
+      if (pp[0] == NULL) {
+       perror ("strdup");
+       free (pp);
+       goto error0;
+      }
+      pp[1] = NULL;
+    }
+
+    globs[i] = pp;
+    count[i] = count_strings (pp);
+  }
+
+  /* Issue the commands. */
+  glob_issue (argv[0], argc, globs, posn, count, &r);
+
+  /* Free resources. */
+ error0:
+  for (i = 1; i < argc; ++i)
+    if (globs[i])
+      free_strings (globs[i]);
+  return r;
+}
+
+static void
+glob_issue (char *cmd, int argc,
+           char ***globs, int *posn, int *count,
+           int *r)
+{
+  int i;
+  char *argv[argc+1];
+
+  argv[0] = cmd;
+  argv[argc] = NULL;
+
+ again:
+  printf ("%s", argv[0]);
+  for (i = 1; i < argc; ++i) {
+    argv[i] = globs[i][posn[i]];
+    printf (" %s", argv[i]);
+  }
+  printf ("\n");
+
+  if (issue_command (argv[0], &argv[1]) == -1)
+    r = -1;                    /* ... but don't exit */
+
+  for (i = argc-1; i >= 1; --i) {
+    posn[i]++;
+    if (posn[i] < count[i])
+      break;
+    posn[i] = 0;
+  }
+  if (i == 0)                  /* All done. */
+    return;
+
+  goto again;
+}