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 grub_err_t
wildcard_expand (const char *s
, char ***strs
);
41 struct grub_script_wildcard_translator grub_filename_translator
= {
42 .expand
= wildcard_expand
,
46 merge (char **dest
, char **ps
)
58 for (i
= 0; dest
[i
]; i
++)
60 for (j
= 0; ps
[j
]; j
++)
63 p
= grub_realloc (dest
, sizeof (char*) * (i
+ j
+ 1));
72 for (j
= 0; ps
[j
]; j
++)
83 return grub_strchr ("*.\\|+{}[]?", ch
) ? 1 : 0;
87 make_dir (const char *prefix
, const char *start
, const char *end
)
94 i
= grub_strlen (prefix
);
97 result
= grub_malloc (n
+ 1);
101 grub_strcpy (result
, prefix
);
102 while (start
< end
&& (ch
= *start
++))
103 if (ch
== '\\' && isregexop (*start
))
104 result
[i
++] = *start
++;
113 make_regex (const char *start
, const char *end
, regex_t
*regexp
)
117 unsigned len
= end
- start
;
118 char *buffer
= grub_malloc (len
* 2 + 2 + 1); /* worst case size. */
126 /* XXX Only * and ? expansion for now. */
127 switch ((ch
= *start
++))
132 buffer
[i
++] = *start
++;
164 grub_dprintf ("expand", "Regexp is %s\n", buffer
);
166 if (regcomp (regexp
, buffer
, RE_SYNTAX_GNU_AWK
))
176 /* Split `str' into two parts: (1) dirname that is regexop free (2)
177 dirname that has a regexop. */
179 split_path (const char *str
, const char **noregexop
, const char **regexop
)
185 const char *split
; /* points till the end of dirnaname that doesn't
191 if (ch
== '\\' && end
[1])
194 else if (ch
== '*' || ch
== '?')
197 else if (ch
== '/' && ! regex
)
198 split
= end
+ 1; /* forward to next regexop-free dirname */
200 else if (ch
== '/' && regex
)
201 break; /* stop at the first dirname with a regexop */
213 /* Context for match_devices. */
214 struct match_devices_ctx
216 const regex_t
*regexp
;
222 /* Helper for match_devices. */
224 match_devices_iter (const char *name
, void *data
)
226 struct match_devices_ctx
*ctx
= data
;
230 /* skip partitions if asked to. */
231 if (ctx
->noparts
&& grub_strchr (name
, ','))
234 buffer
= grub_xasprintf ("(%s)", name
);
238 grub_dprintf ("expand", "matching: %s\n", buffer
);
239 if (regexec (ctx
->regexp
, buffer
, 0, 0, 0))
241 grub_dprintf ("expand", "not matched\n");
246 t
= grub_realloc (ctx
->devs
, sizeof (char*) * (ctx
->ndev
+ 2));
254 ctx
->devs
[ctx
->ndev
++] = buffer
;
255 ctx
->devs
[ctx
->ndev
] = 0;
260 match_devices (const regex_t
*regexp
, int noparts
)
262 struct match_devices_ctx ctx
= {
270 if (grub_device_iterate (match_devices_iter
, &ctx
))
277 for (i
= 0; ctx
.devs
&& ctx
.devs
[i
]; i
++)
278 grub_free (ctx
.devs
[i
]);
280 grub_free (ctx
.devs
);
285 /* Context for match_files. */
286 struct match_files_ctx
288 const regex_t
*regexp
;
294 /* Helper for match_files. */
296 match_files_iter (const char *name
, const struct grub_dirhook_info
*info
,
299 struct match_files_ctx
*ctx
= data
;
303 /* skip . and .. names */
304 if (grub_strcmp(".", name
) == 0 || grub_strcmp("..", name
) == 0)
307 grub_dprintf ("expand", "matching: %s in %s\n", name
, ctx
->dir
);
308 if (regexec (ctx
->regexp
, name
, 0, 0, 0))
311 grub_dprintf ("expand", "matched\n");
313 buffer
= grub_xasprintf ("%s%s", ctx
->dir
, name
);
317 t
= grub_realloc (ctx
->files
, sizeof (char*) * (ctx
->nfile
+ 2));
325 ctx
->files
[ctx
->nfile
++] = buffer
;
326 ctx
->files
[ctx
->nfile
] = 0;
331 match_files (const char *prefix
, const char *suffix
, const char *end
,
332 const regex_t
*regexp
)
334 struct match_files_ctx ctx
= {
349 ctx
.dir
= make_dir (prefix
, suffix
, end
);
353 device_name
= grub_file_get_device_name (ctx
.dir
);
354 dev
= grub_device_open (device_name
);
358 fs
= grub_fs_probe (dev
);
362 if (ctx
.dir
[0] == '(')
364 path
= grub_strchr (ctx
.dir
, ')');
372 if (fs
->dir (dev
, path
, match_files_iter
, &ctx
))
376 grub_device_close (dev
);
377 grub_free (device_name
);
385 for (i
= 0; ctx
.files
&& ctx
.files
[i
]; i
++)
386 grub_free (ctx
.files
[i
]);
388 grub_free (ctx
.files
);
391 grub_device_close (dev
);
393 grub_free (device_name
);
399 /* Context for check_file. */
400 struct check_file_ctx
402 const char *basename
;
406 /* Helper for check_file. */
408 check_file_iter (const char *name
, const struct grub_dirhook_info
*info
,
411 struct check_file_ctx
*ctx
= data
;
413 if (ctx
->basename
[0] == 0
414 || (info
->case_insensitive
? grub_strcasecmp (name
, ctx
->basename
) == 0
415 : grub_strcmp (name
, ctx
->basename
) == 0))
425 check_file (const char *dir
, const char *basename
)
427 struct check_file_ctx ctx
= {
428 .basename
= basename
,
433 const char *device_name
, *path
;
435 device_name
= grub_file_get_device_name (dir
);
436 dev
= grub_device_open (device_name
);
440 fs
= grub_fs_probe (dev
);
446 path
= grub_strchr (dir
, ')');
454 fs
->dir (dev
, path
[0] ? path
: "/", check_file_iter
, &ctx
);
455 if (grub_errno
== 0 && basename
[0] == 0)
465 unescape (char *out
, const char *in
, const char *end
)
470 for (optr
= out
, iptr
= in
; iptr
< end
;)
472 if (*iptr
== '\\' && iptr
+ 1 < end
)
486 wildcard_expand (const char *s
, char ***strs
)
490 const char *noregexop
;
498 if (s
[0] != '/' && s
[0] != '(' && s
[0] != '*')
504 split_path (start
, &noregexop
, ®exop
);
506 if (noregexop
== regexop
)
508 grub_dprintf ("expand", "no expansion needed\n");
511 paths
= grub_malloc (sizeof (char *) * 2);
514 paths
[0] = grub_malloc (regexop
- start
+ 1);
517 unescape (paths
[0], start
, regexop
);
523 for (i
= 0; paths
[i
]; i
++)
529 oend
= o
+ grub_strlen (o
);
530 n
= grub_malloc ((oend
- o
) + (regexop
- start
) + 1);
533 grub_memcpy (n
, o
, oend
- o
);
535 unescape (n
+ (oend
- o
), start
, regexop
);
537 p
= grub_strrchr (n
, '/');
547 if (!check_file (n
, p
+ 1))
549 grub_dprintf ("expand", "file <%s> in <%s> not found\n",
567 grub_dprintf ("expand", "paths[0] = `%s'\n", paths
[0]);
572 if (make_regex (noregexop
, regexop
, ®exp
))
579 if (start
== noregexop
) /* device part has regexop */
580 paths
= match_devices (®exp
, *start
!= '(');
582 else /* device part explicit wo regexop */
583 paths
= match_files ("", start
, noregexop
, ®exp
);
589 for (i
= 0; paths
[i
]; i
++)
593 p
= match_files (paths
[i
], start
, noregexop
, ®exp
);
594 grub_free (paths
[i
]);
620 for (i
= 0; paths
&& paths
[i
]; i
++)
621 grub_free (paths
[i
]);