All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] User definable terminfo support
@ 2005-12-28  0:34 Omniflux
  2005-12-31 17:02 ` Marco Gerards
  2006-01-02 19:44 ` Marco Gerards
  0 siblings, 2 replies; 9+ messages in thread
From: Omniflux @ 2005-12-28  0:34 UTC (permalink / raw)
  To: The development of GRUB 2

[-- Attachment #1: Type: text/plain, Size: 1005 bytes --]

Attached are three patches....

enable-serial-module.diff
  * conf/i386-pc.rmk (pkgdata_MODULES): Add serial.mod.


strcasecmp.diff
  * kern/misc.c (grub_strcasecmp): New function.
  * include/grub/misc.h (grub_strcasecmp): New function prototype.


terminfo-definition-support.diff
  * term/terminfo.c: Replaced static vt100 definition with user 
definable definition support.
  * include/grub/terminfo.h (grub_terminfo_get_current): Removed.
  (grub_terminfo_set_current): Likewise


As with the previous serial patch, some code here is based on the 
terminfo support from GRUB Legacy. Any mistakes, however, are mine. :)

I would appreciate comments and suggestions, especially for the short 
parameter selection for the reverse video and cursor options.

I tried not using a short parameter, but state[x].set does not get set 
in that case, or I tried incorrectly.


Also can someone point me to instructions for creating a menu so that I 
may test other then the command line?

Thanks!

-- 
Omniflux

