grub-devel
[Top][All Lists]
Advanced

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

Re: Idea: elimination of the normal mode (revised version)


From: Marco Gerards
Subject: Re: Idea: elimination of the normal mode (revised version)
Date: Mon, 21 Jul 2008 19:27:09 +0200
User-agent: Gnus/5.110006 (No Gnus v0.6) Emacs/21.4 (gnu/linux)

Hi,

Bean <address@hidden> writes:

> On Mon, Jul 21, 2008 at 4:02 AM, Marco Gerards <address@hidden> wrote:
>> Hi,
>>
>> Bean <address@hidden> writes:
>>
>>> First of all, we can still keep rescue and normal command. But instead
>>> of depending on normal.mod, normal command depends on module arg,
>>> which is an option parser. Also, these two type of commands are of the
>>> same command set. In fact, module arg is implemented as a pre parser,
>>> which goes through the list of arguments and extract the options. In
>>> the case of rescue command, the pre parser field is null, which means
>>> it wants to parse options itself.
>>
>> pre parser?
>
> Maybe not the best word, the idea is that normal command is just like
> rescue command, except that it goes through an extra parser that
> convert options to grub_arg_list.

Sorry, but I still don't understand this.  Do you propose:
svn move normal/arg.c kern/arg.c

?

If you propose a stripped down argument parser, how do arguments
parsers differ amongst each other?  

>>> Then, I think of a new structure to represent all configurable
>>> handlers of grub. Different types of handler have different fields,
>>> but they all share a command header:
>>
>> What is a handler and what are its responsibilities?
>
> Actually, handler is a genetic term. For terminal, there is grub_term.
> For video, there is grub_video_adapter. They're similar in operation.
> For example, there can have multiple objects, but only one is active,
> they all have basic field like init, fini, next, name. They can be
> handled by the same function. I also extend this concept to other
> objects, like script engine, console and menu interface.

Right...

>>> struct grub_handler
>>> {
>>>   .next,
>>>   .name,
>>>   .init,
>>>   .fini
>>> };
>>>
>>> Same type of handlers are linked together. We first define an enum to
>>> list all types. For example:
>>>
>>> enum {
>>>   GRUB_HANDLER_INPUT,
>>>   GRUB_HANDLER_OUTPUT,
>>>   GRUB_HANDLER_CONSOLE,
>>>   GRUB_HANDLER_MENU,
>>>   GRUB_HANDLER_SCRIPT,
>>>   GRUB_HANDLER_NUM
>>> };

Seems ok to me.

Although what if we want additional handlers.  Like image readers or
so?  We do not need to centralize this.  What about this:

handler.c:

grub_handler_t
grub_handler_add (const char *name)
{
...
}

grub_err_t
grub_handler_remove (const char *name)
{
 ...
 if (module uses handler)
   return grub_error (...);
 ...
}


/* NAME is the name of the handler, HANDLER the handler struct.  */
grub_err_t
grub_handler_register (const char *name, grub_handler_t handler)
{
...
}


/* NAME is the name of the handler, HANDLER the handler struct.  */
grub_err_t
grub_handler_deregister (const char *name, grub_handler_t handler)
{
...
}


/* HANDLER the handler struct.  Hook the hook function.  */
grub_err_t
grub_handler_register (grub_handler_t handler,
                       int (*hook) (grub_handler_t handler))
{
...
}


handler.h:

struct grub_handler
{
  struct grub_handler *next;
  const char *name;
};

#define GRUB_CREATE_HANDLER_H(name, interfaces) \
grub_err_t grub_##name##_register (struct grub_##name##_handler h);

(same for the other functions)

#define GRUB_CREATE_HANDLER_FUNCS(name, interfaces)      \
grub_err_t                                               \
grub_##name##_register (struct grub_##name##_handler h); \
{                                                        \
  grub_handler_register (name, &h->handler);
}

(same for other functions)


So the handler framework is opaque to the user.  The user just works
with this like it works now.  For example:

foo.h:

struct grub_foo
{
  /* This is *required*.  */
  grub_handler_t handle;

  /* Every FOO needs to foo.  */
  int (*foo) (int bar);
};

GRUB_CREATE_HANDLER_T ("foo", struct grub_foo);

this creates all prototypes

foo.c:

