1
/* wildcard.c - Wildcard character expansion for GRUB script. */
3
* GRUB -- GRand Unified Bootloader
4
* Copyright (C) 2010 Free Software Foundation, Inc.
6
* GRUB is free software: you can redistribute it and/or modify
7
* it under the terms of the GNU General Public License as published by
8
* the Free Software Foundation, either version 3 of the License, or
9
* (at your option) any later version.
11
* GRUB is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
* GNU General Public License for more details.
16
* You should have received a copy of the GNU General Public License
17
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
23
#include <grub/file.h>
24
#include <grub/device.h>
25
#include <grub/script_sh.h>
29
static inline int isregexop (char ch);
30
static char ** merge (char **lhs, char **rhs);
31
static char *make_dir (const char *prefix, const char *start, const char *end);
32
static int make_regex (const char *regex_start, const char *regex_end,
34
static void split_path (const char *path, const char **suffix_end, const char **regex_end);
35
static char ** match_devices (const regex_t *regexp, int noparts);
36
static char ** match_files (const char *prefix, const char *suffix_start,
37
const char *suffix_end, const regex_t *regexp);
39
static char* wildcard_escape (const char *s);
40
static char* wildcard_unescape (const char *s);
41
static grub_err_t wildcard_expand (const char *s, char ***strs);
43
struct grub_script_wildcard_translator grub_filename_translator = {
44
.expand = wildcard_expand,
45
.escape = wildcard_escape,
46
.unescape = wildcard_unescape
50
merge (char **dest, char **ps)
62
for (i = 0; dest[i]; i++)
64
for (j = 0; ps[j]; j++)
67
p = grub_realloc (dest, sizeof (char*) * (i + j + 1));
76
for (j = 0; ps[j]; j++)
87
return grub_strchr ("*.\\", ch) ? 1 : 0;
91
make_dir (const char *prefix, const char *start, const char *end)
98
i = grub_strlen (prefix);
101
result = grub_malloc (n + 1);
105
grub_strcpy (result, prefix);
106
while (start < end && (ch = *start++))
107
if (ch == '\\' && isregexop (*start))
108
result[i++] = *start++;
117
make_regex (const char *start, const char *end, regex_t *regexp)
121
unsigned len = end - start;
122
char *buffer = grub_malloc (len * 2 + 2 + 1); /* worst case size. */
130
/* XXX Only * expansion for now. */
131
switch ((ch = *start++))
136
buffer[i++] = *start++;
157
grub_dprintf ("expand", "Regexp is %s\n", buffer);
159
if (regcomp (regexp, buffer, RE_SYNTAX_GNU_AWK))
169
/* Split `str' into two parts: (1) dirname that is regexop free (2)
170
dirname that has a regexop. */
172
split_path (const char *str, const char **noregexop, const char **regexop)
178
const char *split; /* points till the end of dirnaname that doesn't
184
if (ch == '\\' && end[1])
187
else if (isregexop (ch))
190
else if (ch == '/' && ! regex)
191
split = end + 1; /* forward to next regexop-free dirname */
193
else if (ch == '/' && regex)
194
break; /* stop at the first dirname with a regexop */
207
match_devices (const regex_t *regexp, int noparts)
213
auto int match (const char *name);
214
int match (const char *name)
219
/* skip partitions if asked to. */
220
if (noparts && grub_strchr(name, ','))
223
buffer = grub_xasprintf ("(%s)", name);
227
grub_dprintf ("expand", "matching: %s\n", buffer);
228
if (regexec (regexp, buffer, 0, 0, 0))
230
grub_dprintf ("expand", "not matched\n");
235
t = grub_realloc (devs, sizeof (char*) * (ndev + 2));
240
devs[ndev++] = buffer;
248
if (grub_device_iterate (match))
255
for (i = 0; devs && devs[i]; i++)
265
match_files (const char *prefix, const char *suffix, const char *end,
266
const regex_t *regexp)
278
auto int match (const char *name, const struct grub_dirhook_info *info);
279
int match (const char *name, const struct grub_dirhook_info *info)
284
/* skip . and .. names */
285
if (grub_strcmp(".", name) == 0 || grub_strcmp("..", name) == 0)
288
grub_dprintf ("expand", "matching: %s in %s\n", name, dir);
289
if (regexec (regexp, name, 0, 0, 0))
292
buffer = grub_xasprintf ("%s%s", dir, name);
296
t = grub_realloc (files, sizeof (char*) * (nfile + 2));
304
files[nfile++] = buffer;
315
dir = make_dir (prefix, suffix, end);
319
device_name = grub_file_get_device_name (dir);
320
dev = grub_device_open (device_name);
324
fs = grub_fs_probe (dev);
328
path = grub_strchr (dir, ')');
333
if (fs->dir (dev, path, match))
337
grub_device_close (dev);
338
grub_free (device_name);
347
for (i = 0; files && files[i]; i++)
348
grub_free (files[i]);
354
grub_device_close (dev);
357
grub_free (device_name);
364
wildcard_escape (const char *s)
371
len = grub_strlen (s);
372
p = grub_malloc (len * 2 + 1);
388
wildcard_unescape (const char *s)
395
len = grub_strlen (s);
396
p = grub_malloc (len + 1);
403
if (ch == '\\' && isregexop (*s))
413
wildcard_expand (const char *s, char ***strs)
417
const char *noregexop;
426
split_path (start, &noregexop, ®exop);
427
if (noregexop >= regexop) /* no more wildcards */
430
if (make_regex (noregexop, regexop, ®exp))
435
if (start == noregexop) /* device part has regexop */
436
paths = match_devices (®exp, *start != '(');
438
else if (*start == '(') /* device part explicit wo regexop */
439
paths = match_files ("", start, noregexop, ®exp);
441
else if (*start == '/') /* no device part */
448
root = grub_env_get ("root");
452
prefix = grub_xasprintf ("(%s)", root);
456
paths = match_files (prefix, start, noregexop, ®exp);
464
for (i = 0; paths[i]; i++)
468
p = match_files (paths[i], start, noregexop, ®exp);
493
for (i = 0; paths && paths[i]; i++)
494
grub_free (paths[i]);