[-- Attachment #2: terminfo-definition-support.diff --]
[-- Type: text/plain, Size: 17880 bytes --]

diff -uNr grub2.cvs/include/grub/terminfo.h grub2/include/grub/terminfo.h
--- grub2.cvs/include/grub/terminfo.h	2005-09-03 10:54:26.000000000 -0600
+++ grub2/include/grub/terminfo.h	2005-12-27 16:57:57.000000000 -0700
@@ -23,9 +23,6 @@
 #include <grub/err.h>
 #include <grub/types.h>
 
-char *grub_terminfo_get_current (void);
-grub_err_t grub_terminfo_set_current (const char *);
-
 void grub_terminfo_gotoxy (grub_uint8_t x, grub_uint8_t y);
 void grub_terminfo_cls (void);
 void grub_terminfo_reverse_video_on (void);
diff -uNr grub2.cvs/term/terminfo.c grub2/term/terminfo.c
--- grub2.cvs/term/terminfo.c	2005-11-13 08:47:09.000000000 -0700
+++ grub2/term/terminfo.c	2005-12-27 16:57:57.000000000 -0700
@@ -33,78 +33,244 @@
 #include <grub/terminfo.h>
 #include <grub/tparm.h>
 
+/* A terminfo definition.  */
 struct terminfo
 {
   char *name;
-
   char *gotoxy;
   char *cls;
   char *reverse_video_on;
   char *reverse_video_off;
   char *cursor_on;
   char *cursor_off;
+
+  struct terminfo *next;
 };
+typedef struct terminfo terminfo_t;
+
+/* The list of terminfo definitions.  */
+static terminfo_t *terminfo_list;
+
+/* The current terminfo definition.  */
+static terminfo_t *cur_terminfo;
+
+/* Argument options.  */
+static const struct grub_arg_option options[] =
+{
+  {"add",            'a', 0, "Add a definition", 0, 0},
+  {"modify",         'm', 0, "Modify a definition", 0, 0},
+  {"delete",         'd', 0, "Delete a definition", 0, 0},
+  {"view",           'v', 0, "View a definition", 0, 0},
+  {"cursor-address", 'p', 0, "Sequence to position cursor", 0, ARG_TYPE_STRING},
+  {"clear-screen",   'c', 0, "Sequence to clear screen", 0, ARG_TYPE_STRING},
+  {"reverse-on",     'w', 0, "Sequence to enable reverse video mode", 0, ARG_TYPE_STRING},
+  {"reverse-off",    'x', 0, "Sequence to disable reverse video mode", 0, ARG_TYPE_STRING},
+  {"cursor-on",      'y', 0, "Sequence to enable cursor", 0, ARG_TYPE_STRING},
+  {"cursor-off",     'z', 0, "Sequence to disable cursor", 0, ARG_TYPE_STRING},
+  {0, 0, 0, 0, 0, 0}
+};
+
+/* Add new terminfo definition to list.  */
+static grub_err_t
+add_definition (terminfo_t *ti)
+{
+  terminfo_t *p;
 
-static struct terminfo term;
+  for (p = terminfo_list; p; p = p->next)
+    if (grub_strcasecmp (p->name, ti->name) == 0)
+      return grub_error (GRUB_ERR_BAD_ARGUMENT, "terminfo definition name already exists.");
 
-/* Get current terminfo name.  */
-char *
-grub_terminfo_get_current (void)
+  ti->next = terminfo_list;
+  terminfo_list = ti;
+
+  return GRUB_ERR_NONE;
+}
+
+/* Delete terminfo definition from list.  */
+static void
+delete_definition (terminfo_t *ti)
 {
-  return term.name;
+  terminfo_t *p, *q;
+
+  for (p = 0, q = terminfo_list; q; p = q, q = q->next)
+    if (q == ti)
+      {
+        if (p)
+          p->next = q->next;
+        else
+          terminfo_list = q->next;
+
+        grub_free (q->name);
+        grub_free (q->gotoxy);
+        grub_free (q->cls);
+        grub_free (q->reverse_video_on);
+        grub_free (q->reverse_video_off);
+        grub_free (q->cursor_on);
+        grub_free (q->cursor_off);
+        grub_free (q);
+
+        break;
+      }
 }
 
-/* Free *PTR and set *PTR to NULL, to prevent double-free.  */
+/* Iterate through terminfo definitions.  */
 static void
-grub_terminfo_free (char **ptr)
+iterate_definitions (int (*hook) (terminfo_t *ti))
 {
-  grub_free (*ptr);
-  *ptr = 0;
+  terminfo_t *p;
+
+  for (p = terminfo_list; p; p = p->next)
+    if (hook (p))
+      break;
+}
+
+/* Escape a string.  */
+static char *
+escape_string (const char *in)
+{
+  char *q, *new_string;
+
+  new_string = (char *) grub_malloc (grub_strlen (in) * 4 + 1);	/* Max escaped string size is 4x.  */
+
+  for (q = new_string; *in; in++, q++)
+    {
+      switch (*in)
+        {
+          case '\a':	/* Common escape sequences.  */
+            *q++ = '\\';
+            *q = 'a';
+            break;
+	  case '\b':
+            *q++ = '\\';
+            *q = 'b';
+            break;
+          case '\e':
+            *q++ = '\\';
+            *q = 'e';
+            break;
+          case '\f':
+            *q++ = '\\';
+            *q = 'f';
+            break;
+          case '\n':
+            *q++ = '\\';
+            *q = 'n';
+            break;
+          case '\r':
+            *q++ = '\\';
+            *q = 'r';
+            break;
+          case '\t':
+            *q++ = '\\';
+            *q = 't';
+            break;
+          case '\v':
+            *q++ = '\\';
+            *q = 'v';
+            break;
+          case '\\':
+            *q++ = '\\';
+            *q = '\\';
+            break;
+          case 32 ... 91:	/* Typeable character. Leave as is.  */
+          case 93 ... 126:
+            *q = *in;
+            break;
+          default:	/* Convert to octal sequence.  */
+            *q++ = '\\';
+            *q++ = ((*in >> 8) & 7) + '0';
+            *q++ = ((*in >> 4) & 7) + '0';
+            *q = ((*in >> 0) & 7) + '0';
+        }
+    }
+
+  *q = 0;
+  return grub_realloc (new_string, grub_strlen (new_string) + 1);	/* Shrink alloc.  */
 }
 
-/* Set current terminfo type.  */
-grub_err_t
-grub_terminfo_set_current (const char *str)
-{
-  /* TODO
-   * Lookup user specified terminfo type. If found, set term variables
-   * as appropriate. Otherwise return an error.
-   *
-   * How should this be done?
-   *  a. A static table included in this module.
-   *     - I do not like this idea.
-   *  b. A table stored in the configuration directory.
-   *     - Users must convert their terminfo settings if we have not already.
-   *  c. Look for terminfo files in the configuration directory.
-   *     - /usr/share/terminfo is 6.3M on my system.
-   *     - /usr/share/terminfo is not on most users boot partition.
-   *     + Copying the terminfo files you want to use to the grub
-   *       configuration directory is easier then (b).
-   *  d. Your idea here.
-   */
-
-  /* Free previously allocated memory.  */
-  grub_terminfo_free (&term.name);
-  grub_terminfo_free (&term.gotoxy);
-  grub_terminfo_free (&term.cls);
-  grub_terminfo_free (&term.reverse_video_on);
-  grub_terminfo_free (&term.reverse_video_off);
-  grub_terminfo_free (&term.cursor_on);
-  grub_terminfo_free (&term.cursor_off);
-  
-  if (grub_strcmp ("vt100", str) == 0)
-    {
-      term.name              = grub_strdup ("vt100");
-      term.gotoxy            = grub_strdup ("\e[%i%p1%d;%p2%dH");
-      term.cls               = grub_strdup ("\e[H\e[J");
-      term.reverse_video_on  = grub_strdup ("\e[7m");
-      term.reverse_video_off = grub_strdup ("\e[m");
-      term.cursor_on         = grub_strdup ("\e[?25l");
-      term.cursor_off        = grub_strdup ("\e[?25h");
-      return grub_errno;
+/* Unescape a string.  */
+static char *
+unescape_string (const char *in)
+{
+  char *q, *new_string;
+
+  new_string = (char *) grub_malloc (grub_strlen (in) + 1);	/* New string will be <= current string.  */
+
+  for (q = new_string; *in; in++, q++)
+    {
+      switch (*in)
+        {
+          case '\\':	/* Escape sequence.  */
+            in++;
+            if (*in >= '0' && *in <= '3')	/* Looks like the begining of an octal.  */
+              {
+                *q = *in - '0';
+                in++;
+                if (*in >= '0' && *in <= '7')	/* Middle of an octal.  */
+                  {
+                   *q = (*q << 3) | (*in - '0');
+                   in++;
+                   if (*in >= '0' && *in <= '7')	/* End of an octal.  */
+                     {
+                       *q = (*q << 3) | (*in - '0'); 
+                       break;
+                     }
+                   in--;	/* Was not an octal; rewind.  */
+                  }
+                in -= 2;	/* Was not an octal; rewind.  */
+                q--;	/* Nothing to put here; rewind.  */
+		break;
+              }
+            switch (*in)	/* Does not look like an octal.  */
+              {
+                case 'a':	/* Alert.  */
+                 *q = '\a';
+                 break;
+
+                case 'b':	/* Backspace.  */
+                  *q = '\b';
+                  break;
+
+                case 'e':	/* Escape.  */
+                case 'E':
+                  *q = '\e';
+                  break;
+
+                case 'f':	/* Form Feed.  */
+                  *q = '\f';
+                  break;
+
+                case 'n':	/* Newline.  */
+                  *q = '\n';
+                  break;
+
+                case 'r':	/* Carriage Return.  */
+                  *q = '\r';
+                  break;
+
+                case 't':	/* Tab.  */
+                  *q = '\t';
+                  break;
+
+                case 'v':	/* Vertical Tab.  */
+                  *q = '\v';
+                  break;
+
+                case '\\':	/* Backslash.  */
+                  *q = '\\';
+                  break;
+
+                default:	/* Anything else.  */
+                  *q = *in;
+              }
+	    break;
+
+          default:	/* Does not need any interpretation.  */
+            *q = *in;
+        }
     }
-  
-  return grub_error (GRUB_ERR_BAD_ARGUMENT, "unknown terminfo type.");
+  *q = 0;
+  return grub_realloc (new_string, grub_strlen (new_string) + 1);	/* Shrink alloc.  */
 }
 
 /* Wrapper for grub_putchar to write strings.  */
@@ -119,70 +285,273 @@
 void
 grub_terminfo_gotoxy (grub_uint8_t x, grub_uint8_t y)
 {
-  putstr (grub_terminfo_tparm (term.gotoxy, y, x));
+  putstr (grub_terminfo_tparm (cur_terminfo->gotoxy, y, x));
 }
 
 /* Clear the screen.  */
 void
 grub_terminfo_cls (void)
 {
-  putstr (grub_terminfo_tparm (term.cls));
+  putstr (grub_terminfo_tparm (cur_terminfo->cls));
 }
 
 /* Set reverse video mode on.  */
 void
 grub_terminfo_reverse_video_on (void)
 {
-  putstr (grub_terminfo_tparm (term.reverse_video_on));
+  putstr (grub_terminfo_tparm (cur_terminfo->reverse_video_on));
 }
 
 /* Set reverse video mode off.  */
 void
 grub_terminfo_reverse_video_off (void)
 {
-  putstr (grub_terminfo_tparm (term.reverse_video_off));
+  putstr (grub_terminfo_tparm (cur_terminfo->reverse_video_off));
 }
 
 /* Show cursor.  */
 void
 grub_terminfo_cursor_on (void)
 {
-  putstr (grub_terminfo_tparm (term.cursor_on));
+  putstr (grub_terminfo_tparm (cur_terminfo->cursor_on));
 }
 
 /* Hide cursor.  */
 void
 grub_terminfo_cursor_off (void)
 {
-  putstr (grub_terminfo_tparm (term.cursor_off));
+  putstr (grub_terminfo_tparm (cur_terminfo->cursor_off));
 }
 
 /* GRUB Command.  */
 
 static grub_err_t
-grub_cmd_terminfo (struct grub_arg_list *state __attribute__ ((unused)),
-		int argc, char **args)
+grub_cmd_terminfo (struct grub_arg_list *state, int argc, char **args)
 {
+  int i;
+  char *p;
+  terminfo_t *ti = 0;
+
+  auto int print_terminfo (terminfo_t *);
+  auto int find_terminfo (terminfo_t *);
+
+  int print_terminfo (terminfo_t *t)
+    {
+      grub_printf (" %s", t->name);
+      return 0;
+    }
+
+  int find_terminfo (terminfo_t *t)
+    {
+      if (grub_strcasecmp (t->name, args[0]) == 0)
+        {
+          ti = t;
+          return 1;
+        }
+
+      return 0;
+    }
+
+  i = state[0].set + state[1].set + state[2].set + state[3].set;
+
+  if (i > 1)
+    return grub_error (GRUB_ERR_INVALID_COMMAND, "add, modify, delete and view are exclusive operations.");
+
   if (argc == 0)
-  {
-    grub_printf ("Current terminfo type: %s\n", grub_terminfo_get_current());
-    return GRUB_ERR_NONE;
-  }
-  else if (argc != 1)
+    {
+      if (i == 0)
+        {
+          grub_printf ("Available terminfo definition(s):");
+            iterate_definitions (print_terminfo);
+          grub_putchar ('\n');
+          grub_printf ("Current terminfo definition: %s\n", cur_terminfo->name);
+	  
+	  return GRUB_ERR_NONE;
+        }
+      else
+        return grub_error (GRUB_ERR_BAD_ARGUMENT, "terminfo definition name must be specified.");
+    }
+
+  if (argc > 1)
     return grub_error (GRUB_ERR_BAD_ARGUMENT, "too many parameters.");
+
+  iterate_definitions (find_terminfo);
+  if ((! ti) && (! state[0].set))
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, "no such terminfo definition.");
+
+  if (ti && state[0].set)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, "terminfo definition name already exists");
+
+  if (i == 0)
+    cur_terminfo = ti;
   else
-    return grub_terminfo_set_current (args[0]);
+    {
+      if (state[0].set)	/* Add new definition.  */
+        {
+          if (! state[4].set)
+            return grub_error (GRUB_ERR_INVALID_COMMAND, "Missing mandatory option for `cursor-address'.");
+
+          p = unescape_string (state[4].arg);	/* Cursor address.  */
+          if (! *p)
+            {
+              grub_free (p);
+              return grub_error (GRUB_ERR_INVALID_COMMAND, "option `cursor-address' must not be blank.");
+            }
+
+          ti = (terminfo_t *) grub_malloc (sizeof (terminfo_t));
+	  if (! ti)
+            return grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot allocate memory for new definition."); 
+          grub_memset (ti, 0, sizeof (terminfo_t));
+
+          ti->name   = grub_strdup (args[0]);
+
+          ti->gotoxy = p;
+
+          if (state[5].set)	/* Clear screen.  */
+            ti->cls = unescape_string (state[5].arg);
+          else
+            ti->cls = grub_strdup ("");
+
+          if (state[6].set)	/* Reverse video on.  */
+            ti->reverse_video_on = unescape_string (state[6].arg);
+          else
+            ti->reverse_video_on = grub_strdup ("");
+
+          if (state[7].set)	/* Reverse video off.  */
+            ti->reverse_video_off = unescape_string (state[7].arg);
+          else
+            ti->reverse_video_off = grub_strdup ("");
+
+          if (state[8].set)	/* Cursor on.  */
+            ti->cursor_on = unescape_string (state[8].arg);
+          else
+            ti->cursor_on = grub_strdup ("");
+
+          if (state[9].set)	/* Cursor off.  */
+            ti->cursor_off = unescape_string (state[9].arg);
+          else
+            ti->cursor_off = grub_strdup ("");
+
+          add_definition (ti);
+        }
+      else if (state[1].set)	/* Modify definition.  */
+        {
+          if (state[4].set)
+            {
+              p = unescape_string (state[4].arg);
+              if (! *p)
+                {
+                  grub_free (p);
+                  return grub_error (GRUB_ERR_INVALID_COMMAND, "option `cursor-address' must not be blank.");
+                }
+
+              grub_free (ti->gotoxy);
+              ti->gotoxy = p;
+            }
+
+          if (state[5].set)
+            {
+              grub_free (ti->cls);
+              ti->cls = unescape_string (state[5].arg);
+            }
+
+          if (state[6].set)
+            {
+              grub_free (ti->reverse_video_on);
+              ti->reverse_video_on = unescape_string (state[6].arg);
+            }
+
+          if (state[7].set)
+            {
+              grub_free (ti->reverse_video_off);
+              ti->reverse_video_off = unescape_string (state[7].arg);
+            }
+
+          if (state[8].set)
+            {
+              grub_free (ti->cursor_on);
+              ti->cursor_on = unescape_string (state[8].arg);
+            }
+
+          if (state[9].set)
+            {
+              grub_free (ti->cursor_off);
+              ti->cursor_off = unescape_string (state[9].arg);
+            }
+        }
+      else if (state[2].set)	/* Delete definition.  */
+        {
+          if (cur_terminfo == ti)
+            return grub_error (GRUB_ERR_BAD_ARGUMENT, "cannot delete in use definition.");
+
+          delete_definition (ti);
+        }
+      else if (state[3].set)	/* View definition.  */
+        {
+          grub_printf ("Terminfo definition: %s\n", ti->name);
+
+          p = escape_string (ti->gotoxy);
+          grub_printf ("Cursor address: %s\n", p);
+          grub_free (p);
+
+          p = escape_string (ti->cls);
+          grub_printf ("Clear screen: %s\n", p);
+          grub_free (p);
+
+          p = escape_string (ti->reverse_video_on);
+          grub_printf ("Reverse video on: %s\n", p);
+          grub_free (p);
+
+          p = escape_string (ti->reverse_video_off);
+          grub_printf ("Reverse video off: %s\n", p);
+          grub_free (p);
+
+          p = escape_string (ti->cursor_on);
+          grub_printf ("Cursor on: %s\n", p);
+          grub_free (p);
+
+          p = escape_string (ti->cursor_off);
+          grub_printf ("Cursor off: %s\n", p);
+          grub_free (p);
+        }
+    }
+
+  return GRUB_ERR_NONE;
+}
+
+static void
+setup_defaults (void)
+{
+  terminfo_t *ti;
+
+  ti = (terminfo_t *) grub_malloc (sizeof (terminfo_t));
+  /* Do I need to test if malloc succeeded here? What do I do if it did not? */
+
+  /* Default terminal definition vt100.  */
+  ti->name              = grub_strdup ("vt100");
+  ti->gotoxy            = grub_strdup ("\e[%i%p1%d;%p2%dH");
+  ti->cls               = grub_strdup ("\e[H\e[J");
+  ti->reverse_video_on  = grub_strdup ("\e[7m");
+  ti->reverse_video_off = grub_strdup ("\e[m");
+  ti->cursor_on         = grub_strdup ("\e[?25l");
+  ti->cursor_off        = grub_strdup ("\e[?25h");
+  ti->next              = 0;
+
+  add_definition (ti);
+  cur_terminfo = ti;
 }
 
 GRUB_MOD_INIT(terminfo)
 {
   (void) mod;			/* To stop warning. */
+  setup_defaults ();
   grub_register_command ("terminfo", grub_cmd_terminfo, GRUB_COMMAND_FLAG_BOTH,
-			 "terminfo [TERM]", "Set terminfo type.", 0);
-  grub_terminfo_set_current ("vt100");
+			 "terminfo [TERMTYPE [-a|-m|-d|-v [ARGS...]]]", "Select or define terminfo definition(s). Escape sequences must be double quoted.", options);
 }
 
 GRUB_MOD_FINI(terminfo)
 {
   grub_unregister_command ("terminfo");
+  while (terminfo_list)
+    delete_definition (terminfo_list);
 }