GRUB_CREATE_HANDLER ("foo", struct grub_foo);

this creates functions that are very light and are mainly there to
cast the structs back and forth so we can do type checking and get
sane warnings and maximal cleaness.  Furthermore, users can now use:

grub_foo_register (...);

^^ Every foo can register itself

grub_foo_iterate (...);

^^ You can iterate over every foo

The only "weird" thing is that when you register a handler, you have
to know its name.  For example disk.c would do:

 {
   err = grub_handler_add ("disk");
   ...
 }


So as a summary:

grub_handler_add will be used to add new handler lists,
grub_handler_remove will remove them

grub_handler_register will register specific handlers (like disk
handlers), a macro can be used to add a wrapper that passes along the
name.  grub_handler_register can fill in the mandatory member "handle"
such that later on the name is not required anymore.  Requiring
strings only at register time is not very expensive.

Sorry for the crappy code, but hopefully you get the idea, otherwise I
can hack a bit or comment on your patches :-)

>>> Then, we define an array to point to the head of handler linked list:
>>> grub_handler[GRUB_HANDLER_NUM];

Right.  Although what I proposed above you do not need an array, but
you can register anything for free :-)

>>> Head is the default selection. When we insert a new handler module, it
>>> would automatically become the new default, although we can switch
>>> back to old handler using a command.

Are you sure this is the right way to go?  This needs more feedback
from other people, I think...?

>>> Here are more details about different handlers:
>>>
>>> input:
>>> This is the input component of terminal:
>>>
>>> struct grub_handler_input
>>> {
>>>   .next,
>>>   .name,
>>>   .init,
>>>   .fini,
>>>   .checkkey,
>>>   .getkey
>>>   .flags,
>>> };

This is what we had, right?

>>> output:
>>> This is the output component of terminal:
>>>
>>> struct grub_handler_output
>>> {
>>>   .next,
>>>   .name,
>>>   .init,
>>>   .fini,
>>>   .putchar,
>>>   .getcharwidth,
>>>   .getxy,
>>>   .gotoxy,
>>>   .cls,
>>>   .setcolorstate,
>>>   .setcursor,
>>>   .flags,
>>> };
>>
>> Sometimes the input and output are tightly coupled.  How do you want
>> to handle this?
>
> Some module can export both input and output handler, like serial. But
> some only export input (like atkeyboard), or output (gfxterm).

Okay...

>>> console interface:
>>> It represent the grub console, users type commands and execute them
>>> line by line.
>>>
>>> struct grub_handler_console
>>> {
>>>   .next,
>>>   .name,
>>>   .init,
>>>   .fini,
>>>   .run
>>> };
>>>
>>> menu interface:
>>> It represent the menu, users select a menu item and execute it.
>>>
>>> struct grub_handler_menu
>>> {
>>>   .next,
>>>   .name,
>>>   .init,
>>>   .fini,
>>>   .run
>>> };
>>>
>>> script engine:
>>> It's responsible for parsing config file to get the menu list, and
>>> execution of commands.
>>>
>>> struct grub_handler_script
>>> {
>>>   .next,
>>>   .name,
>>>   .init,
>>>   .fini,
>>>   .readconfig
>>>   .getmenu
>>>   .execute
>>> };
>>
>> What I had in mind was a twofold.  I wrote a parser part that actually
>> parses the script and code to manage the AST and to execute it.  It
>> would be easy to extend the current design with other languages.
>> Although I think we should not add more than one to SVN, otherwise it
>> will become bloated.
>
> Right, but actually, there are already two script engine existing. One
> is the normal bash-like parser, another is the command line scanner in
> the rescue mode.

Ah, right.

>>> The handlers are independent of each other. When they need something,
>>> they called specific function of the default handler. For example, to
>>> read a key from the console, we can use
>>> grub_handler[GRUB_HANDLER_INPUT]->getkey. Also, to get the list of
>>> items to be displayed on screen, the menu handler can call
>>> grub_handler[GRUB_HANDLER_SCRIPT]->getmenu.
>>
>> How does this differ from what we have now?  You can register all
>> kinds of objects and deregister them.
>
> Yes, handler is just objects, but they share common fields, so that we
> can handle them together, other than writing different code for
> different object.

Right, I agree with this.

--
Marco





reply via email to

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