qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] gdbstub/user-target: Map errno values from the host OS to GDB
@ 2025-10-15 16:25 Yodel Eldar via
  2025-10-16 14:56 ` Alex Bennée
  0 siblings, 1 reply; 5+ messages in thread
From: Yodel Eldar via @ 2025-10-15 16:25 UTC (permalink / raw)
  To: qemu-devel
  Cc: Yodel Eldar, Dominik 'Disconnect3d' Czarnota,
	Alex Bennée, Philippe Mathieu-Daudé

This patch introduces the function "gdb_host_errno_to_gdb" that maps
host-dependent errno values to their GDB protocol-specific
representations as listed in the GDB manual [1].

The stub now uses the correct GDB errno values in F reply packets.

[1] https://sourceware.org/gdb/current/onlinedocs/gdb.html/Errno-Values.html

Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2751
Reported-by: Dominik 'Disconnect3d' Czarnota <dominik.b.czarnota@gmail.com>
Signed-off-by: Yodel Eldar <yodel.eldar@yodel.dev>
---
 gdbstub/user-target.c | 93 +++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 89 insertions(+), 4 deletions(-)

diff --git a/gdbstub/user-target.c b/gdbstub/user-target.c
index 43231e695e..29feb0509c 100644
--- a/gdbstub/user-target.c
+++ b/gdbstub/user-target.c
@@ -302,6 +302,87 @@ static void hostio_reply_with_data(const void *buf, size_t n)
                           gdbserver_state.str_buf->len, true);
 }
 
+/*
+ * Map host error numbers to their GDB protocol counterparts.
+ * For the list of GDB File-I/O supported error numbers, please consult:
+ * https://sourceware.org/gdb/current/onlinedocs/gdb.html/Errno-Values.html
+ */
+
+static int gdb_host_errno_to_gdb(int errnum)
+{
+    enum {
+        GDB_EPERM        =    1,
+        GDB_ENOENT       =    2,
+        GDB_EINTR        =    4,
+        GDB_EIO          =    5,
+        GDB_EBADF        =    9,
+        GDB_EACCES       =   13,
+        GDB_EFAULT       =   14,
+        GDB_EBUSY        =   16,
+        GDB_EEXIST       =   17,
+        GDB_ENODEV       =   19,
+        GDB_ENOTDIR      =   20,
+        GDB_EISDIR       =   21,
+        GDB_EINVAL       =   22,
+        GDB_ENFILE       =   23,
+        GDB_EMFILE       =   24,
+        GDB_EFBIG        =   27,
+        GDB_ENOSPC       =   28,
+        GDB_ESPIPE       =   29,
+        GDB_EROFS        =   30,
+        GDB_ENOSYS       =   88,
+        GDB_ENAMETOOLONG =   91,
+        GDB_EUNKNOWN     = 9999,
+    };
+
+    switch (errnum) {
+    case EPERM:
+        return GDB_EPERM;
+    case ENOENT:
+        return GDB_ENOENT;
+    case EINTR:
+        return GDB_EINTR;
+    case EIO:
+        return GDB_EIO;
+    case EBADF:
+        return GDB_EBADF;
+    case EACCES:
+        return GDB_EACCES;
+    case EFAULT:
+        return GDB_EFAULT;
+    case EBUSY:
+        return GDB_EBUSY;
+    case EEXIST:
+        return GDB_EEXIST;
+    case ENODEV:
+        return GDB_ENODEV;
+    case ENOTDIR:
+        return GDB_ENOTDIR;
+    case EISDIR:
+        return GDB_EISDIR;
+    case EINVAL:
+        return GDB_EINVAL;
+    case ENFILE:
+        return GDB_ENFILE;
+    case EMFILE:
+        return GDB_EMFILE;
+    case EFBIG:
+        return GDB_EFBIG;
+    case ENOSPC:
+        return GDB_ENOSPC;
+    case ESPIPE:
+        return GDB_ESPIPE;
+    case EROFS:
+        return GDB_EROFS;
+    case ENOSYS:
+        return GDB_ENOSYS;
+    case ENAMETOOLONG:
+        return GDB_ENAMETOOLONG;
+    default:
+        return GDB_EUNKNOWN;
+    }
+}
+
 void gdb_handle_v_file_open(GArray *params, void *user_ctx)
 {
     const char *filename = get_filename_param(params, 0);
@@ -315,7 +396,8 @@ void gdb_handle_v_file_open(GArray *params, void *user_ctx)
     int fd = open(filename, flags, mode);
 #endif
     if (fd < 0) {
-        g_string_printf(gdbserver_state.str_buf, "F-1,%x", errno);
+        int gdb_errno = gdb_host_errno_to_gdb(errno);
+        g_string_printf(gdbserver_state.str_buf, "F-1,%x", gdb_errno);
     } else {
         g_string_printf(gdbserver_state.str_buf, "F%x", fd);
     }
@@ -327,7 +409,8 @@ void gdb_handle_v_file_close(GArray *params, void *user_ctx)
     int fd = gdb_get_cmd_param(params, 0)->val_ul;
 
     if (close(fd) == -1) {
-        g_string_printf(gdbserver_state.str_buf, "F-1,%x", errno);
+        int gdb_errno = gdb_host_errno_to_gdb(errno);
+        g_string_printf(gdbserver_state.str_buf, "F-1,%x", gdb_errno);
         gdb_put_strbuf();
         return;
     }
@@ -350,7 +433,8 @@ void gdb_handle_v_file_pread(GArray *params, void *user_ctx)
 
     ssize_t n = pread(fd, buf, bufsiz, offset);
     if (n < 0) {
-        g_string_printf(gdbserver_state.str_buf, "F-1,%x", errno);
+        int gdb_errno = gdb_host_errno_to_gdb(errno);
+        g_string_printf(gdbserver_state.str_buf, "F-1,%x", gdb_errno);
         gdb_put_strbuf();
         return;
     }
@@ -373,7 +457,8 @@ void gdb_handle_v_file_readlink(GArray *params, void *user_ctx)
     ssize_t n = readlink(filename, buf, BUFSIZ);
 #endif
     if (n < 0) {
-        g_string_printf(gdbserver_state.str_buf, "F-1,%x", errno);
+        int gdb_errno = gdb_host_errno_to_gdb(errno);
+        g_string_printf(gdbserver_state.str_buf, "F-1,%x", gdb_errno);
         gdb_put_strbuf();
         return;
     }
-- 
2.51.0



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

* Re: [PATCH] gdbstub/user-target: Map errno values from the host OS to GDB
  2025-10-15 16:25 [PATCH] gdbstub/user-target: Map errno values from the host OS to GDB Yodel Eldar via
@ 2025-10-16 14:56 ` Alex Bennée
  2025-10-16 20:51   ` Yodel Eldar via
  0 siblings, 1 reply; 5+ messages in thread
From: Alex Bennée @ 2025-10-16 14:56 UTC (permalink / raw)
  To: Yodel Eldar
  Cc: qemu-devel, Dominik 'Disconnect3d' Czarnota,
	Philippe Mathieu-Daudé

Yodel Eldar <yodel.eldar@yodel.dev> writes:

> This patch introduces the function "gdb_host_errno_to_gdb" that maps
> host-dependent errno values to their GDB protocol-specific
> representations as listed in the GDB manual [1].
>
> The stub now uses the correct GDB errno values in F reply packets.
>
> [1] https://sourceware.org/gdb/current/onlinedocs/gdb.html/Errno-Values.html
>
> Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2751
> Reported-by: Dominik 'Disconnect3d' Czarnota <dominik.b.czarnota@gmail.com>
> Signed-off-by: Yodel Eldar <yodel.eldar@yodel.dev>
> ---
>  gdbstub/user-target.c | 93 +++++++++++++++++++++++++++++++++++++++++--
>  1 file changed, 89 insertions(+), 4 deletions(-)
>
> diff --git a/gdbstub/user-target.c b/gdbstub/user-target.c
> index 43231e695e..29feb0509c 100644
> --- a/gdbstub/user-target.c
> +++ b/gdbstub/user-target.c
> @@ -302,6 +302,87 @@ static void hostio_reply_with_data(const void *buf, size_t n)
>                            gdbserver_state.str_buf->len, true);
>  }
>  
> +/*
> + * Map host error numbers to their GDB protocol counterparts.
> + * For the list of GDB File-I/O supported error numbers, please consult:
> + * https://sourceware.org/gdb/current/onlinedocs/gdb.html/Errno-Values.html
> + */
> +
> +static int gdb_host_errno_to_gdb(int errnum)
> +{
> +    enum {
> +        GDB_EPERM        =    1,
> +        GDB_ENOENT       =    2,
> +        GDB_EINTR        =    4,
> +        GDB_EIO          =    5,
> +        GDB_EBADF        =    9,
> +        GDB_EACCES       =   13,
> +        GDB_EFAULT       =   14,
> +        GDB_EBUSY        =   16,
> +        GDB_EEXIST       =   17,
> +        GDB_ENODEV       =   19,
> +        GDB_ENOTDIR      =   20,
> +        GDB_EISDIR       =   21,
> +        GDB_EINVAL       =   22,
> +        GDB_ENFILE       =   23,
> +        GDB_EMFILE       =   24,
> +        GDB_EFBIG        =   27,
> +        GDB_ENOSPC       =   28,
> +        GDB_ESPIPE       =   29,
> +        GDB_EROFS        =   30,
> +        GDB_ENOSYS       =   88,
> +        GDB_ENAMETOOLONG =   91,
> +        GDB_EUNKNOWN     = 9999,
> +    };

We have this enum in include/gdbstub/syscalls.h already.

> +
> +    switch (errnum) {
> +    case EPERM:
> +        return GDB_EPERM;
> +    case ENOENT:
> +        return GDB_ENOENT;
> +    case EINTR:
> +        return GDB_EINTR;
> +    case EIO:
> +        return GDB_EIO;
> +    case EBADF:
> +        return GDB_EBADF;
> +    case EACCES:
> +        return GDB_EACCES;
> +    case EFAULT:
> +        return GDB_EFAULT;
> +    case EBUSY:
> +        return GDB_EBUSY;
> +    case EEXIST:
> +        return GDB_EEXIST;
> +    case ENODEV:
> +        return GDB_ENODEV;
> +    case ENOTDIR:
> +        return GDB_ENOTDIR;
> +    case EISDIR:
> +        return GDB_EISDIR;
> +    case EINVAL:
> +        return GDB_EINVAL;
> +    case ENFILE:
> +        return GDB_ENFILE;
> +    case EMFILE:
> +        return GDB_EMFILE;
> +    case EFBIG:
> +        return GDB_EFBIG;
> +    case ENOSPC:
> +        return GDB_ENOSPC;
> +    case ESPIPE:
> +        return GDB_ESPIPE;
> +    case EROFS:
> +        return GDB_EROFS;
> +    case ENOSYS:
> +        return GDB_ENOSYS;
> +    case ENAMETOOLONG:
> +        return GDB_ENAMETOOLONG;
> +    default:
> +        return GDB_EUNKNOWN;
> +    }
> +}
> +
>  void gdb_handle_v_file_open(GArray *params, void *user_ctx)
>  {
>      const char *filename = get_filename_param(params, 0);
> @@ -315,7 +396,8 @@ void gdb_handle_v_file_open(GArray *params, void *user_ctx)
>      int fd = open(filename, flags, mode);
>  #endif
>      if (fd < 0) {
> -        g_string_printf(gdbserver_state.str_buf, "F-1,%x", errno);
> +        int gdb_errno = gdb_host_errno_to_gdb(errno);
> +        g_string_printf(gdbserver_state.str_buf, "F-1,%x", gdb_errno);
>      } else {
>          g_string_printf(gdbserver_state.str_buf, "F%x", fd);
>      }
> @@ -327,7 +409,8 @@ void gdb_handle_v_file_close(GArray *params, void *user_ctx)
>      int fd = gdb_get_cmd_param(params, 0)->val_ul;
>  
>      if (close(fd) == -1) {
> -        g_string_printf(gdbserver_state.str_buf, "F-1,%x", errno);
> +        int gdb_errno = gdb_host_errno_to_gdb(errno);
> +        g_string_printf(gdbserver_state.str_buf, "F-1,%x", gdb_errno);
>          gdb_put_strbuf();
>          return;
>      }
> @@ -350,7 +433,8 @@ void gdb_handle_v_file_pread(GArray *params, void *user_ctx)
>  
>      ssize_t n = pread(fd, buf, bufsiz, offset);
>      if (n < 0) {
> -        g_string_printf(gdbserver_state.str_buf, "F-1,%x", errno);
> +        int gdb_errno = gdb_host_errno_to_gdb(errno);
> +        g_string_printf(gdbserver_state.str_buf, "F-1,%x", gdb_errno);
>          gdb_put_strbuf();
>          return;
>      }
> @@ -373,7 +457,8 @@ void gdb_handle_v_file_readlink(GArray *params, void *user_ctx)
>      ssize_t n = readlink(filename, buf, BUFSIZ);
>  #endif
>      if (n < 0) {
> -        g_string_printf(gdbserver_state.str_buf, "F-1,%x", errno);
> +        int gdb_errno = gdb_host_errno_to_gdb(errno);
> +        g_string_printf(gdbserver_state.str_buf, "F-1,%x", gdb_errno);
>          gdb_put_strbuf();
>          return;
>      }

with that fixed:

Reviewed-by: Alex Bennée <alex.bennee@linaro.org>

-- 
Alex Bennée
Virtualisation Tech Lead @ Linaro


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

* Re: [PATCH] gdbstub/user-target: Map errno values from the host OS to GDB
  2025-10-16 14:56 ` Alex Bennée
