+diff -Nur grub-0.97-dirs/docs/grub.texi grub-0.97-wildcards/docs/grub.texi
+--- grub-0.97-dirs/docs/grub.texi 2005-08-21 20:31:12.000000000 +0300
++++ grub-0.97-wildcards/docs/grub.texi 2005-08-21 20:32:45.000000000 +0300
+@@ -2121,6 +2121,7 @@
+ * gfxmenu:: Use graphical menu interface
+ * timeout:: Set the timeout
+ * title:: Start a menu entry
++* wildcard:: Define a wildcard boot entry
+ @end menu
+
+
+@@ -2190,6 +2191,42 @@
+ @end deffn
+
+
++@node wildcard
++@subsection wildcard
++
++@deffn Command wildcard pathname
++Treat this boot entry as a wildcard entry: The
++wildcard, title, kernel, and initrd commands (see @ref{Menu-specific
++commands} and @ref{Command-line and menu entry commands}) each have an
++asterisk (*) in their value. A filename match is performed on the
++@var{pathname} of the wildcard command. For each match, the entire boot
++entry is duplicated. The part of the filename whcih matches the asterisk
++in the wildcard command replaces the asterisks in the title, kernel, and
++initrd commands. For example, with the files vmlinuz-2.6.5-1 and
++vmlinuz-2.6.8-8 below (hd0,7)/boot, the following entry in the stage 2
++configuration file:
++
++@example
++title Linux-*
++ wildcard (hd0,7)/boot/vmlinuz-*
++ kernel (hd0,7)/boot/vmlinuz-* root=/dev/hda8
++ initrd (hd0,7)/boot/initrd-*
++@end example
++
++would expand as follows:
++
++@example
++title Linux-2.6.5-1
++ wildcard (hd0,7)/boot/vmlinuz-2.6.5-1
++ kernel (hd0,7)/boot/vmlinuz-2.6.5-1 root=/dev/hda8
++ initrd (hd0,7)/boot/initrd-2.6.5-1
++title Linux-2.6.8-8
++ wildcard (hd0,7)/boot/vmlinuz-2.6.8-8
++ kernel (hd0,7)/boot/vmlinuz-2.6.8-8 root=/dev/hda8
++ initrd (hd0,7)/boot/initrd-2.6.8-8
++@end example
++@end deffn
++
+ @node General commands
+ @section The list of general commands
+
+diff -Nur grub-0.97-dirs/netboot/fsys_tftp.c grub-0.97-wildcards/netboot/fsys_tftp.c
+--- grub-0.97-dirs/netboot/fsys_tftp.c 2005-08-21 20:31:02.000000000 +0300
++++ grub-0.97-wildcards/netboot/fsys_tftp.c 2005-08-21 20:32:45.000000000 +0300
+@@ -409,7 +409,7 @@
+ /* Check if the file DIRNAME really exists. Get the size and save it in
+ FILEMAX. */
+ int
+-tftp_dir (char *dirname)
++tftp_dir (char *dirname, void (*handle)(char *))
+ {
+ int ch;
+
+@@ -418,7 +418,7 @@
+ #endif
+
+ /* In TFTP, there is no way to know what files exist. */
+- if (print_possibilities)
++ if (handle)
+ return 1;
+
+ /* Don't know the size yet. */
+diff -Nur grub-0.97-dirs/stage2/builtins.c grub-0.97-wildcards/stage2/builtins.c
+--- grub-0.97-dirs/stage2/builtins.c 2005-08-21 20:31:02.000000000 +0300
++++ grub-0.97-wildcards/stage2/builtins.c 2005-08-21 20:32:45.000000000 +0300
+@@ -4828,6 +4828,49 @@
+ };
+
+ \f
++/* wildcard */
++ static int
++wildcard_func (char *arg, int flags)
++{
++#ifdef DEBUG_WILDCARD
++ char *w = wildcard (arg);
++
++ if (w)
++ {
++ while (*w)
++ {
++ grub_printf("%s ", w);
++ w += strlen (w) + 1;
++ }
++ grub_printf("\n");
++ return 1;
++ }
++ else
++ print_error();
++#endif
++
++ /* This special command is interpreted in the config file parser. */
++ return 0;
++}
++
++static struct builtin builtin_wildcard =
++ {
++ "wildcard",
++ wildcard_func,
++#ifndef DEBUG_WILDCARD
++ BUILTIN_MENU,
++#else
++ BUILTIN_CMDLINE | BUILTIN_MENU | BUILTIN_HELP_LIST,
++ "wildcard GLOB",
++ "Declare this menu entry as a wildcard entry. GLOB is a path containing"
++ " one asterisk. All files matching this expression are looked up; the"
++ " menu entry is duplicated for each match with asterisks in other"
++ " commands replaced by the string matching the asterisk in the wildcard"
++ " command."
++#endif
++};
++
++\f
+ /* The table of builtin commands. Sorted in dictionary order. */
+ struct builtin *builtin_table[] =
+ {
+@@ -4917,5 +4960,6 @@
+ &builtin_unhide,
+ &builtin_uppermem,
+ &builtin_vbeprobe,
++ &builtin_wildcard,
+ 0
+ };
+diff -Nur grub-0.97-dirs/stage2/disk_io.c grub-0.97-wildcards/stage2/disk_io.c
+--- grub-0.97-dirs/stage2/disk_io.c 2005-08-21 20:31:02.000000000 +0300
++++ grub-0.97-wildcards/stage2/disk_io.c 2005-08-21 20:32:45.000000000 +0300
+@@ -36,7 +36,6 @@
+ void (*disk_read_func) (int, int, int) = NULL;
+
+ #ifndef STAGE1_5
+-int print_possibilities;
+
+ static int do_completion;
+ static int unique;
+@@ -1479,7 +1478,7 @@
+ if (! is_completion)
+ grub_printf (" Possible files are:");
+
+- dir (buf);
++ dir (buf, print_a_completion);
+
+ if (is_completion && *unique_string)
+ {
+@@ -1498,7 +1497,7 @@
+ *ptr = '/';
+ *(ptr + 1) = 0;
+
+- dir (buf);
++ dir (buf, print_a_completion);
+
+ /* Restore the original unique value. */
+ unique = 1;
+@@ -1626,12 +1625,7 @@
+ if (!errnum && fsys_type == NUM_FSYS)
+ errnum = ERR_FSYS_MOUNT;
+
+-# ifndef STAGE1_5
+- /* set "dir" function to open a file */
+- print_possibilities = 0;
+-# endif
+-
+- if (!errnum && (*(fsys_table[fsys_type].dir_func)) (filename))
++ if (!errnum && (*(fsys_table[fsys_type].dir_func)) (filename, NULL))
+ {
+ #ifndef NO_DECOMPRESSION
+ return gunzip_test_header ();
+@@ -1752,7 +1746,7 @@
+ }
+
+ int
+-dir (char *dirname)
++dir (char *dirname, void (*handle)(char *))
+ {
+ #ifndef NO_DECOMPRESSION
+ compressed_file = 0;
+@@ -1761,19 +1755,18 @@
+ if (!(dirname = setup_part (dirname)))
+ return 0;
+
++ errnum = 0;
+ if (*dirname != '/')
+ errnum = ERR_BAD_FILENAME;
+-
+- if (fsys_type == NUM_FSYS)
++ else if (fsys_type == NUM_FSYS)
+ errnum = ERR_FSYS_MOUNT;
+-
+- if (errnum)
+- return 0;
+-
+- /* set "dir" function to list completions */
+- print_possibilities = 1;
+-
+- return (*(fsys_table[fsys_type].dir_func)) (dirname);
++ else
++ {
++ fsys_table[fsys_type].dir_func (dirname, handle);
++ if (errnum == ERR_FILE_NOT_FOUND)
++ errnum = 0;
++ }
++ return errnum == 0;
+ }
+ #endif /* STAGE1_5 */
+
+diff -Nur grub-0.97-dirs/stage2/filesys.h grub-0.97-wildcards/stage2/filesys.h
+--- grub-0.97-dirs/stage2/filesys.h 2005-08-21 20:31:02.000000000 +0300
++++ grub-0.97-wildcards/stage2/filesys.h 2005-08-21 20:32:45.000000000 +0300
+@@ -24,7 +24,7 @@
+ #define FSYS_FFS_NUM 1
+ int ffs_mount (void);
+ int ffs_read (char *buf, int len);
+-int ffs_dir (char *dirname);
++int ffs_dir (char *dirname, void (*handle)(char *));
+ int ffs_embed (int *start_sector, int needed_sectors);
+ #else
+ #define FSYS_FFS_NUM 0
+@@ -34,7 +34,7 @@
+ #define FSYS_UFS2_NUM 1
+ int ufs2_mount (void);
+ int ufs2_read (char *buf, int len);
+-int ufs2_dir (char *dirname);
++int ufs2_dir (char *dirname, void (*handle)(char *));
+ int ufs2_embed (int *start_sector, int needed_sectors);
+ #else
+ #define FSYS_UFS2_NUM 0
+@@ -44,7 +44,7 @@
+ #define FSYS_FAT_NUM 1
+ int fat_mount (void);
+ int fat_read (char *buf, int len);
+-int fat_dir (char *dirname);
++int fat_dir (char *dirname, void (*handle)(char *));
+ #else
+ #define FSYS_FAT_NUM 0
+ #endif
+@@ -53,7 +53,7 @@
+ #define FSYS_EXT2FS_NUM 1
+ int ext2fs_mount (void);
+ int ext2fs_read (char *buf, int len);
+-int ext2fs_dir (char *dirname);
++int ext2fs_dir (char *dirname, void (*handle)(char *));
+ #else
+ #define FSYS_EXT2FS_NUM 0
+ #endif
+@@ -62,7 +62,7 @@
+ #define FSYS_MINIX_NUM 1
+ int minix_mount (void);
+ int minix_read (char *buf, int len);
+-int minix_dir (char *dirname);
++int minix_dir (char *dirname, void (*handle)(char *));
+ #else
+ #define FSYS_MINIX_NUM 0
+ #endif
+@@ -71,7 +71,7 @@
+ #define FSYS_REISERFS_NUM 1
+ int reiserfs_mount (void);
+ int reiserfs_read (char *buf, int len);
+-int reiserfs_dir (char *dirname);
++int reiserfs_dir (char *dirname, void (*handle)(char *));
+ int reiserfs_embed (int *start_sector, int needed_sectors);
+ #if defined(__linux__) && defined (GRUB_UTIL)
+ #include <sys/types.h>
+@@ -91,7 +91,7 @@
+ #define FSYS_VSTAFS_NUM 1
+ int vstafs_mount (void);
+ int vstafs_read (char *buf, int len);
+-int vstafs_dir (char *dirname);
++int vstafs_dir (char *dirname, void (*handle)(char *));
+ #else
+ #define FSYS_VSTAFS_NUM 0
+ #endif
+@@ -100,7 +100,7 @@
+ #define FSYS_JFS_NUM 1
+ int jfs_mount (void);
+ int jfs_read (char *buf, int len);
+-int jfs_dir (char *dirname);
++int jfs_dir (char *dirname, void (*handle)(char *));
+ int jfs_embed (int *start_sector, int needed_sectors);
+ #else
+ #define FSYS_JFS_NUM 0
+@@ -110,7 +110,7 @@
+ #define FSYS_XFS_NUM 1
+ int xfs_mount (void);
+ int xfs_read (char *buf, int len);
+-int xfs_dir (char *dirname);
++int xfs_dir (char *dirname, void (*handle)(char *));
+ #else
+ #define FSYS_XFS_NUM 0
+ #endif
+@@ -119,7 +119,7 @@
+ #define FSYS_TFTP_NUM 1
+ int tftp_mount (void);
+ int tftp_read (char *buf, int len);
+-int tftp_dir (char *dirname);
++int tftp_dir (char *dirname, void (*handle)(char *));
+ void tftp_close (void);
+ #else
+ #define FSYS_TFTP_NUM 0
+@@ -129,7 +129,7 @@
+ #define FSYS_ISO9660_NUM 1
+ int iso9660_mount (void);
+ int iso9660_read (char *buf, int len);
+-int iso9660_dir (char *dirname);
++int iso9660_dir (char *dirname, void (*handle)(char *));
+ #else
+ #define FSYS_ISO9660_NUM 0
+ #endif
+@@ -160,16 +160,10 @@
+ char *name;
+ int (*mount_func) (void);
+ int (*read_func) (char *buf, int len);
+- int (*dir_func) (char *dirname);
++ int (*dir_func) (char *dirname, void (*print_one)(char *));
+ void (*close_func) (void);
+ int (*embed_func) (int *start_sector, int needed_sectors);
+ };
+
+-#ifdef STAGE1_5
+-# define print_possibilities 0
+-#else
+-extern int print_possibilities;
+-#endif
+-
+ extern int fsmax;
+ extern struct fsys_entry fsys_table[NUM_FSYS + 1];
+diff -Nur grub-0.97-dirs/stage2/fsys_ext2fs.c grub-0.97-wildcards/stage2/fsys_ext2fs.c
+--- grub-0.97-dirs/stage2/fsys_ext2fs.c 2005-08-21 20:31:02.000000000 +0300
++++ grub-0.97-wildcards/stage2/fsys_ext2fs.c 2005-08-21 20:32:45.000000000 +0300
+@@ -495,7 +495,7 @@
+ * side effects: messes up GROUP_DESC buffer area
+ */
+ int
+-ext2fs_dir (char *dirname)
++ext2fs_dir (char *dirname, void (*handle)(char *))
+ {
+ int current_ino = EXT2_ROOT_INO; /* start at the root */
+ int updir_ino = current_ino; /* the parent of the current directory */
+@@ -521,7 +521,6 @@
+ #ifdef E2DEBUG
+ unsigned char *i;
+ #endif /* E2DEBUG */
+-
+ /* loop invariants:
+ current_ino = inode to lookup
+ dirname = pointer to filename component we are cur looking up within
+@@ -713,18 +712,9 @@
+ give up */
+ if (loc >= INODE->i_size)
+ {
+- if (print_possibilities < 0)
+- {
+-# if 0
+- putchar ('\n');
+-# endif
+- }
+- else
+- {
+- errnum = ERR_FILE_NOT_FOUND;
+- *rest = ch;
+- }
+- return (print_possibilities < 0);
++ errnum = ERR_FILE_NOT_FOUND;
++ *rest = ch;
++ return 0;
+ }
+
+ /* else, find the (logical) block component of our location */
+@@ -765,20 +755,15 @@
+ str_chk = substring (dirname, dp->name);
+
+ # ifndef STAGE1_5
+- if (print_possibilities && ch != '/'
+- && (!*dirname || str_chk <= 0))
+- {
+- if (print_possibilities > 0)
+- print_possibilities = -print_possibilities;
+- print_a_completion (dp->name);
+- }
++ if (handle && ch != '/' && (!*dirname || str_chk <= 0))
++ handle (dp->name);
+ # endif
+
+ dp->name[dp->name_len] = saved_c;
+ }
+
+ }
+- while (!dp->inode || (str_chk || (print_possibilities && ch != '/')));
++ while (!dp->inode || (str_chk || (handle && ch != '/')));
+
+ current_ino = dp->inode;
+ *(dirname = rest) = ch;
+diff -Nur grub-0.97-dirs/stage2/fsys_fat.c grub-0.97-wildcards/stage2/fsys_fat.c
+--- grub-0.97-dirs/stage2/fsys_fat.c 2005-08-21 20:31:02.000000000 +0300
++++ grub-0.97-wildcards/stage2/fsys_fat.c 2005-08-21 20:32:45.000000000 +0300
+@@ -289,7 +289,7 @@
+ }
+
+ int
+-fat_dir (char *dirname)
++fat_dir (char *dirname, void (*handle)(char *))
+ {
+ char *rest, ch, dir_buf[FAT_DIRENTRY_LENGTH];
+ char *filename = (char *) NAME_BUF;
+@@ -345,7 +345,7 @@
+ *rest = 0;
+
+ # ifndef STAGE1_5
+- if (print_possibilities && ch != '/')
++ if (handle && ch != '/')
+ do_possibilities = 1;
+ # endif
+
+@@ -356,16 +356,6 @@
+ {
+ if (!errnum)
+ {
+-# ifndef STAGE1_5
+- if (print_possibilities < 0)
+- {
+-#if 0
+- putchar ('\n');
+-#endif
+- return 1;
+- }
+-# endif /* STAGE1_5 */
+-
+ errnum = ERR_FILE_NOT_FOUND;
+ *rest = ch;
+ }
+@@ -460,11 +450,7 @@
+ {
+ print_filename:
+ if (substring (dirname, filename) <= 0)
+- {
+- if (print_possibilities > 0)
+- print_possibilities = -print_possibilities;
+- print_a_completion (filename);
+- }
++ handle (filename);
+ continue;
+ }
+ # endif /* STAGE1_5 */
+diff -Nur grub-0.97-dirs/stage2/fsys_ffs.c grub-0.97-wildcards/stage2/fsys_ffs.c
+--- grub-0.97-dirs/stage2/fsys_ffs.c 2005-08-21 20:31:02.000000000 +0300
++++ grub-0.97-wildcards/stage2/fsys_ffs.c 2005-08-21 20:32:45.000000000 +0300
+@@ -180,7 +180,7 @@
+
+
+ int
+-ffs_dir (char *dirname)
++ffs_dir (char *dirname, void (*handle)(char *))
+ {
+ char *rest, ch;
+ int block, off, loc, map, ino = ROOTINO;
+@@ -236,13 +236,6 @@
+ {
+ if (loc >= INODE->i_size)
+ {
+-#if 0
+- putchar ('\n');
+-#endif
+-
+- if (print_possibilities < 0)
+- return 1;
+-
+ errnum = ERR_FILE_NOT_FOUND;
+ *rest = ch;
+ return 0;
+@@ -267,18 +260,13 @@
+ loc += dp->d_reclen;
+
+ #ifndef STAGE1_5
+- if (dp->d_ino && print_possibilities && ch != '/'
++ if (dp->d_ino && handle && ch != '/'
+ && (!*dirname || substring (dirname, dp->d_name) <= 0))
+- {
+- if (print_possibilities > 0)
+- print_possibilities = -print_possibilities;
+-
+- print_a_completion (dp->d_name);
+- }
++ handle (dp->d_name);
+ #endif /* STAGE1_5 */
+ }
+ while (!dp->d_ino || (substring (dirname, dp->d_name) != 0
+- || (print_possibilities && ch != '/')));
++ || (handle && ch != '/')));
+
+ /* only get here if we have a matching directory entry */
+
+diff -Nur grub-0.97-dirs/stage2/fsys_iso9660.c grub-0.97-wildcards/stage2/fsys_iso9660.c
+--- grub-0.97-dirs/stage2/fsys_iso9660.c 2005-08-21 20:31:02.000000000 +0300
++++ grub-0.97-wildcards/stage2/fsys_iso9660.c 2005-08-21 20:32:45.000000000 +0300
+@@ -133,7 +133,7 @@
+ }
+
+ int
+-iso9660_dir (char *dirname)
++iso9660_dir (char *dirname, void (*handle)(char *))
+ {
+ struct iso_directory_record *idr;
+ RR_ptr_t rr_ptr;
+@@ -346,7 +346,7 @@
+ if (name_len >= pathlen
+ && !memcmp(name, dirname, pathlen))
+ {
+- if (dirname[pathlen] == '/' || !print_possibilities)
++ if (dirname[pathlen] == '/' || !handle)
+ {
+ /*
+ * DIRNAME is directory component of pathname,
+@@ -377,11 +377,9 @@
+ else /* Completion */
+ {
+ #ifndef STAGE1_5
+- if (print_possibilities > 0)
+- print_possibilities = -print_possibilities;
+ memcpy(NAME_BUF, name, name_len);
+ NAME_BUF[name_len] = '\0';
+- print_a_completion (NAME_BUF);
++ handle (NAME_BUF);
+ #endif
+ }
+ }
+@@ -390,7 +388,7 @@
+ size -= ISO_SECTOR_SIZE;
+ } /* size>0 */
+
+- if (dirname[pathlen] == '/' || print_possibilities >= 0)
++ if (dirname[pathlen] == '/' || handle)
+ {
+ errnum = ERR_FILE_NOT_FOUND;
+ return 0;
+diff -Nur grub-0.97-dirs/stage2/fsys_jfs.c grub-0.97-wildcards/stage2/fsys_jfs.c
+--- grub-0.97-dirs/stage2/fsys_jfs.c 2005-08-21 20:31:02.000000000 +0300
++++ grub-0.97-wildcards/stage2/fsys_jfs.c 2005-08-21 20:32:45.000000000 +0300
+@@ -270,7 +270,7 @@
+ }
+
+ int
+-jfs_dir (char *dirname)
++jfs_dir (char *dirname, void (*handle)(char *))
+ {
+ char *ptr, *rest, ch;
+ ldtentry_t *de;
+@@ -357,12 +357,9 @@
+
+ cmp = (!*dirname) ? -1 : substring (dirname, namebuf);
+ #ifndef STAGE1_5
+- if (print_possibilities && ch != '/'
+- && cmp <= 0) {
+- if (print_possibilities > 0)
+- print_possibilities = -print_possibilities;
+- print_a_completion (namebuf);
+- } else
++ if (handle && ch != '/' && cmp <= 0)
++ handle (namebuf);
++ else
+ #endif
+ if (cmp == 0) {
+ parent_inum = inum;
+@@ -372,9 +369,6 @@
+ }
+ de = next_dentry ();
+ if (de == NULL) {
+- if (print_possibilities < 0)
+- return 1;
+-
+ errnum = ERR_FILE_NOT_FOUND;
+ *rest = ch;
+ return 0;
+diff -Nur grub-0.97-dirs/stage2/fsys_minix.c grub-0.97-wildcards/stage2/fsys_minix.c
+--- grub-0.97-dirs/stage2/fsys_minix.c 2005-08-21 20:31:02.000000000 +0300
++++ grub-0.97-wildcards/stage2/fsys_minix.c 2005-08-21 20:32:45.000000000 +0300
+@@ -294,7 +294,7 @@
+ inode of the file we were trying to look up
+ side effects: none yet */
+ int
+-minix_dir (char *dirname)
++minix_dir (char *dirname, void (*handle)(char *))
+ {
+ int current_ino = MINIX_ROOT_INO; /* start at the root */
+ int updir_ino = current_ino; /* the parent of the current directory */
+@@ -457,18 +457,9 @@
+ give up */
+ if (loc >= INODE->i_size)
+ {
+- if (print_possibilities < 0)
+- {
+-#if 0
+- putchar ('\n');
+-#endif
+- }
+- else
+- {
+- errnum = ERR_FILE_NOT_FOUND;
+- *rest = ch;
+- }
+- return (print_possibilities < 0);
++ errnum = ERR_FILE_NOT_FOUND;
++ *rest = ch;
++ return 0;
+ }
+
+ /* else, find the (logical) block component of our location */
+@@ -510,20 +501,15 @@
+ str_chk = substring (dirname, dp->name);
+
+ # ifndef STAGE1_5
+- if (print_possibilities && ch != '/'
+- && (!*dirname || str_chk <= 0))
+- {
+- if (print_possibilities > 0)
+- print_possibilities = -print_possibilities;
+- print_a_completion (dp->name);
+- }
++ if (handle && ch != '/' && (!*dirname || str_chk <= 0))
++ handle (dp->name);
+ # endif
+
+ dp->name[namelen] = saved_c;
+ }
+
+ }
+- while (!dp->inode || (str_chk || (print_possibilities && ch != '/')));
++ while (!dp->inode || (str_chk || (handle && ch != '/')));
+
+ current_ino = dp->inode;
+ *(dirname = rest) = ch;
+diff -Nur grub-0.97-dirs/stage2/fsys_reiserfs.c grub-0.97-wildcards/stage2/fsys_reiserfs.c
+--- grub-0.97-dirs/stage2/fsys_reiserfs.c 2005-08-21 20:31:02.000000000 +0300
++++ grub-0.97-wildcards/stage2/fsys_reiserfs.c 2005-08-21 20:32:45.000000000 +0300
+@@ -991,7 +991,7 @@
+ * the size of the file.
+ */
+ int
+-reiserfs_dir (char *dirname)
++reiserfs_dir (char *dirname, void (*handle)(char *))
+ {
+ struct reiserfs_de_head *de_head;
+ char *rest, ch;
+@@ -1123,7 +1123,7 @@
+ *rest = 0;
+
+ # ifndef STAGE1_5
+- if (print_possibilities && ch != '/')
++ if (handle && ch != '/')
+ do_possibilities = 1;
+ # endif /* ! STAGE1_5 */
+
+@@ -1170,10 +1170,8 @@
+ {
+ if (cmp <= 0)
+ {
+- if (print_possibilities > 0)
+- print_possibilities = -print_possibilities;
+ *name_end = 0;
+- print_a_completion (filename);
++ handle (filename);
+ *name_end = tmp;
+ }
+ }
+@@ -1189,12 +1187,6 @@
+ num_entries--;
+ }
+ }
+-
+-# ifndef STAGE1_5
+- if (print_possibilities < 0)
+- return 1;
+-# endif /* ! STAGE1_5 */
+-
+ errnum = ERR_FILE_NOT_FOUND;
+ *rest = ch;
+ return 0;
+diff -Nur grub-0.97-dirs/stage2/fsys_ufs2.c grub-0.97-wildcards/stage2/fsys_ufs2.c
+--- grub-0.97-dirs/stage2/fsys_ufs2.c 2005-08-21 20:31:02.000000000 +0300
++++ grub-0.97-wildcards/stage2/fsys_ufs2.c 2005-08-21 20:32:45.000000000 +0300
+@@ -204,7 +204,7 @@
+ }
+
+ int
+-ufs2_dir (char *dirname)
++ufs2_dir (char *dirname, void (*handle)(char *))
+ {
+ char *rest, ch;
+ int block, off, loc, ino = ROOTINO;
+@@ -261,9 +261,6 @@
+ {
+ if (loc >= INODE_UFS2->di_size)
+ {
+- if (print_possibilities < 0)
+- return 1;
+-
+ errnum = ERR_FILE_NOT_FOUND;
+ *rest = ch;
+ return 0;
+@@ -288,18 +285,13 @@
+ loc += dp->d_reclen;
+
+ #ifndef STAGE1_5
+- if (dp->d_ino && print_possibilities && ch != '/'
++ if (dp->d_ino && handle && ch != '/'
+ && (!*dirname || substring (dirname, dp->d_name) <= 0))
+- {
+- if (print_possibilities > 0)
+- print_possibilities = -print_possibilities;
+-
+- print_a_completion (dp->d_name);
+- }
++ handle (dp->d_name);
+ #endif /* STAGE1_5 */
+ }
+ while (!dp->d_ino || (substring (dirname, dp->d_name) != 0
+- || (print_possibilities && ch != '/')));
++ || (handle && ch != '/')));
+
+ /* only get here if we have a matching directory entry */
+
+diff -Nur grub-0.97-dirs/stage2/fsys_vstafs.c grub-0.97-wildcards/stage2/fsys_vstafs.c
+--- grub-0.97-dirs/stage2/fsys_vstafs.c 2005-08-21 20:31:02.000000000 +0300
++++ grub-0.97-wildcards/stage2/fsys_vstafs.c 2005-08-21 20:32:45.000000000 +0300
+@@ -115,7 +115,7 @@
+ }
+
+ int
+-vstafs_dir (char *dirname)
++vstafs_dir (char *dirname, void (*handle)(char *))
+ {
+ char *fn, ch;
+ struct dir_entry *d;
+@@ -146,14 +146,9 @@
+ continue;
+
+ #ifndef STAGE1_5
+- if (print_possibilities && ch != '/'
++ if (handle && ch != '/'
+ && (! *dirname || strcmp (dirname, d->name) <= 0))
+- {
+- if (print_possibilities > 0)
+- print_possibilities = -print_possibilities;
+-
+- printf (" %s", d->name);
+- }
++ handle(d->name);
+ #endif
+ if (! grub_strcmp (dirname, d->name))
+ {
+@@ -168,12 +163,6 @@
+ *(dirname = fn) = ch;
+ if (! d)
+ {
+- if (print_possibilities < 0)
+- {
+- putchar ('\n');
+- return 1;
+- }
+-
+ errnum = ERR_FILE_NOT_FOUND;
+ return 0;
+ }
+diff -Nur grub-0.97-dirs/stage2/fsys_xfs.c grub-0.97-wildcards/stage2/fsys_xfs.c
+--- grub-0.97-dirs/stage2/fsys_xfs.c 2005-08-21 20:31:02.000000000 +0300
++++ grub-0.97-wildcards/stage2/fsys_xfs.c 2005-08-21 20:32:45.000000000 +0300
+@@ -534,7 +534,7 @@
+ }
+
+ int
+-xfs_dir (char *dirname)
++xfs_dir (char *dirname, void (*handle)(char *))
+ {
+ xfs_ino_t ino, parent_ino, new_ino;
+ xfs_fsize_t di_size;
+@@ -595,11 +595,9 @@
+ for (;;) {
+ cmp = (!*dirname) ? -1 : substring (dirname, name);
+ #ifndef STAGE1_5
+- if (print_possibilities && ch != '/' && cmp <= 0) {
+- if (print_possibilities > 0)
+- print_possibilities = -print_possibilities;
+- print_a_completion (name);
+- } else
++ if (handle && ch != '/' && cmp <= 0)
++ handle (name);
++ else
+ #endif
+ if (cmp == 0) {
+ parent_ino = ino;
+@@ -610,9 +608,6 @@
+ }
+ name = next_dentry (&new_ino);
+ if (name == NULL) {
+- if (print_possibilities < 0)
+- return 1;
+-
+ errnum = ERR_FILE_NOT_FOUND;
+ *rest = ch;
+ return 0;
+diff -Nur grub-0.97-dirs/stage2/shared.h grub-0.97-wildcards/stage2/shared.h
+--- grub-0.97-dirs/stage2/shared.h 2005-08-21 20:31:02.000000000 +0300
++++ grub-0.97-wildcards/stage2/shared.h 2005-08-21 20:32:45.000000000 +0300
+@@ -1012,9 +1012,11 @@
+ /* Close a file. */
+ void grub_close (void);
+
+-/* List the contents of the directory that was opened with GRUB_OPEN,
+- printing all completions. */
+-int dir (char *dirname);
++/* List the contents of DIRECTORY. */
++int dir (char *dirname, void (*handle)(char *));
++
++/* Wildcard expand the last pathname component of GLOB. */
++char *wildcard (char *glob, int *len);
+
+ int set_bootdev (int hdbias);
+
+diff -Nur grub-0.97-dirs/stage2/stage2.c grub-0.97-wildcards/stage2/stage2.c
+--- grub-0.97-dirs/stage2/stage2.c 2005-08-21 20:31:02.000000000 +0300
++++ grub-0.97-wildcards/stage2/stage2.c 2005-08-21 20:33:24.000000000 +0300
+@@ -1243,6 +1243,230 @@
+ }
+
+
++char *wildcard_prefix, *wildcard_suffix;
++char wildcard_matches[1024], *end_wildcard_matches;
++
++static void wildcard_handler(char *name);
++
++/* Match one directory entry against the current wildcard. If the entry
++ matches, store it in WILDCARD_MATCHES. Silently ignore entries that
++ don't fit into WILDCARD_MATCHES anymore. */
++static void
++wildcard_handler(char *name)
++{
++ char *n = name, *p = wildcard_prefix;
++
++ while (*p && *p == *n)
++ {
++ p++;
++ n++;
++ }
++ if (*p)
++ return; /* prefix mismatch */
++
++ p = name + grub_strlen (name) - grub_strlen (wildcard_suffix);
++ /* [n .. p) is the part matching the asterisk */
++
++ if (p < n || grub_strcmp (p, wildcard_suffix) != 0)
++ return; /* suffix mismatch */
++
++ /* store this match */
++ if (p - n + 1 > sizeof (wildcard_matches) -
++ (end_wildcard_matches - wildcard_matches))
++ return; /* out of space */
++ while (n < p)
++ *end_wildcard_matches++ = *n++;
++ *end_wildcard_matches++ = 0;
++}
++
++/* Wildcard expand the GLOB argument. Return NULL upon failure, or
++ a list of 0-terminated expansions, terminated by a zero-length string. */
++char *
++wildcard (char *glob, int *len)
++{
++ char path[128], *p;
++ int ret;
++
++ end_wildcard_matches = wildcard_matches;
++ if (grub_strlen (glob) + 1 > sizeof (path)) {
++ errnum = ERR_FILELENGTH;
++ return NULL; /* cannot handle pathnames this long */
++ }
++ grub_strcpy (path, glob);
++ p = path;
++ while (*p)
++ p++;
++ wildcard_suffix = p;
++ while (p > path && *p != '/')
++ p--;
++ if (*p != '/')
++ {
++ errnum = ERR_BAD_FILETYPE;
++ return NULL; /* Cannot wildcard device names */
++ }
++ *(++p) = 0;
++ wildcard_prefix = glob + (p - path);
++ for (p = wildcard_prefix;; p++)
++ {
++ if (*p == 0)
++ {
++ /* We cannot do exact matches: this cannot be represented in the
++ result list. */
++ return NULL;
++ }
++ else if (*p == '*')
++ {
++ *p++ = 0;
++ wildcard_suffix = p;
++ break;
++ }
++ }
++
++ ret = dir (path, wildcard_handler);
++ /* restore original argument */
++ wildcard_prefix[grub_strlen (wildcard_prefix)] = '*';
++ if (!ret)
++ return NULL;
++ *len = end_wildcard_matches - wildcard_matches;
++ return wildcard_matches;
++}
++
++static int inplace_sort_nextint(char **p);
++
++static int inplace_sort_nextint(char **p)
++{
++ int i = 0;
++
++ while (**p && **p < '0' && **p > '9') *p++;
++ if (!**p) return -1;
++ while (**p && **p >= '0' && **p <= '9')
++ {
++ i = i * 10 + **p - '0';
++ *p++;
++ }
++ return i;
++}
++
++static int inplace_sort_strcmp(char *l, char *r);
++
++static int
++inplace_sort_strcmp(char *l, char *r)
++{
++ char *lp = l;
++ char *rp = r;
++ int li, ri;
++
++ do
++ {
++ li = inplace_sort_nextint(&lp);
++ ri = inplace_sort_nextint(&rp);
++ if (li > ri) return 1;
++ if (ri > li) return -1;
++ }
++ while (li != -1 || ri != -1);
++ return 0;
++}
++
++#define skip(str) ((str) + grub_strlen (str) + 1)
++
++static void inplace_sort (char *str, int len);
++
++static void
++inplace_sort (char *str, int len)
++{
++ int m, n = 0;
++ char *s, *t;
++
++ /* we use x as temporary storage */
++ char *x = str + len;
++
++ for (s = str; s < x; s = skip (s))
++ n++;
++
++ for (; n >= 2; n--)
++ {
++ s = str;
++ t = skip (s);
++
++ for (m = n; m >= 2; m--)
++ {
++ if (inplace_sort_strcmp (s, t) < 0)
++ {
++ int ls = skip (s) - s;
++ int lt = skip (t) - t;
++
++ memcpy (x, s, ls);
++ grub_memmove (s + ls, s + lt, t - (s + ls));
++ memcpy (s, t, lt);
++ t = t + lt - ls;
++ memcpy (t, x, ls);
++ }
++ s = t;
++ t = skip (t);
++ }
++ }
++}
++
++#undef skip
++
++static int this_config_len (const char *config);
++static int
++this_config_len (const char *config)
++{
++ const char *c = config;
++ while (*c)
++ {
++ while (*c)
++ c++;
++ c++;
++ }
++ c++;
++ return c - config;
++}
++
++static const char * expand_asterisks (const char *str, int *len,
++ const char *subst);
++
++/* Expand all asterisks (*) in a menu entry or commands section with its
++ substitution. Use a backslash as escape character. */
++static const char *
++expand_asterisks (const char *str, int *len, const char *subst)
++{
++ static char buffer[1024];
++ char *b = buffer, escaped = 0;
++ const char *end = str + *len;
++
++ while (str < end)
++ {
++ if (*str == '*' && !escaped)
++ {
++ if (b - buffer + grub_strlen (subst) > sizeof (buffer))
++ {
++ errnum = ERR_FILELENGTH;
++ return NULL;
++ }
++ grub_strcpy (b, subst);
++ b += grub_strlen (subst);
++ }
++ else if (*str == '\\' && !escaped)
++ escaped = 1;
++ else
++ {
++ escaped = 0;
++ if (b - buffer + 1 > sizeof (buffer))
++ {
++ errnum = ERR_FILELENGTH;
++ return NULL;
++ }
++ *b++ = *str;
++ }
++ str++;
++ }
++ *len = b - buffer;
++
++ return buffer;
++}
++
+ /* This is the starting function in C. */
+ void
+ cmain (void)
+@@ -1262,6 +1486,97 @@
+ menu_entries = (char *) MENU_BUF;
+ init_config ();
+ }
++
++ auto void expand_wildcard_entries (void);
++ void expand_wildcard_entries (void)
++ {
++ char *config_entry = config_entries;
++ char *menu_entry = menu_entries;
++
++ while (*menu_entry)
++ {
++ char *command = config_entry;
++
++ do
++ {
++ char *c = command;
++ const char *w = "wildcard";
++
++ while (*w && *c == *w)
++ {
++ c++;
++ w++;
++ }
++ if (*w == 0 && (*c == ' ' || *c == '\t' || *c == '='))
++ {
++ int len, wlen;
++
++ /* This is a wildcard command. Advance to the argument. */
++ while (*c == ' ' || *c == '\t' || *c == '=')
++ c++;
++
++ /* Expand wildcard entry. */
++ w = wildcard (c, &wlen);
++ if (w)
++ inplace_sort (w, wlen);
++
++ /* Remove the wildcard command from the command section;
++ it has no meaning beyond the wildcard expansion just
++ performed. */
++ len = grub_strlen (command) + 1;
++ grub_memmove (command, command + len,
++ config_len - (command - config_entries));
++ config_len -= len;
++
++ while (w && wlen)
++ {
++ /* Insert expansion before the wildcard entry in the
++ list of entry names. */
++ len = grub_strlen (menu_entry) + 1;
++ const char *x = expand_asterisks (menu_entry, &len, w);
++ grub_memmove (menu_entry + len, menu_entry,
++ menu_len - (menu_entry - menu_entries));
++ memcpy (menu_entry, x, len);
++ menu_entry += len;
++ menu_len += len;
++
++ /* Insert expansion before the wildcard command section
++ in the list of command sections. */
++ len = this_config_len (config_entry);
++ x = expand_asterisks (config_entry, &len, w);
++ grub_memmove (config_entry + len, config_entry,
++ config_len - (config_entry -
++ config_entries));
++ memcpy (config_entry, x, len);
++ config_entry += len;
++ config_len += len;
++
++ num_entries++;
++ wlen -= grub_strlen (w) + 1;
++ w += grub_strlen (w) + 1;
++ }
++
++ /* Remove the wildcard command section; it has just
++ been expanded. */
++ len = grub_strlen (menu_entry) + 1;
++ grub_memmove (menu_entry, menu_entry + len,
++ menu_len - (menu_entry - menu_entries));
++ menu_len -= len;
++
++ len = this_config_len(config_entry);
++ grub_memmove (config_entry, config_entry + len,
++ config_len - (config_entry - config_entries));
++ config_len -= len;
++
++ num_entries--;
++ }
++ command += grub_strlen (command) + 1;
++ }
++ while (*command);
++ menu_entry += grub_strlen (menu_entry) + 1;
++ config_entry += this_config_len(config_entry);
++ }
++ }
+
+ /* Initialize the environment for restarting Stage 2. */
+ grub_setjmp (restart_env);
+@@ -1414,8 +1729,16 @@
+ config_len = prev_config_len;
+ }
+
++ if (is_preset)
++ close_preset_menu ();
++ else
++ grub_close ();
++
+ menu_entries[menu_len++] = 0;
+ config_entries[config_len++] = 0;
++
++ expand_wildcard_entries();
++
+ grub_memmove (config_entries + config_len, menu_entries,
+ menu_len);
+ menu_entries = config_entries + config_len;
+@@ -1456,11 +1779,6 @@
+ else
+ default_entry = 0;
+ }
+-
+- if (is_preset)
+- close_preset_menu ();
+- else
+- grub_close ();
+ }
+ while (is_preset);
+ }