summaryrefslogtreecommitdiffstats
path: root/fish/glob.c
blob: 8250c138a6608db240659de86c714b690764771f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
/* guestfish - the filesystem interactive shell
 * Copyright (C) 2009-2010 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, size_t argc, char ***globs, int *posn, int *count, int *r);

int
run_glob (const char *cmd, size_t 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];
  size_t i;
  int 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, size_t argc,
            char ***globs, int *posn, int *count,
            int *r)
{
  size_t i;
  char *argv[argc+1];

  argv[0] = cmd;
  argv[argc] = NULL;

 again:
  for (i = 1; i < argc; ++i)
    argv[i] = globs[i][posn[i]];

  if (issue_command (argv[0], &argv[1], NULL, 0) == -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;
}