[-- Attachment #3: enable-serial-module.diff --]
[-- Type: text/plain, Size: 548 bytes --]

diff -uNr grub2.cvs/conf/i386-pc.rmk grub2/conf/i386-pc.rmk
--- grub2.cvs/conf/i386-pc.rmk	2005-12-25 08:59:50.000000000 -0700
+++ grub2/conf/i386-pc.rmk	2005-12-27 16:56:54.000000000 -0700
@@ -115,7 +115,7 @@
 # Modules.
 pkgdata_MODULES = _chain.mod _linux.mod linux.mod normal.mod vga.mod	\
 	_multiboot.mod chain.mod multiboot.mod reboot.mod halt.mod	\
-	vbe.mod vesafb.mod vbetest.mod vbeinfo.mod play.mod
+	vbe.mod vesafb.mod vbetest.mod vbeinfo.mod play.mod serial.mod
 
 # For _chain.mod.
 _chain_mod_SOURCES = loader/i386/pc/chainloader.c

[-- Attachment #4: strcasecmp.diff --]
[-- Type: text/plain, Size: 1291 bytes --]

diff -uNr grub2.cvs/include/grub/misc.h grub2/include/grub/misc.h
--- grub2.cvs/include/grub/misc.h	2005-10-24 04:23:46.000000000 -0600
+++ grub2/include/grub/misc.h	2005-12-27 16:57:45.000000000 -0700
@@ -44,6 +44,7 @@
 int EXPORT_FUNC(grub_memcmp) (const void *s1, const void *s2, grub_size_t n);
 int EXPORT_FUNC(grub_strcmp) (const char *s1, const char *s2);
 int EXPORT_FUNC(grub_strncmp) (const char *s1, const char *s2, grub_size_t n);
+int EXPORT_FUNC(grub_strcasecmp) (const char *s1, const char *s2);
 int EXPORT_FUNC(grub_strncasecmp) (const char *s1, const char *s2, int c);
 char *EXPORT_FUNC(grub_strchr) (const char *s, int c);
 char *EXPORT_FUNC(grub_strrchr) (const char *s, int c);
diff -uNr grub2.cvs/kern/misc.c grub2/kern/misc.c
--- grub2.cvs/kern/misc.c	2005-10-27 21:14:33.000000000 -0600
+++ grub2/kern/misc.c	2005-12-27 16:57:45.000000000 -0700
@@ -209,6 +209,21 @@
 }
 
 int
+grub_strcasecmp (const char *s1, const char *s2)
+{
+  while (grub_tolower (*s1) && grub_tolower (*s2))
+    {
+      if (grub_tolower (*s1) != grub_tolower (*s2))
+         return (int) grub_tolower (*s1) - (int) grub_tolower (*s2);
+
+      s1++;
+      s2++;
+    }
+
+  return (int) *s1 - (int) *s2;
+}
+
+int
 grub_strncasecmp (const char *s1, const char *s2, int c)
 {
   int p = 1;

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2006-01-02 21:45 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2005-12-28  0:34 [PATCH] User definable terminfo support Omniflux
2005-12-31 17:02 ` Marco Gerards
2006-01-02 19:53   ` Omniflux
2006-01-02 20:37     ` Marco Gerards
2006-01-02 21:33       ` Omniflux
2006-01-02 21:43         ` Marco Gerards
2006-01-02 19:44 ` Marco Gerards
2006-01-02 20:21   ` Omniflux
2006-01-02 20:43     ` Marco Gerards

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.