grub-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Tab completion bug fixes


From: Marco Gerards
Subject: Tab completion bug fixes
Date: Sat, 22 Oct 2005 15:28:54 +0200
User-agent: Gnus/5.1007 (Gnus v5.10.7) Emacs/21.4 (gnu/linux)

Hi,

Like I said yesterday, I was fixing tab completion.  Here is a patch
for that.  It changes the kernel and the command line parsing a lot
and is a beginning for scripting support (at least for the lexer).  So
I will not commit it right away.  If I do not receive complaints
before Monday I will commit it after Monday.

i removed the old grub_split_cmdline.  It was way too hard to read and
some parts of it were required by tab completion.  Now I am using a
state machine:

http://www.xs4all.nl/~mgerards/grub_cmd_state.png

As you can see it contains all state transitions, for example:

foo "bar ${baz}"
          ^
          |

The arrow points to the { before baz.  In the diagram this is the
transition from qvariable to qvarname2.  The old state was qvarname
(because of the $) and because of the { the next state is qvarname2.
In the sourcecode this is described by a table.  This example is
described by the following entry:

{ GRUB_PARSER_STATE_QVAR, GRUB_PARSER_STATE_QVARNAME2, '{', 0},

The last zero means that the character '{' should be discarded.

This kind of state machine is like how flex works AFAIK.  I have
written a function to determine the state transitions
(grub_parser_cmdline_state) and grub_parser_split_cmdline.  The last
replaces grub_split_cmdline.  Later it should be quite simple to write
a lexer using grub_parser_cmdline_state without using flex.

Please test these changes if you have the time.  Especially with tab
completing files with spaces in it.

Thanks,
Marco


2005-10-22  Marco Gerards  <address@hidden>

        * include/grub/parser.h: New file.

        * kern/parser.c: Likewise.

        * conf/i386-pc.rmk (kernel_img_SOURCES): Add `kern/parser.c'.
        (grub_setup_SOURCES): Likewise.
        (grub_probefs_SOURCES): Likewise.
        (grub_emu_SOURCES): Likewise.
        (kernel_img_HEADERS): Add `parser.h'.

        * conf/powerpc-ieee1275.rmk (grubof_HEADERS): Add `parser.h'.
        (grub_emu_SOURCES): Add `kern/parser.c'.
        (grubof_SOURCES): Likewise.

        * conf/sparc64-ieee1275.rmk (grubof_HEADERS): Add `parser.h'.
        (grubof_SOURCES): Add `kern/parser.c'.

        * include/grub/misc.h (grub_split_cmdline): Removed prototype.

        * kern/misc.c (grub_split_cmdline): Removed function.

        * kern/rescue.c: Include <grub/parser.h>.
        (grub_enter_rescue_mode): Use `grub_parser_split_cmdline' instead
        of `grub_split_cmdline'.

        * normal/command.c: Include <grub/parser.h>.
        (grub_command_execute):  Use `grub_parser_split_cmdline' instead
        of `grub_split_cmdline'.

        * normal/completion.c: Include <grub/parser.h>.
        (cmdline_state): New variable.
        (iterate_dir): End the filename with a quote depending on the
        command line state.
        (get_state): new function.
        (grub_normal_do_completion): Use `grub_parser_split_cmdline' to
        split the arguments and determine the current argument.  When the
        argument string is not quoted, escape all spaces.


Index: conf/i386-pc.rmk
===================================================================
RCS file: /cvsroot/grub/grub2/conf/i386-pc.rmk,v
retrieving revision 1.49
diff -u -p -u -p -r1.49 i386-pc.rmk
--- conf/i386-pc.rmk    9 Oct 2005 13:03:53 -0000       1.49
+++ conf/i386-pc.rmk    22 Oct 2005 13:08:33 -0000
@@ -26,12 +26,12 @@ diskboot_img_LDFLAGS = -nostdlib -Wl,-N,
 kernel_img_SOURCES = kern/i386/pc/startup.S kern/main.c kern/device.c \
        kern/disk.c kern/dl.c kern/file.c kern/fs.c kern/err.c \
        kern/misc.c kern/mm.c kern/loader.c kern/rescue.c kern/term.c \
-       kern/i386/dl.c kern/i386/pc/init.c kern/partition.c \
+       kern/i386/dl.c kern/i386/pc/init.c kern/parser.c kern/partition.c \
        kern/env.c disk/i386/pc/biosdisk.c \
        term/i386/pc/console.c \
        symlist.c
 kernel_img_HEADERS = arg.h boot.h device.h disk.h dl.h elf.h env.h err.h \
-       file.h fs.h kernel.h loader.h misc.h mm.h net.h partition.h \
+       file.h fs.h kernel.h loader.h misc.h mm.h net.h parser.h partition.h \
        pc_partition.h rescue.h symbol.h term.h types.h \
        machine/biosdisk.h machine/boot.h machine/console.h machine/init.h \
        machine/memory.h machine/loader.h machine/time.h machine/vga.h \
@@ -63,7 +63,7 @@ grub_mkimage_LDFLAGS = $(LIBLZO)
 grub_setup_SOURCES = util/i386/pc/grub-setup.c util/i386/pc/biosdisk.c \
        util/misc.c util/i386/pc/getroot.c kern/device.c kern/disk.c \
        kern/err.c kern/misc.c fs/fat.c fs/ext2.c fs/xfs.c fs/affs.c  \
-       fs/sfs.c kern/partition.c partmap/pc.c \
+       fs/sfs.c kern/parser.c kern/partition.c partmap/pc.c \
        fs/ufs.c fs/minix.c fs/hfs.c fs/jfs.c kern/file.c kern/fs.c kern/env.c 
fs/fshelp.c
 
 # For grub-mkdevicemap.
@@ -73,9 +73,9 @@ grub_mkdevicemap_SOURCES = util/i386/pc/
 grub_probefs_SOURCES = util/i386/pc/grub-probefs.c     \
        util/i386/pc/biosdisk.c util/misc.c util/i386/pc/getroot.c      \
        kern/device.c kern/disk.c kern/err.c kern/misc.c fs/fat.c       \
-       fs/ext2.c kern/partition.c partmap/pc.c fs/ufs.c fs/minix.c     \
-       fs/hfs.c fs/jfs.c kern/fs.c kern/env.c fs/fshelp.c fs/xfs.c     \
-       fs/affs.c fs/sfs.c
+       fs/ext2.c kern/parser.c kern/partition.c partmap/pc.c fs/ufs.c  \
+       fs/minix.c fs/hfs.c fs/jfs.c kern/fs.c kern/env.c fs/fshelp.c   \
+       fs/xfs.c fs/affs.c fs/sfs.c
 
 # For grub-emu.
 grub_emu_SOURCES = commands/boot.c commands/cat.c commands/cmp.c       \
@@ -89,7 +89,7 @@ grub_emu_SOURCES = commands/boot.c comma
        io/gzio.c                                                       \
        kern/device.c kern/disk.c kern/dl.c kern/env.c kern/err.c       \
        kern/file.c kern/fs.c kern/loader.c kern/main.c kern/misc.c     \
-       kern/partition.c kern/rescue.c kern/term.c                      \
+       kern/parser.c kern/partition.c kern/rescue.c kern/term.c        \
        normal/arg.c normal/cmdline.c normal/command.c                  \
        normal/completion.c normal/context.c normal/main.c              \
        normal/menu.c normal/menu_entry.c normal/misc.c                 \
Index: conf/powerpc-ieee1275.rmk
===================================================================
RCS file: /cvsroot/grub/grub2/conf/powerpc-ieee1275.rmk,v
retrieving revision 1.44
diff -u -p -u -p -r1.44 powerpc-ieee1275.rmk
--- conf/powerpc-ieee1275.rmk   9 Oct 2005 13:03:53 -0000       1.44
+++ conf/powerpc-ieee1275.rmk   22 Oct 2005 13:08:33 -0000
@@ -10,7 +10,7 @@ MOSTLYCLEANFILES += grubof_symlist.c ker
 DEFSYMFILES += kernel_syms.lst
 
 grubof_HEADERS = arg.h boot.h device.h disk.h dl.h elf.h env.h err.h \
-       file.h fs.h kernel.h misc.h mm.h net.h rescue.h symbol.h \
+       file.h fs.h kernel.h misc.h mm.h net.h parser.h rescue.h symbol.h \
        term.h types.h powerpc/libgcc.h loader.h \
        partition.h pc_partition.h ieee1275/ieee1275.h machine/time.h \
        machine/kernel.h
@@ -44,9 +44,9 @@ grub_emu_SOURCES = commands/boot.c comma
        io/gzio.c                                                       \
        kern/device.c kern/disk.c kern/dl.c kern/env.c kern/err.c       \
        kern/file.c kern/fs.c kern/loader.c kern/main.c kern/misc.c     \
-       kern/partition.c kern/rescue.c kern/term.c                      \
+       kern/parser.c kern/partition.c kern/rescue.c kern/term.c        \
        normal/arg.c normal/cmdline.c normal/command.c                  \
-       normal/completion.c normal/context.c    \
+       normal/completion.c normal/context.c                            \
        normal/main.c normal/menu.c normal/menu_entry.c normal/misc.c   \
        partmap/amiga.c partmap/apple.c partmap/pc.c partmap/sun.c      \
        util/console.c util/grub-emu.c util/misc.c                      \
@@ -56,13 +56,13 @@ grub_emu_SOURCES = commands/boot.c comma
 grub_emu_LDFLAGS = $(LIBCURSES)
 
 grubof_SOURCES = kern/powerpc/ieee1275/crt0.S kern/powerpc/ieee1275/cmain.c \
-       kern/ieee1275/ieee1275.c kern/main.c kern/device.c \
-       kern/disk.c kern/dl.c kern/file.c kern/fs.c kern/err.c \
-       kern/misc.c kern/mm.c kern/loader.c kern/rescue.c kern/term.c \
-       kern/powerpc/ieee1275/init.c term/ieee1275/ofconsole.c \
-       kern/powerpc/ieee1275/openfw.c disk/ieee1275/ofdisk.c \
-       kern/partition.c kern/env.c kern/powerpc/dl.c grubof_symlist.c \
-       kern/powerpc/cache.S
+       kern/ieee1275/ieee1275.c kern/main.c kern/device.c              \
+       kern/disk.c kern/dl.c kern/file.c kern/fs.c kern/err.c          \
+       kern/misc.c kern/mm.c kern/loader.c kern/rescue.c kern/term.c   \
+       kern/powerpc/ieee1275/init.c term/ieee1275/ofconsole.c          \
+       kern/powerpc/ieee1275/openfw.c disk/ieee1275/ofdisk.c           \
+       kern/parser.c kern/partition.c kern/env.c kern/powerpc/dl.c     \
+       grubof_symlist.c kern/powerpc/cache.S
 grubof_HEADERS = grub/powerpc/ieee1275/ieee1275.h
 grubof_CFLAGS = $(COMMON_CFLAGS)
 grubof_ASFLAGS = $(COMMON_ASFLAGS)
Index: conf/sparc64-ieee1275.rmk
===================================================================
RCS file: /cvsroot/grub/grub2/conf/sparc64-ieee1275.rmk,v
retrieving revision 1.2
diff -u -p -u -p -r1.2 sparc64-ieee1275.rmk
--- conf/sparc64-ieee1275.rmk   21 Aug 2005 19:33:14 -0000      1.2
+++ conf/sparc64-ieee1275.rmk   22 Oct 2005 13:08:33 -0000
@@ -10,7 +10,7 @@ MOSTLYCLEANFILES += grubof_symlist.c ker
 DEFSYMFILES += kernel_syms.lst
 
 grubof_HEADERS = arg.h boot.h device.h disk.h dl.h elf.h env.h err.h \
-       file.h fs.h kernel.h misc.h mm.h net.h rescue.h symbol.h \
+       file.h fs.h kernel.h misc.h mm.h net.h parser.h rescue.h symbol.h \
        term.h types.h loader.h \
        partition.h pc_partition.h ieee1275/ieee1275.h machine/time.h
 
@@ -59,7 +59,7 @@ grubof_SOURCES = kern/sparc64/ieee1275/i
        kern/rescue.c kern/term.c term/ieee1275/ofconsole.c \
        kern/sparc64/ieee1275/openfw.c disk/ieee1275/ofdisk.c \
        kern/partition.c kern/env.c kern/sparc64/dl.c grubof_symlist.c \
-       kern/sparc64/cache.c
+       kern/sparc64/cache.c kern/parser.c
 grubof_HEADERS = grub/sparc64/ieee1275/ieee1275.h
 grubof_CFLAGS = $(COMMON_CFLAGS)
 grubof_ASFLAGS = $(COMMON_ASFLAGS)
Index: include/grub/misc.h
===================================================================
RCS file: /cvsroot/grub/grub2/include/grub/misc.h,v
retrieving revision 1.16
diff -u -p -u -p -r1.16 misc.h
--- include/grub/misc.h 21 Aug 2005 07:22:51 -0000      1.16
+++ include/grub/misc.h 22 Oct 2005 13:08:33 -0000
@@ -76,8 +76,4 @@ grub_ssize_t EXPORT_FUNC(grub_utf8_to_uc
                                             const grub_uint8_t *src,
                                             grub_size_t size);
 
-grub_err_t EXPORT_FUNC(grub_split_cmdline) (const char *str, 
-                                           grub_err_t (* getline) (char **),
-                                           int *argc, char ***argv);
-
 #endif /* ! GRUB_MISC_HEADER */
Index: include/grub/parser.h
===================================================================
RCS file: include/grub/parser.h
diff -N include/grub/parser.h
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ include/grub/parser.h       22 Oct 2005 13:08:33 -0000
@@ -0,0 +1,68 @@
+/* parser.h - prototypes for the command line parser.  */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2005  Free Software Foundation, 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.
+ */
+
+#ifndef GRUB_PARSER_HEADER
+#define GRUB_PARSER_HEADER     1
+
+#include <grub/types.h>
+#include <grub/err.h>
+
+/* All the states for the command line.  */
+typedef enum
+  {
+    GRUB_PARSER_STATE_TEXT = 1,
+    GRUB_PARSER_STATE_ESC,
+    GRUB_PARSER_STATE_QUOTE,
+    GRUB_PARSER_STATE_DQUOTE,
+    GRUB_PARSER_STATE_VAR,
+    GRUB_PARSER_STATE_VARNAME,
+    GRUB_PARSER_STATE_VARNAME2,
+    GRUB_PARSER_STATE_QVAR,
+    GRUB_PARSER_STATE_QVARNAME,
+    GRUB_PARSER_STATE_QVARNAME2
+  } grub_parser_state_t;
+
+/* A single state transition.  */
+struct grub_parser_state_transition
+{
+  /* The state that is looked up.  */
+  grub_parser_state_t from_state;
+
+  /* The next state, determined by FROM_STATE and INPUT.  */
+  grub_parser_state_t to_state;
+
+  /* The input that will determine the next state from FROM_STATE.  */
+  char input;
+
+  /* If set to 1, the input is valid and should be used.  */
+  int keep_value;
+};
+
+/* Determines the state following STATE, determined by C.  */
+grub_parser_state_t
+EXPORT_FUNC (grub_parser_cmdline_state) (grub_parser_state_t state,
+                                        char c, char *result);
+
+grub_err_t
+EXPORT_FUNC (grub_parser_split_cmdline) (const char *cmdline,
+                                        grub_err_t (*getline) (char **),
+                                        int *argc, char ***argv);
+
+#endif /* ! GRUB_PARSER_HEADER */
Index: kern/misc.c
===================================================================
RCS file: /cvsroot/grub/grub2/kern/misc.c,v
retrieving revision 1.23
diff -u -p -u -p -r1.23 misc.c
--- kern/misc.c 21 Aug 2005 07:22:51 -0000      1.23
+++ kern/misc.c 22 Oct 2005 13:08:33 -0000
@@ -1,7 +1,7 @@
 /* misc.c - definitions of misc functions */
 /*
  *  GRUB  --  GRand Unified Bootloader
- *  Copyright (C) 1999,2000,2001,2002,2003,2004  Free Software Foundation, Inc.
+ *  Copyright (C) 1999,2000,2001,2002,2003,2004,2005  Free Software 
Foundation, Inc.
  *
  *  GRUB is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -873,226 +873,4 @@ grub_utf8_to_ucs4 (grub_uint32_t *dest, 
     }
 
   return p - dest;
-}
-
-grub_err_t
-grub_split_cmdline (const char *cmdline, grub_err_t (*getline) (char **),
-                   int *argc, char ***argv)
-{
-  /* XXX: Fixed size buffer, perhaps this buffer should be dynamically
-     allocated.  */
-  char buffer[1024];
-  char *bp = buffer;
-  char *rd = (char *) cmdline;
-  char unputbuf;
-  int unput = 0;
-  char *args;
-  int i;
-
-  auto char getchar (void);
-  auto void unputc (char c);
-  auto void getenvvar (void);
-  auto int getarg (void);
-
-  /* Get one character from the commandline.  If the caller reads
-     beyond the end of the string a new line will be read.  This
-     function will not chech for errors, the caller has to check for
-     grub_errno.  */
-  char getchar (void)
-    {
-      int c;
-      if (unput)
-       {
-         unput = 0;
-         return unputbuf;
-       }
-
-      if (! rd)
-       {
-         getline (&rd);
-         /* Error is ignored here, the caller will check for this
-            when it reads beyond the EOL.  */
-         c = *(rd)++;
-         return c;
-       }
-
-      c = *(rd)++;
-      if (! c)
-       {
-         rd = 0;
-         return '\n';
-       }
-
-      return c;
-    }
-
-  void unputc (char c)
-    {
-      unputbuf = c;
-      unput = 1;
-    }
-
-  /* Read a variable name from the commandline and insert its content
-     into the buffer.  */
-  void getenvvar (void)
-    {
-      char varname[100];
-      char *p = varname;
-      char *val;
-      char c;
-
-      c = getchar ();
-      if (c == '{')
-       while ((c = getchar ()) != '}')
-         *(p++) = c;
-      else
-       {
-         /* XXX: An env. variable can have characters and digits in
-            its name, are more characters allowed here?  */
-         while (c && (grub_isalpha (c) || grub_isdigit (c)))
-           {
-             *(p++) = c;
-             c = getchar ();
-           }
-         unputc (c);
-       }
-      *p = '\0';
-
-      /* The variable does not exist.  */
-      val = grub_env_get (varname);
-      if (! val)
-       return;
-
-      /* Copy the contents of the variable into the buffer.  */
-      for (p = val; *p; p++)
-       *(bp++) = *p;
-    }
-
-  /* Read one argument.  Return 1 if no variables can be read anymore,
-     otherwise return 0.  If there is an error, return 1, the caller
-     has to check grub_errno.  */
-  int getarg (void)
-    {
-      char c;
-
-      /* Skip all whitespaces before an argument.  */
-      do {
-       c = getchar ();
-      } while (c == ' ' || c == '\t');
-
-      do {
-       switch (c)
-         {
-         case '"':
-           /* Double quote.  */
-           while ((c = getchar ()))
-             {
-               if (grub_errno)
-                 return 1;
-               /* Read in an escaped character.  */
-               if (c == '\\')
-                 {
-                   c = getchar ();
-                   *(bp++) = c;
-                   continue;
-                 }
-               else if (c == '"')
-                 break;
-               /* Read a variable.  */
-               if (c == '$')
-                 {
-                   getenvvar ();
-                   continue;
-                 }
-               *(bp++) = c;
-             }
-           break;
-
-         case '\'':
-           /* Single quote.  */
-           while ((c = getchar ()) != '\'')
-             {
-               if (grub_errno)
-                 return 1;
-
-               *(bp++) = c;
-             }
-           break;
-
-         case '\n':
-           /* This was not a argument afterall.  */
-           return 1;
-
-         default:
-           /* A normal option.  */
-           while (c && (grub_isalpha (c)
-                        || grub_isdigit (c) || grub_isgraph (c)))
-             {
-               /* Read in an escaped character.  */
-               if (c == '\\')
-                 {
-                   c = getchar ();
-                   *(bp++) = c;
-                   c = getchar ();
-                   continue;
-                 }
-               /* Read a variable.  */
-               if (c == '$')
-                 {
-                   getenvvar ();
-                   c = getchar ();
-                   continue;
-                 }
-               *(bp++) = c;
-               c = getchar ();
-             }
-           unputc (c);
-
-           break;
-         }
-      } while (! grub_isspace (c) && c != '\'' && c != '"');
-
-      return 0;
-    }
-
-  /* Read in all arguments and count them.  */
-  *argc = 0;
-  while (1)
-    {
-      if (getarg ())
-       break;
-      *(bp++) = '\0';
-      (*argc)++;
-    }
-
-  /* Check if there were no errors.  */
-  if (grub_errno)
-    return grub_errno;
-
-  /* Reserve memory for the return values.  */
-  args = grub_malloc (bp - buffer);
-  if (! args)
-    return grub_errno;
-  grub_memcpy (args, buffer, bp - buffer);
-  
-  *argv = grub_malloc (sizeof (char *) * (*argc + 1));
-  if (! *argv)
-    {
-      grub_free (args);
-      return grub_errno;
-    }
-
-  /* The arguments are separated with 0's, setup argv so it points to
-     the right values.  */
-  bp = args;
-  for (i = 0; i < *argc; i++)
-    {
-      (*argv)[i] = bp;
-      while (*bp)
-       bp++;
-      bp++;
-    }
-
-  (*argc)--;
-  return 0;
 }
Index: kern/parser.c
===================================================================
RCS file: kern/parser.c
diff -N kern/parser.c
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ kern/parser.c       22 Oct 2005 13:08:33 -0000
@@ -0,0 +1,230 @@
+/* parser.c - the part of the parser that can return partial tokens */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2005  Free Software Foundation, 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 <grub/parser.h>
+#include <grub/env.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+
+
+/* All the possible state transitions on the command line.  If a
+   transition can not be found, it is assumed that there is no
+   transition and keep_value is assumed to be 1.  */
+static struct grub_parser_state_transition state_transitions[] =
+{
+  { GRUB_PARSER_STATE_TEXT, GRUB_PARSER_STATE_QUOTE, '\'', 0},
+  { GRUB_PARSER_STATE_TEXT, GRUB_PARSER_STATE_DQUOTE, '\"', 0},
+  { GRUB_PARSER_STATE_TEXT, GRUB_PARSER_STATE_VAR, '$', 0},
+  { GRUB_PARSER_STATE_TEXT, GRUB_PARSER_STATE_ESC, '\\', 0},
+
+  { GRUB_PARSER_STATE_ESC, GRUB_PARSER_STATE_TEXT, 0, 1},
+
+  { GRUB_PARSER_STATE_QUOTE, GRUB_PARSER_STATE_TEXT, '\'', 0},
+
+  { GRUB_PARSER_STATE_DQUOTE, GRUB_PARSER_STATE_TEXT, '\"', 0},
+  { GRUB_PARSER_STATE_DQUOTE, GRUB_PARSER_STATE_QVAR, '$', 0},
+
+  { GRUB_PARSER_STATE_VAR, GRUB_PARSER_STATE_VARNAME2, '{', 0},
+  { GRUB_PARSER_STATE_VAR, GRUB_PARSER_STATE_VARNAME, 0, 1},
+  { GRUB_PARSER_STATE_VARNAME, GRUB_PARSER_STATE_TEXT, ' ', 1},
+  { GRUB_PARSER_STATE_VARNAME2, GRUB_PARSER_STATE_TEXT, '}', 0},
+
+  { GRUB_PARSER_STATE_QVAR, GRUB_PARSER_STATE_QVARNAME2, '{', 0},
+  { GRUB_PARSER_STATE_QVAR, GRUB_PARSER_STATE_QVARNAME, 0, 1},
+  { GRUB_PARSER_STATE_QVARNAME, GRUB_PARSER_STATE_DQUOTE, ' ', 1},
+  { GRUB_PARSER_STATE_QVARNAME, GRUB_PARSER_STATE_TEXT, '\"', 0},
+  { GRUB_PARSER_STATE_QVARNAME2, GRUB_PARSER_STATE_DQUOTE, '}', 0},
+
+  { 0, 0, 0, 0}
+};
+
+
+/* Determines the state following STATE, determined by C.  */
+grub_parser_state_t
+grub_parser_cmdline_state (grub_parser_state_t state, char c, char *result)
+{
+  struct grub_parser_state_transition *transition;
+  struct grub_parser_state_transition *next_match = 0;
+  struct grub_parser_state_transition default_transition;
+  int found = 0;
+
+  default_transition.to_state = state;
+  default_transition.keep_value = 1;
+
+  /* Look for a good translation.  */
+  for (transition = state_transitions; transition->from_state; transition++)
+    {
+      /* An exact match was found, use it.  */
+      if (transition->from_state == state && transition->input == c)
+       {
+         found = 1;
+         break;
+       }
+
+      /* A less perfect match was found, use this one if no exact
+        match can be found.  */
+      if (transition->from_state == state && transition->input == 0)
+       next_match = transition;
+    }
+
+  if (! found)
+    {
+      if (next_match)
+       transition = next_match;
+      else
+       transition = &default_transition;
+    }
+
+  if (transition->keep_value)
+    *result = c;
+  else
+    *result = 0;
+  return transition->to_state;
+}
+
+
+grub_err_t
+grub_parser_split_cmdline (const char *cmdline, grub_err_t (*getline) (char 
**),
+                          int *argc, char ***argv)
+{
+  grub_parser_state_t state = GRUB_PARSER_STATE_TEXT;
+  /* XXX: Fixed size buffer, perhaps this buffer should be dynamically
+     allocated.  */
+  char buffer[1024];
+  char *bp = buffer;
+  char *rd = (char *) cmdline;
+  char varname[200];
+  char *vp = varname;
+  char *args;
+  int i;
+
+  auto int check_varstate (grub_parser_state_t state);
+
+  int check_varstate (grub_parser_state_t state)
+    {
+      return (state == GRUB_PARSER_STATE_VARNAME
+             || state == GRUB_PARSER_STATE_VARNAME2
+             || state == GRUB_PARSER_STATE_QVARNAME
+             || state == GRUB_PARSER_STATE_QVARNAME2);
+    }
+
+  auto void add_var (grub_parser_state_t newstate);
+
+  void add_var (grub_parser_state_t newstate)
+    {
+      char *val;
+
+      /* Check if a variable was being read in and the end of the name
+        was reached.  */
+      if (! (check_varstate (state) && !check_varstate (newstate)))
+       return;
+
+      *(vp++) = '\0';
+      val = grub_env_get (varname);
+      vp = varname;
+      if (! val)
+       return;
+      
+      /* Insert the contents of the variable in the buffer.  */
+      for (; *val; val++)
+       *(bp++) = *val;
+    }
+
+  *argc = 1;
+  do
+    {
+      if (! *rd)
+       {
+         if (getline)
+           getline (&rd);
+         else break;
+       }
+
+      for (; *rd; rd++)
+       {
+         grub_parser_state_t newstate;
+         char use;
+         
+         newstate = grub_parser_cmdline_state (state, *rd, &use);
+
+         /* If a variable was being processed and this character does
+            not describe the variable anymore, write the variable to
+            the buffer.  */
+         add_var (newstate);
+
+         if (check_varstate (newstate))
+           {
+             if (use)
+               *(vp++) = use;
+           }
+         else
+           {
+             if (newstate == GRUB_PARSER_STATE_TEXT
+                 && state != GRUB_PARSER_STATE_ESC && use == ' ')
+               {
+                 /* Don't add more than one argument if multiple
+                    spaces are used.  */
+                 if (bp != buffer && *(bp - 1))
+                   {
+                     *(bp++) = '\0';
+                     (*argc)++;
+                   }
+               }
+             else if (use)
+               *(bp++) = use;
+           }
+         state = newstate;
+       }
+    } while (state != GRUB_PARSER_STATE_TEXT && !check_varstate (state));
+  *(bp++) = '\0';
+
+  /* A special case for when the last character was part of a
+     variable.  */
+  add_var (GRUB_PARSER_STATE_TEXT);
+  
+
+  /* Reserve memory for the return values.  */
+  args = grub_malloc (bp - buffer);
+  if (! args)
+    return grub_errno;
+  grub_memcpy (args, buffer, bp - buffer);
+  
+  *argv = grub_malloc (sizeof (char *) * (*argc + 1));
+  if (! *argv)
+    {
+      grub_free (args);
+      return grub_errno;
+    }
+
+  /* The arguments are separated with 0's, setup argv so it points to
+     the right values.  */
+  bp = args;
+  for (i = 0; i < *argc; i++)
+    {
+      (*argv)[i] = bp;
+      while (*bp)
+       bp++;
+      bp++;
+    }
+
+  (*argc)--;
+
+  return 0;
+}
Index: kern/rescue.c
===================================================================
RCS file: /cvsroot/grub/grub2/kern/rescue.c,v
retrieving revision 1.15
diff -u -p -u -p -r1.15 rescue.c
--- kern/rescue.c       20 Aug 2005 07:49:01 -0000      1.15
+++ kern/rescue.c       22 Oct 2005 13:08:33 -0000
@@ -1,7 +1,7 @@
 /* rescue.c - rescue mode */
 /*
  *  GRUB  --  GRand Unified Bootloader
- *  Copyright (C) 2002, 2003  Free Software Foundation, Inc.
+ *  Copyright (C) 2002, 2003, 2005  Free Software Foundation, 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
@@ -30,6 +30,7 @@
 #include <grub/dl.h>
 #include <grub/partition.h>
 #include <grub/env.h>
+#include <grub/parser.h>
 
 #define GRUB_RESCUE_BUF_SIZE   256
 #define GRUB_RESCUE_MAX_ARGS   20
@@ -650,7 +651,7 @@ grub_enter_rescue_mode (void)
       /* Get a command line.  */
       grub_rescue_get_command_line ("grub rescue> ");
 
-      if (grub_split_cmdline (line, getline, &n, &args) || n < 0)
+      if (grub_parser_split_cmdline (line, getline, &n, &args) || n < 0)
        continue;
 
       /* In case of an assignment set the environment accordingly
Index: normal/command.c
===================================================================
RCS file: /cvsroot/grub/grub2/normal/command.c,v
retrieving revision 1.12
diff -u -p -u -p -r1.12 command.c
--- normal/command.c    14 Aug 2005 19:36:55 -0000      1.12
+++ normal/command.c    22 Oct 2005 13:08:33 -0000
@@ -24,6 +24,7 @@
 #include <grub/term.h>
 #include <grub/env.h>
 #include <grub/dl.h>
+#include <grub/parser.h>
 
 static grub_command_t grub_command_list;
 
@@ -203,7 +204,7 @@ grub_command_execute (char *cmdline, int
   char **arglist;
   int numargs;
 
-  if (grub_split_cmdline (cmdline, cmdline_get, &num, &args))
+  if (grub_parser_split_cmdline (cmdline, cmdline_get, &num, &args))
     return 0;
   
   /* In case of an assignment set the environment accordingly instead
Index: normal/completion.c
===================================================================
RCS file: /cvsroot/grub/grub2/normal/completion.c,v
retrieving revision 1.4
diff -u -p -u -p -r1.4 completion.c
--- normal/completion.c 3 Sep 2005 16:54:26 -0000       1.4
+++ normal/completion.c 22 Oct 2005 13:08:33 -0000
@@ -25,6 +25,7 @@
 #include <grub/partition.h>
 #include <grub/disk.h>
 #include <grub/file.h>
+#include <grub/parser.h>
 
 /* The current word.  */
 static char *current_word;
@@ -41,6 +42,8 @@ static const char *suffix;
 /* The callback function to print items.  */
 static void (*print_func) (const char *, grub_completion_type_t, int);
 
+/* The state the command line is in.  */
+static grub_parser_state_t cmdline_state;
 
 
 /* Add a string to the list of possible completions. COMPLETION is the
@@ -125,7 +128,15 @@ iterate_dir (const char *filename, int d
 {
   if (! dir)
     {
-      if (add_completion (filename, " ", GRUB_COMPLETION_TYPE_FILE))
+      const char *prefix;
+      if (cmdline_state == GRUB_PARSER_STATE_DQUOTE)
+       prefix = "\" ";
+      else if (cmdline_state == GRUB_PARSER_STATE_QUOTE)
+       prefix = "\' ";
+      else
+       prefix = " ";
+
+      if (add_completion (filename, prefix, GRUB_COMPLETION_TYPE_FILE))
        return 1;
     }
   else
@@ -358,6 +369,19 @@ complete_arguments (char *command)
   return 0;
 }
 
+
+static grub_parser_state_t
+get_state (const char *cmdline)
+{
+  grub_parser_state_t state = GRUB_PARSER_STATE_TEXT;
+  char use;
+
+  while (*cmdline)
+    state = grub_parser_cmdline_state (state, *(cmdline++), &use);
+  return state;
+}
+
+
 /* Try to complete the string in BUF. Return the characters that
    should be added to the string.  This command outputs the possible
    completions by calling HOOK, in that case set RESTORE to 1 so the
@@ -366,7 +390,8 @@ char *
 grub_normal_do_completion (char *buf, int *restore,
                           void (*hook) (const char *, grub_completion_type_t, 
int))
 {
-  char *first_word;
+  int argc;
+  char **argv;
 
   /* Initialize variables.  */
   match = 0;
@@ -375,45 +400,38 @@ grub_normal_do_completion (char *buf, in
   print_func = hook;
 
   *restore = 1;
-  
-  /* Find the first word.  */
-  for (first_word = buf; *first_word == ' '; first_word++)
-    ;
-
-  /* Find the delimeter of the current word.  */
-  for (current_word = first_word + grub_strlen (first_word);
-       current_word > first_word;
-       current_word--)
-    if (*current_word == ' ' || *current_word == '=')
-      break;
-  
-  if (current_word == first_word)
+
+  if (grub_parser_split_cmdline (buf, 0, &argc, &argv))
+    return 0;
+
+  current_word = argv[argc];
+
+  /* Determine the state the command line is in, depending on the
+     state, it can be determined how to complete.  */
+  cmdline_state = get_state (buf);
+
+  if (argc == 0)
     {
       /* Complete a command.  */
       if (grub_iterate_commands (iterate_command))
        goto fail;
     }
-  else
+  else if (*current_word == '-')
     {
-      current_word++;
-      
-      if (*current_word == '-')
-       {
-         if (complete_arguments (buf))
-           goto fail;
-       }
-      else if (*current_word == '(' && ! grub_strchr (current_word, ')'))
-       {
-         /* Complete a device.  */
-         if (complete_device ())
-           goto fail;
-       }
-      else
-       {
-         /* Complete a file.  */
-         if (complete_file ())
+      if (complete_arguments (buf))
+       goto fail;
+    }
+  else if (*current_word == '(' && ! grub_strchr (current_word, ')'))
+    {
+      /* Complete a device.  */
+      if (complete_device ())
            goto fail;
-       }
+    }
+  else
+    {
+      /* Complete a file.  */
+      if (complete_file ())
+       goto fail;
     }
 
   /* If more than one match is found those matches will be printed and
@@ -427,13 +445,32 @@ grub_normal_do_completion (char *buf, in
   if (match)
     {
       char *ret;
+      char *escstr;
+      char *newstr;
       int current_len;
       int match_len;
+      int spaces = 0;
 
       current_len = grub_strlen (current_word);
       match_len = grub_strlen (match);
-      ret = grub_malloc (match_len - current_len + grub_strlen (suffix) + 1);
-      grub_strcpy (ret, match + current_len);
+
+      /* Count the number of spaces that have to be escaped.  XXX:
+        More than just spaces have to be escaped.  */
+      for (escstr = match + current_len; *escstr; escstr++)
+       if (*escstr == ' ')
+         spaces++;
+
+      ret = grub_malloc (match_len - current_len + grub_strlen (suffix) + 
spaces + 1);
+      newstr = ret;
+      for (escstr = match + current_len; *escstr; escstr++)
+       {
+         if (*escstr == ' ' && cmdline_state != GRUB_PARSER_STATE_QUOTE
+             && cmdline_state != GRUB_PARSER_STATE_QUOTE)
+           *(newstr++) = '\\';
+         *(newstr++) = *escstr;
+       }
+      *newstr = '\0';
+
       if (num_found == 1)
        grub_strcat (ret, suffix);
       





reply via email to

[Prev in Thread] Current Thread [Next in Thread]