* [PATCH 2/2] atmodem: add serial line debugging to gatchat
@ 2009-08-05 21:33 Andres Salomon
2009-08-05 22:09 ` Denis Kenzior
0 siblings, 1 reply; 4+ messages in thread
From: Andres Salomon @ 2009-08-05 21:33 UTC (permalink / raw)
To: ofono
[-- Attachment #1: Type: text/plain, Size: 8690 bytes --]
When debugging G1 stuff, it's really much more helpful to see exactly
what's going across the serial line. A debug zone for the parser is
also useful, but that'll have to wait for denkenz's pluggable parser
stuff.
From 4402edf0bd4c782229259e7ec17d6da5809d1deb Mon Sep 17 00:00:00 2001
From: Andres Salomon <dilinger@collabora.co.uk>
Date: Wed, 5 Aug 2009 17:18:34 -0400
Subject: [PATCH 2/2] atmodem: add serial line debugging to gatchat
This patch adds debugging support to GAtChat (specifically of what's going
across the serial line). A callback is passed to g_at_chat_new*, and that
callback is called after any channel reads/writes.
From the atmodem driver perspective, this adds an additional bit to the
debugging flags, and allows the user to select it via --debug=atio.
---
drivers/atmodem/atmodem.c | 13 ++++++++++++-
gatchat/gatchat.c | 33 ++++++++++++++++++++++++++++++---
gatchat/gatchat.h | 7 +++++--
include/log.h | 5 +++++
src/log.c | 39 +++++++++++++++++++++++++++++++++------
src/main.c | 1 +
6 files changed, 86 insertions(+), 12 deletions(-)
diff --git a/drivers/atmodem/atmodem.c b/drivers/atmodem/atmodem.c
index fc255b3..56f5503 100644
--- a/drivers/atmodem/atmodem.c
+++ b/drivers/atmodem/atmodem.c
@@ -320,11 +320,21 @@ static void msg_destroy(gpointer user)
dbus_message_unref(msg);
}
+static void atmodem_debug(const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+ __ofono_vdebug(OFONO_DEBUG_AT_IO, format, ap);
+ va_end(ap);
+}
+
static void create_cb(GIOChannel *io, gboolean success, gpointer user)
{
DBusConnection *conn = ofono_dbus_get_connection();
DBusMessage *msg = user;
DBusMessage *reply;
+ GAtDebugFunc debugf;
struct at_data *at = NULL;
const char *path;
const char *target, *driver;
@@ -337,7 +347,8 @@ static void create_cb(GIOChannel *io, gboolean success, gpointer user)
at = g_new0(struct at_data, 1);
- at->parser = g_at_chat_new(io, 0);
+ debugf = ofono_debug_flag_isset(OFONO_DEBUG_AT_IO) ? atmodem_debug : NULL;
+ at->parser = g_at_chat_new(io, 0, debugf);
if (!at->parser)
goto out;
diff --git a/gatchat/gatchat.c b/gatchat/gatchat.c
index 15cc934..ff07136 100644
--- a/gatchat/gatchat.c
+++ b/gatchat/gatchat.c
@@ -96,6 +96,7 @@ struct _GAtChat {
guint read_so_far; /* Number of bytes processed */
gboolean disconnecting; /* Whether we're disconnecting */
enum chat_state state; /* Current chat state */
+ GAtDebugFunc debugf; /* debugging output function */
int flags;
char *pdu_notify; /* Unsolicited Resp w/ PDU */
GSList *response_lines; /* char * lines of the response */
@@ -713,6 +714,27 @@ static void new_bytes(GAtChat *p)
}
}
+static void g_at_chat_debug_log(GAtChat *chat, gboolean is_read,
+ char *str, gsize len)
+{
+ char type = is_read ? '<' : '>';
+ char *tmp;
+
+ if (!chat->debugf || !len)
+ return;
+
+ if (len > 1024) {
+ /* let's keep it to a reasonable length.. */
+ chat->debugf("%c (ignoring string of length %d)\n", type, len);
+ return;
+ }
+
+ str[len] = '\0'; /* XXX: is it safe to overwrite this byte? */
+ tmp = g_strescape(str, "\"");
+ chat->debugf("%c %s\n", type, tmp);
+ g_free(tmp);
+}
+
static gboolean received_data(GIOChannel *channel, GIOCondition cond,
gpointer data)
{
@@ -747,6 +769,7 @@ static gboolean received_data(GIOChannel *channel, GIOCondition cond,
buf = ring_buffer_write_ptr(chat->buf);
err = g_io_channel_read(channel, (char *) buf, toread, &rbytes);
+ g_at_chat_debug_log(chat, TRUE, buf, rbytes);
total_read += rbytes;
@@ -876,6 +899,8 @@ static gboolean can_write_data(GIOChannel *channel, GIOCondition cond,
return FALSE;
}
+ g_at_chat_debug_log(chat, FALSE, cmd->cmd + chat->cmd_bytes_written,
+ bytes_written);
chat->cmd_bytes_written += bytes_written;
if (bytes_written < towrite)
@@ -900,7 +925,7 @@ static void g_at_chat_wakeup_writer(GAtChat *chat)
(GDestroyNotify)write_watcher_destroy_notify);
}
-GAtChat *g_at_chat_new(GIOChannel *channel, int flags)
+GAtChat *g_at_chat_new(GIOChannel *channel, int flags, GAtDebugFunc debugf)
{
GAtChat *chat;
GIOFlags io_flags;
@@ -915,6 +940,7 @@ GAtChat *g_at_chat_new(GIOChannel *channel, int flags)
chat->next_cmd_id = 1;
chat->next_notify_id = 1;
+ chat->debugf = debugf;
chat->flags = flags;
chat->buf = ring_buffer_new(4096);
@@ -1002,7 +1028,8 @@ static int open_device(const char *device)
return fd;
}
-GAtChat *g_at_chat_new_from_tty(const char *device, int flags)
+GAtChat *g_at_chat_new_from_tty(const char *device, int flags,
+ GAtDebugFunc debugf)
{
GIOChannel *channel;
int fd;
@@ -1017,7 +1044,7 @@ GAtChat *g_at_chat_new_from_tty(const char *device, int flags)
return NULL;
}
- return g_at_chat_new(channel, flags);
+ return g_at_chat_new(channel, flags, debugf);
}
GAtChat *g_at_chat_ref(GAtChat *chat)
diff --git a/gatchat/gatchat.h b/gatchat/gatchat.h
index 969f6f4..4353e08 100644
--- a/gatchat/gatchat.h
+++ b/gatchat/gatchat.h
@@ -36,6 +36,8 @@ typedef void (*GAtResultFunc)(gboolean success, GAtResult *result,
gpointer user_data);
typedef void (*GAtNotifyFunc)(GAtResult *result, gpointer user_data);
typedef void (*GAtDisconnectFunc)(gpointer user_data);
+typedef void (*GAtDebugFunc)(const char *format, ...)
+ __attribute__((format(printf, 1, 2)));
enum _GAtChatFlags {
G_AT_CHAT_FLAG_NO_LEADING_CRLF = 1, /* Some emulators are broken */
@@ -44,8 +46,9 @@ enum _GAtChatFlags {
typedef enum _GAtChatFlags GAtChatFlags;
-GAtChat *g_at_chat_new(GIOChannel *channel, int flags);
-GAtChat *g_at_chat_new_from_tty(const char *device, int flags);
+GAtChat *g_at_chat_new(GIOChannel *channel, int flags, GAtDebugFunc debugf);
+GAtChat *g_at_chat_new_from_tty(const char *device, int flags,
+ GAtDebugFunc debugf);
int g_at_chat_get_flags(GAtChat *chat);
void g_at_chat_set_flags(GAtChat *chat, int flags);
diff --git a/include/log.h b/include/log.h
index ef2c663..153308a 100644
--- a/include/log.h
+++ b/include/log.h
@@ -28,6 +28,7 @@ extern "C" {
typedef enum {
OFONO_DEBUG_CORE = 1 << 0,
+ OFONO_DEBUG_AT_IO = 1 << 1,
} ofono_debug_flags;
/**
@@ -42,9 +43,13 @@ extern void ofono_error(const char *format, ...)
__attribute__((format(printf, 1, 2)));
extern void __ofono_debug(ofono_debug_flags flag, const char *format, ...)
__attribute__((format(printf, 2, 3)));
+extern void __ofono_vdebug(ofono_debug_flags flag, const char *format,
+ va_list ap);
#define ofono_debug(format, ...) \
__ofono_debug(OFONO_DEBUG_CORE, (format), ##__VA_ARGS__)
+extern gboolean ofono_debug_flag_isset(ofono_debug_flags flag);
+
/**
* DBG:
* @fmt: format string
diff --git a/src/log.c b/src/log.c
index 167fe21..c8c4d99 100644
--- a/src/log.c
+++ b/src/log.c
@@ -68,6 +68,38 @@ void ofono_error(const char *format, ...)
}
/**
+ * __ofono_vdebug:
+ * @flag: zone flag (ie, OFONO_DEBUG_CORE)
+ *
+ * Determine whether a specific debug flag is set or not
+ *
+ * Whether or not debugging is enabled is not considered.
+ */
+gboolean ofono_debug_flag_isset(ofono_debug_flags flag)
+{
+ return (debug_flags & flag) ? TRUE : FALSE;
+}
+
+/**
+ * __ofono_vdebug:
+ * @flag: zone flag (ie, OFONO_DEBUG_CORE)
+ * @format: format string
+ * @ap: valist of arguments
+ *
+ * Output debug message
+ *
+ * The actual output of the debug message is controlled via a command line
+ * switch. If not enabled, these messages will be ignored.
+ */
+void __ofono_vdebug(ofono_debug_flags flag, const char *format, va_list ap)
+{
+ if (!debug_enabled || !(debug_flags & flag))
+ return;
+
+ vsyslog(LOG_DEBUG, format, ap);
+}
+
+/**
* __ofono_debug:
* @flag: zone flag (ie, OFONO_DEBUG_CORE)
* @format: format string
@@ -82,13 +114,8 @@ void __ofono_debug(ofono_debug_flags flag, const char *format, ...)
{
va_list ap;
- if (!debug_enabled || !(debug_flags & flag))
- return;
-
va_start(ap, format);
-
- vsyslog(LOG_DEBUG, format, ap);
-
+ __ofono_vdebug(flag, format, ap);
va_end(ap);
}
diff --git a/src/main.c b/src/main.c
index 7227bde..93f6f74 100644
--- a/src/main.c
+++ b/src/main.c
@@ -58,6 +58,7 @@ static guint debug_flags = 0;
static GDebugKey keys[] = {
{ "core", OFONO_DEBUG_CORE },
+ { "atio", OFONO_DEBUG_AT_IO },
};
static gboolean parse_debug_flags(const gchar *option_name, const gchar *value,
--
1.6.3.3
^ permalink raw reply related [flat|nested] 4+ messages in thread* Re: [PATCH 2/2] atmodem: add serial line debugging to gatchat
2009-08-05 21:33 [PATCH 2/2] atmodem: add serial line debugging to gatchat Andres Salomon
@ 2009-08-05 22:09 ` Denis Kenzior
2009-08-06 5:21 ` Andres Salomon
0 siblings, 1 reply; 4+ messages in thread
From: Denis Kenzior @ 2009-08-05 22:09 UTC (permalink / raw)
To: ofono
[-- Attachment #1: Type: text/plain, Size: 2312 bytes --]
Hi Andres,
> drivers/atmodem/atmodem.c | 13 ++++++++++++-
> gatchat/gatchat.c | 33 ++++++++++++++++++++++++++++++---
> gatchat/gatchat.h | 7 +++++--
> include/log.h | 5 +++++
> src/log.c | 39 +++++++++++++++++++++++++++++++++------
> src/main.c | 1 +
> 6 files changed, 86 insertions(+), 12 deletions(-)
Send two separate patches, one for gatchat related functionality. One for the
ofono integration.
> +static void g_at_chat_debug_log(GAtChat *chat, gboolean is_read,
> + char *str, gsize len)
I suggest you use const char *str here
> +{
> + char type = is_read ? '<' : '>';
> + char *tmp;
> +
> + if (!chat->debugf || !len)
> + return;
> +
> + if (len > 1024) {
> + /* let's keep it to a reasonable length.. */
> + chat->debugf("%c (ignoring string of length %d)\n", type, len);
27.007 specifies a max command result length of 2048, Section 5.1:
"the total number of characters, including line terminators, in the
information text shall not exceed 2048 characters."
> + return;
> + }
> +
> + str[len] = '\0'; /* XXX: is it safe to overwrite this byte? */
Probably not
> + tmp = g_strescape(str, "\"");
> + chat->debugf("%c %s\n", type, tmp);
> + g_free(tmp);
> +}
> +
> -GAtChat *g_at_chat_new(GIOChannel *channel, int flags)
> +GAtChat *g_at_chat_new(GIOChannel *channel, int flags, GAtDebugFunc
Lets make the debug function settable / gettable. I.e. something like:
g_at_chat_set_debug_info(GAtChat *chat, GAtDebugFunc *debugf, void *debugdata)
g_at_chat_get_debug_func(GAtChat *chat)
g_at_chat_get_debug_data(GAtChat *chat)
> GAtChat *g_at_chat_ref(GAtChat *chat)
> diff --git a/gatchat/gatchat.h b/gatchat/gatchat.h
> index 969f6f4..4353e08 100644
> --- a/gatchat/gatchat.h
> +++ b/gatchat/gatchat.h
> @@ -36,6 +36,8 @@ typedef void (*GAtResultFunc)(gboolean success, GAtResult
> *result, gpointer user_data);
> typedef void (*GAtNotifyFunc)(GAtResult *result, gpointer user_data);
> typedef void (*GAtDisconnectFunc)(gpointer user_data);
> +typedef void (*GAtDebugFunc)(const char *format, ...)
> + __attribute__((format(printf, 1, 2)));
>
make the signature a simple void (*foo)(const char *str, void *data);
Regards,
-Denis
^ permalink raw reply [flat|nested] 4+ messages in thread* Re: [PATCH 2/2] atmodem: add serial line debugging to gatchat
2009-08-05 22:09 ` Denis Kenzior
@ 2009-08-06 5:21 ` Andres Salomon
2009-08-06 22:08 ` Denis Kenzior
0 siblings, 1 reply; 4+ messages in thread
From: Andres Salomon @ 2009-08-06 5:21 UTC (permalink / raw)
To: ofono
[-- Attachment #1: Type: text/plain, Size: 8312 bytes --]
On Wed, 5 Aug 2009 17:09:12 -0500
Denis Kenzior <denkenz-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:
> Hi Andres,
>
> > drivers/atmodem/atmodem.c | 13 ++++++++++++-
> > gatchat/gatchat.c | 33 ++++++++++++++++++++++++++++++---
> > gatchat/gatchat.h | 7 +++++--
> > include/log.h | 5 +++++
> > src/log.c | 39
> > +++++++++++++++++++++++++++++++++------ src/main.c
> > | 1 + 6 files changed, 86 insertions(+), 12 deletions(-)
>
> Send two separate patches, one for gatchat related functionality.
> One for the ofono integration.
>
How's the following? I didn't create any g_at_chat_get_debug*
functions; I figure those can be created later when someone has a use
for them.
From 61cd909200acde95e42f39fa8949668460531c50 Mon Sep 17 00:00:00 2001
From: Andres Salomon <dilinger@collabora.co.uk>
Date: Wed, 5 Aug 2009 17:18:34 -0400
Subject: [PATCH 2/3] gatchat: add debugging infrastructure
This patch adds debugging support to GAtChat (specifically of what's going
across the serial line). Callbacks can be set via g_at_chat_set_debugging,
and that callback is called after any channel reads or writes.
---
gatchat/gatchat.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++
gatchat/gatchat.h | 4 ++++
2 files changed, 50 insertions(+), 0 deletions(-)
diff --git a/gatchat/gatchat.c b/gatchat/gatchat.c
index 15cc934..031890f 100644
--- a/gatchat/gatchat.c
+++ b/gatchat/gatchat.c
@@ -96,6 +96,8 @@ struct _GAtChat {
guint read_so_far; /* Number of bytes processed */
gboolean disconnecting; /* Whether we're disconnecting */
enum chat_state state; /* Current chat state */
+ GAtDebugFunc debugf; /* debugging output function */
+ gpointer debug_func_user_data;
int flags;
char *pdu_notify; /* Unsolicited Resp w/ PDU */
GSList *response_lines; /* char * lines of the response */
@@ -713,6 +715,35 @@ static void new_bytes(GAtChat *p)
}
}
+static void g_at_chat_debug_log(GAtChat *chat, gboolean is_read,
+ const char *str, gsize len)
+{
+ char type = is_read ? '<' : '>';
+ char *t1, *t2;
+
+ if (!chat->debugf || !len)
+ return;
+
+ if (len > 2048) {
+ /* 27.007 specifies a max command result length of 2048 */
+ t1 = g_strdup_printf("%c (ignoring string of length %d)", type, len);
+ chat->debugf(t1, chat->debug_func_user_data);
+ g_free(t1);
+ return;
+ }
+
+ /* prefix with "> " or "< ", and ensure null-termination */
+ t1 = g_malloc(len + 3);
+ g_sprintf(t1, "%c ", type);
+ g_memmove(t1 + 2, str, len);
+ t1[len + 2] = '\0';
+
+ t2 = g_strescape(t1, "\"");
+ chat->debugf(t2, chat->debug_func_user_data);
+ g_free(t1);
+ g_free(t2);
+}
+
static gboolean received_data(GIOChannel *channel, GIOCondition cond,
gpointer data)
{
@@ -747,6 +778,7 @@ static gboolean received_data(GIOChannel *channel, GIOCondition cond,
buf = ring_buffer_write_ptr(chat->buf);
err = g_io_channel_read(channel, (char *) buf, toread, &rbytes);
+ g_at_chat_debug_log(chat, TRUE, buf, rbytes);
total_read += rbytes;
@@ -876,6 +908,8 @@ static gboolean can_write_data(GIOChannel *channel, GIOCondition cond,
return FALSE;
}
+ g_at_chat_debug_log(chat, FALSE, cmd->cmd + chat->cmd_bytes_written,
+ bytes_written);
chat->cmd_bytes_written += bytes_written;
if (bytes_written < towrite)
@@ -915,6 +949,7 @@ GAtChat *g_at_chat_new(GIOChannel *channel, int flags)
chat->next_cmd_id = 1;
chat->next_notify_id = 1;
+ chat->debugf = NULL;
chat->flags = flags;
chat->buf = ring_buffer_new(4096);
@@ -1075,6 +1110,17 @@ gboolean g_at_chat_set_disconnect_function(GAtChat *chat,
return TRUE;
}
+gboolean g_at_chat_set_debugging(GAtChat *chat, GAtDebugFunc func,
+ gpointer user_data)
+{
+ if (chat == NULL)
+ return FALSE;
+
+ chat->debugf = func;
+ chat->debug_func_user_data = user_data;
+ return TRUE;
+}
+
static guint send_common(GAtChat *chat, const char *cmd,
const char **prefix_list,
GAtNotifyFunc listing, GAtResultFunc func,
diff --git a/gatchat/gatchat.h b/gatchat/gatchat.h
index 969f6f4..dcf600f 100644
--- a/gatchat/gatchat.h
+++ b/gatchat/gatchat.h
@@ -36,6 +36,7 @@ typedef void (*GAtResultFunc)(gboolean success, GAtResult *result,
gpointer user_data);
typedef void (*GAtNotifyFunc)(GAtResult *result, gpointer user_data);
typedef void (*GAtDisconnectFunc)(gpointer user_data);
+typedef void (*GAtDebugFunc)(const char *str, gpointer user_data);
enum _GAtChatFlags {
G_AT_CHAT_FLAG_NO_LEADING_CRLF = 1, /* Some emulators are broken */
@@ -58,6 +59,9 @@ gboolean g_at_chat_shutdown(GAtChat *chat);
gboolean g_at_chat_set_disconnect_function(GAtChat *chat,
GAtDisconnectFunc disconnect, gpointer user_data);
+gboolean g_at_chat_set_debugging(GAtChat *chat, GAtDebugFunc func,
+ gpointer user_data);
+
/*!
* Queue an AT command for execution. The command contents are given
* in cmd. Once the command executes, the callback function given by
--
1.6.3.3
From 6e63802adfcfd3f780a02329d4457350fc2c05c9 Mon Sep 17 00:00:00 2001
From: Andres Salomon <dilinger@collabora.co.uk>
Date: Thu, 6 Aug 2009 01:15:02 -0400
Subject: [PATCH 3/3] atmodem: make use of gatchat debugging
This provides hooks for GAtChat's debugging infrastructure, and exposes
that to users via --debug=atio.
---
drivers/atmodem/atmodem.c | 8 ++++++++
include/log.h | 3 +++
src/log.c | 15 ++++++++++++++-
src/main.c | 1 +
4 files changed, 26 insertions(+), 1 deletions(-)
diff --git a/drivers/atmodem/atmodem.c b/drivers/atmodem/atmodem.c
index fc255b3..e32e2fb 100644
--- a/drivers/atmodem/atmodem.c
+++ b/drivers/atmodem/atmodem.c
@@ -320,6 +320,11 @@ static void msg_destroy(gpointer user)
dbus_message_unref(msg);
}
+static void atmodem_debug(const char *str, gpointer user_data)
+{
+ __ofono_debug(OFONO_DEBUG_AT_IO, "%s", str);
+}
+
static void create_cb(GIOChannel *io, gboolean success, gpointer user)
{
DBusConnection *conn = ofono_dbus_get_connection();
@@ -342,6 +347,9 @@ static void create_cb(GIOChannel *io, gboolean success, gpointer user)
if (!at->parser)
goto out;
+ if (ofono_debug_flag_isset(OFONO_DEBUG_AT_IO))
+ g_at_chat_set_debugging(at->parser, atmodem_debug, NULL);
+
ofono_debug("Seting up AT channel");
dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &target,
diff --git a/include/log.h b/include/log.h
index ef2c663..61b4495 100644
--- a/include/log.h
+++ b/include/log.h
@@ -28,6 +28,7 @@ extern "C" {
typedef enum {
OFONO_DEBUG_CORE = 1 << 0,
+ OFONO_DEBUG_AT_IO = 1 << 1,
} ofono_debug_flags;
/**
@@ -45,6 +46,8 @@ extern void __ofono_debug(ofono_debug_flags flag, const char *format, ...)
#define ofono_debug(format, ...) \
__ofono_debug(OFONO_DEBUG_CORE, (format), ##__VA_ARGS__)
+extern gboolean ofono_debug_flag_isset(ofono_debug_flags flag);
+
/**
* DBG:
* @fmt: format string
diff --git a/src/log.c b/src/log.c
index 167fe21..d027fa9 100644
--- a/src/log.c
+++ b/src/log.c
@@ -68,6 +68,19 @@ void ofono_error(const char *format, ...)
}
/**
+ * ofono_debug_flag_isset:
+ * @flag: zone flag (ie, OFONO_DEBUG_CORE)
+ *
+ * Determine whether a specific debug flag is set or not
+ *
+ * Whether or not debugging is enabled is not considered.
+ */
+gboolean ofono_debug_flag_isset(ofono_debug_flags flag)
+{
+ return (debug_flags & flag) ? TRUE : FALSE;
+}
+
+/**
* __ofono_debug:
* @flag: zone flag (ie, OFONO_DEBUG_CORE)
* @format: format string
@@ -82,7 +95,7 @@ void __ofono_debug(ofono_debug_flags flag, const char *format, ...)
{
va_list ap;
- if (!debug_enabled || !(debug_flags & flag))
+ if (!debug_enabled || !ofono_debug_flag_isset(flag))
return;
va_start(ap, format);
diff --git a/src/main.c b/src/main.c
index 7227bde..93f6f74 100644
--- a/src/main.c
+++ b/src/main.c
@@ -58,6 +58,7 @@ static guint debug_flags = 0;
static GDebugKey keys[] = {
{ "core", OFONO_DEBUG_CORE },
+ { "atio", OFONO_DEBUG_AT_IO },
};
static gboolean parse_debug_flags(const gchar *option_name, const gchar *value,
--
1.6.3.3
^ permalink raw reply related [flat|nested] 4+ messages in thread
end of thread, other threads:[~2009-08-06 22:08 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2009-08-05 21:33 [PATCH 2/2] atmodem: add serial line debugging to gatchat Andres Salomon
2009-08-05 22:09 ` Denis Kenzior
2009-08-06 5:21 ` Andres Salomon
2009-08-06 22:08 ` Denis Kenzior
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.