@ 2025-10-16 20:51   ` Yodel Eldar via
  2025-10-16 21:51     ` Richard Henderson
  0 siblings, 1 reply; 5+ messages in thread
From: Yodel Eldar via @ 2025-10-16 20:51 UTC (permalink / raw)
  To: Alex Bennée
  Cc: qemu-devel, Dominik 'Disconnect3d' Czarnota,
	Philippe Mathieu-Daudé

Hi, Alex!

On 10/16/25 9:56 AM, Alex Bennée wrote:
> Yodel Eldar <yodel.eldar@yodel.dev> writes:
> 
>> This patch introduces the function "gdb_host_errno_to_gdb" that maps
>> host-dependent errno values to their GDB protocol-specific
>> representations as listed in the GDB manual [1].
>>
>> The stub now uses the correct GDB errno values in F reply packets.
>>
>> [1] https://sourceware.org/gdb/current/onlinedocs/gdb.html/Errno-Values.html
>>
>> Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2751
>> Reported-by: Dominik 'Disconnect3d' Czarnota <dominik.b.czarnota@gmail.com>
>> Signed-off-by: Yodel Eldar <yodel.eldar@yodel.dev>
>> ---
>>   gdbstub/user-target.c | 93 +++++++++++++++++++++++++++++++++++++++++--
>>   1 file changed, 89 insertions(+), 4 deletions(-)
>>
>> diff --git a/gdbstub/user-target.c b/gdbstub/user-target.c
>> index 43231e695e..29feb0509c 100644
>> --- a/gdbstub/user-target.c
>> +++ b/gdbstub/user-target.c
>> @@ -302,6 +302,87 @@ static void hostio_reply_with_data(const void *buf, size_t n)
>>                             gdbserver_state.str_buf->len, true);
>>   }
>>   
>> +/*
>> + * Map host error numbers to their GDB protocol counterparts.
>> + * For the list of GDB File-I/O supported error numbers, please consult:
>> + * https://sourceware.org/gdb/current/onlinedocs/gdb.html/Errno-Values.html
>> + */
>> +
>> +static int gdb_host_errno_to_gdb(int errnum)
>> +{
>> +    enum {
>> +        GDB_EPERM        =    1,
>> +        GDB_ENOENT       =    2,
>> +        GDB_EINTR        =    4,
>> +        GDB_EIO          =    5,
>> +        GDB_EBADF        =    9,
>> +        GDB_EACCES       =   13,
>> +        GDB_EFAULT       =   14,
>> +        GDB_EBUSY        =   16,
>> +        GDB_EEXIST       =   17,
>> +        GDB_ENODEV       =   19,
>> +        GDB_ENOTDIR      =   20,
>> +        GDB_EISDIR       =   21,
>> +        GDB_EINVAL       =   22,
>> +        GDB_ENFILE       =   23,
>> +        GDB_EMFILE       =   24,
>> +        GDB_EFBIG        =   27,
>> +        GDB_ENOSPC       =   28,
>> +        GDB_ESPIPE       =   29,
>> +        GDB_EROFS        =   30,
>> +        GDB_ENOSYS       =   88,
>> +        GDB_ENAMETOOLONG =   91,
>> +        GDB_EUNKNOWN     = 9999,
>> +    };
> 
> We have this enum in include/gdbstub/syscalls.h already.
> 

Thanks for pointing that out!

>> +
>> +    switch (errnum) {
>> +    case EPERM:
>> +        return GDB_EPERM;
>> +    case ENOENT:
>> +        return GDB_ENOENT;
>> +    case EINTR:
>> +        return GDB_EINTR;
>> +    case EIO:
>> +        return GDB_EIO;
>> +    case EBADF:
>> +        return GDB_EBADF;
>> +    case EACCES:
>> +        return GDB_EACCES;
>> +    case EFAULT:
>> +        return GDB_EFAULT;
>> +    case EBUSY:
>> +        return GDB_EBUSY;
>> +    case EEXIST:
>> +        return GDB_EEXIST;
>> +    case ENODEV:
>> +        return GDB_ENODEV;
>> +    case ENOTDIR:
>> +        return GDB_ENOTDIR;
>> +    case EISDIR:
>> +        return GDB_EISDIR;
>> +    case EINVAL:
>> +        return GDB_EINVAL;
>> +    case ENFILE:
>> +        return GDB_ENFILE;
>> +    case EMFILE:
>> +        return GDB_EMFILE;
>> +    case EFBIG:
>> +        return GDB_EFBIG;
>> +    case ENOSPC:
>> +        return GDB_ENOSPC;
>> +    case ESPIPE:
>> +        return GDB_ESPIPE;
>> +    case EROFS:
>> +        return GDB_EROFS;
>> +    case ENOSYS:
>> +        return GDB_ENOSYS;
>> +    case ENAMETOOLONG:
>> +        return GDB_ENAMETOOLONG;
>> +    default:
>> +        return GDB_EUNKNOWN;
>> +    }
>> +}
>> +
>>   void gdb_handle_v_file_open(GArray *params, void *user_ctx)
>>   {
>>       const char *filename = get_filename_param(params, 0);
>> @@ -315,7 +396,8 @@ void gdb_handle_v_file_open(GArray *params, void *user_ctx)
>>       int fd = open(filename, flags, mode);
>>   #endif
>>       if (fd < 0) {
>> -        g_string_printf(gdbserver_state.str_buf, "F-1,%x", errno);
>> +        int gdb_errno = gdb_host_errno_to_gdb(errno);
>> +        g_string_printf(gdbserver_state.str_buf, "F-1,%x", gdb_errno);
>>       } else {
>>           g_string_printf(gdbserver_state.str_buf, "F%x", fd);
>>       }
>> @@ -327,7 +409,8 @@ void gdb_handle_v_file_close(GArray *params, void *user_ctx)
>>       int fd = gdb_get_cmd_param(params, 0)->val_ul;
>>   
>>       if (close(fd) == -1) {
>> -        g_string_printf(gdbserver_state.str_buf, "F-1,%x", errno);
>> +        int gdb_errno = gdb_host_errno_to_gdb(errno);
>> +        g_string_printf(gdbserver_state.str_buf, "F-1,%x", gdb_errno);
>>           gdb_put_strbuf();
>>           return;
>>       }
>> @@ -350,7 +433,8 @@ void gdb_handle_v_file_pread(GArray *params, void *user_ctx)
>>   
>>       ssize_t n = pread(fd, buf, bufsiz, offset);
>>       if (n < 0) {
>> -        g_string_printf(gdbserver_state.str_buf, "F-1,%x", errno);
>> +        int gdb_errno = gdb_host_errno_to_gdb(errno);
>> +        g_string_printf(gdbserver_state.str_buf, "F-1,%x", gdb_errno);
>>           gdb_put_strbuf();
>>           return;
>>       }
>> @@ -373,7 +457,8 @@ void gdb_handle_v_file_readlink(GArray *params, void *user_ctx)
>>       ssize_t n = readlink(filename, buf, BUFSIZ);
>>   #endif
>>       if (n < 0) {
>> -        g_string_printf(gdbserver_state.str_buf, "F-1,%x", errno);
>> +        int gdb_errno = gdb_host_errno_to_gdb(errno);
>> +        g_string_printf(gdbserver_state.str_buf, "F-1,%x", gdb_errno);
>>           gdb_put_strbuf();
>>           return;
>>       }
> 
> with that fixed:
> 
> Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
> 

Since your review, I've noticed that Richard Henderson already authored
a host-to-GDB errno mapping function in commit 7327e6023:
host_to_gdb_errno:target/m68k/m68k-semi.c; it's functionally identical
to the mapping proposed in this patch (albeit missing two errno values
that were undocumented in the GDB manual until recently), but written in
the context of semihosting for the m68k.

So as to avoid duplicating code, I'm considering exporting the existing
host_to_gdb_errno in a minor refactor to use that instead; do you think
that's a better route?

Thanks,
Yodel


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

* Re: [PATCH] gdbstub/user-target: Map errno values from the host OS to GDB
  2025-10-16 20:51   ` Yodel Eldar via
@ 2025-10-16 21:51     ` Richard Henderson
  2025-10-16 22:41       ` Yodel Eldar via
  0 siblings, 1 reply; 5+ messages in thread
From: Richard Henderson @ 2025-10-16 21:51 UTC (permalink / raw)
  To: qemu-devel

On 10/16/25 13:51, Yodel Eldar via wrote:
> Since your review, I've noticed that Richard Henderson already authored
> a host-to-GDB errno mapping function in commit 7327e6023:
> host_to_gdb_errno:target/m68k/m68k-semi.c; it's functionally identical
> to the mapping proposed in this patch (albeit missing two errno values
> that were undocumented in the GDB manual until recently), but written in
> the context of semihosting for the m68k.

Heh.  I had glanced at your patch and thought "don't we do that already"?

> 
> So as to avoid duplicating code, I'm considering exporting the existing
> host_to_gdb_errno in a minor refactor to use that instead; do you think
> that's a better route?

Yes.


r~


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

* Re: [PATCH] gdbstub/user-target: Map errno values from the host OS to GDB
  2025-10-16 21:51     ` Richard Henderson
@ 2025-10-16 22:41       ` Yodel Eldar via
  0 siblings, 0 replies; 5+ messages in thread
From: Yodel Eldar via @ 2025-10-16 22:41 UTC (permalink / raw)
  To: Richard Henderson, qemu-devel


On 10/16/25 4:51 PM, Richard Henderson wrote:
> On 10/16/25 13:51, Yodel Eldar via wrote:
>> Since your review, I've noticed that Richard Henderson already authored
>> a host-to-GDB errno mapping function in commit 7327e6023:
>> host_to_gdb_errno:target/m68k/m68k-semi.c; it's functionally identical
>> to the mapping proposed in this patch (albeit missing two errno values
>> that were undocumented in the GDB manual until recently), but written in
>> the context of semihosting for the m68k.
> 
> Heh.  I had glanced at your patch and thought "don't we do that already"?
> 
>>
>> So as to avoid duplicating code, I'm considering exporting the existing
>> host_to_gdb_errno in a minor refactor to use that instead; do you think
>> that's a better route?
> 
> Yes.
> 
> 
> r~
> 

Haha, grateful for your eyes and input on this! I'll be sure to Cc the
next time I name-check you.

Yodel


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

end of thread, other threads:[~2025-10-16 22:42 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-10-15 16:25 [PATCH] gdbstub/user-target: Map errno values from the host OS to GDB Yodel Eldar via
2025-10-16 14:56 ` Alex Bennée
2025-10-16 20:51   ` Yodel Eldar via
2025-10-16 21:51     ` Richard Henderson
2025-10-16 22:41       ` Yodel Eldar via

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).