public inbox for libc-help@sourceware.org
 help / color / mirror / Atom feed
* Is there a way to use vsnprintf_l? Would it possibly be considered for an addition one day, if missing?
@ 2020-10-17 11:33 ell1e
  2020-10-19 21:01 ` Adhemerval Zanella
  0 siblings, 1 reply; 6+ messages in thread
From: ell1e @ 2020-10-17 11:33 UTC (permalink / raw)
  To: libc-help

Hello everyone, I hope I'm sending this to the right place!

I write C stuff on linux with GLIBC, and I've discovered I prefer to use 
a locale-independent version of snprintf to avoid issues with third 
party plugins using a process-wide setlocale() thoughtlessly. While I am 
aware a uselocale() in the local thread would also help with this, I 
don't really want to put that everywhere after I call into an external 
plugin just in case to guarantee this (or every time before I call 
snprintf).

The typical choice from what I can see for a locale-independent snprintf 
would be snprintf_l. However, always specifying that I really want a "C" 
locale as an additional parameter is tedious, especially since I have no 
desire to ever use anything else. In addition, this function isn't 
available on all platforms or might be named differently/have a 
different argument order (e.g. Windows, or MinGW). Therefore, I wrapped 
it to have the regular snprintf signature again:

static int mysnprintf(char *buf, size_t size, const char *format, ...)

... as a three line inline header function (to give it a chance of being 
inlined/optimized away), where the natural function to call inside would 
now be vsnprintf_l as far as I can tell.

However, sadly it seems glibc is lacking vsnprintf_l. I grepped the 
headers, and just couldn't find it.

Does it happen to be available in some ways I didn't anticipate? If not, 
is there a possibility to petition for it to be added?

Right now I'm calling uselocale()+vsnprintf() in a pair every time 
instead. While I don't anticipate any notable performance problems from 
the additional call, it still seems needlessly tedious, and vsnprintf_l 
just feeks like a better match. In addition, vsnprintf_l is available on 
the popular BSDs, macOS, and even Windows 10! (As a variant with 
slightly different argument order, _vsnprintf_l.) So I think it would be 
great to have it on Linux as well for parity.

For a similar reasoning, vprintf_l (in addition to vsnprintf_l) would 
also be a function I'd really love to use. I already use strtod_l, which 
thankfully does seem to be available in glibc already.

Best regards and happy C writing,

ell1e

PS: I hope I didn't typo the function names anywhere, I tend to mess up 
the letters sometimes xD I apologize if that happened at any point

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

* Re: Is there a way to use vsnprintf_l? Would it possibly be considered for an addition one day, if missing?
  2020-10-17 11:33 Is there a way to use vsnprintf_l? Would it possibly be considered for an addition one day, if missing? ell1e
@ 2020-10-19 21:01 ` Adhemerval Zanella
  2020-10-20  5:45   ` Florian Weimer
  0 siblings, 1 reply; 6+ messages in thread
From: Adhemerval Zanella @ 2020-10-19 21:01 UTC (permalink / raw)
  To: ell1e, libc-help



On 17/10/2020 08:33, ell1e wrote:
> Hello everyone, I hope I'm sending this to the right place!
> 
> I write C stuff on linux with GLIBC, and I've discovered I prefer to use a locale-independent version of snprintf to avoid issues with third party plugins using a process-wide setlocale() thoughtlessly. While I am aware a uselocale() in the local thread would also help with this, I don't really want to put that everywhere after I call into an external plugin just in case to guarantee this (or every time before I call snprintf).
> 
> The typical choice from what I can see for a locale-independent snprintf would be snprintf_l. However, always specifying that I really want a "C" locale as an additional parameter is tedious, especially since I have no desire to ever use anything else. In addition, this function isn't available on all platforms or might be named differently/have a different argument order (e.g. Windows, or MinGW). Therefore, I wrapped it to have the regular snprintf signature again:
> 
> static int mysnprintf(char *buf, size_t size, const char *format, ...)
> 
> ... as a three line inline header function (to give it a chance of being inlined/optimized away), where the natural function to call inside would now be vsnprintf_l as far as I can tell.
> 
> However, sadly it seems glibc is lacking vsnprintf_l. I grepped the headers, and just couldn't find it.
> 
> Does it happen to be available in some ways I didn't anticipate? If not, is there a possibility to petition for it to be added?

GLIBC does not support the printf family with the extra locale as argument,
and I think it should be feasible addition.  We already implement the one
that the standard defines (such as strcasecmp_l), this seems a reasonable
addition.

> 
> Right now I'm calling uselocale()+vsnprintf() in a pair every time instead. While I don't anticipate any notable performance problems from the additional call, it still seems needlessly tedious, and vsnprintf_l just feeks like a better match. In addition, vsnprintf_l is available on the popular BSDs, macOS, and even Windows 10! (As a variant with slightly different argument order, _vsnprintf_l.) So I think it would be great to have it on Linux as well for parity.

If the idea is to provide a concise support for printf_l family function,
I think we should add not only snprintf_l or vsnprintf_l, but rather all the 
stdio functions for both printf *and* scanf along with the wchar_t family
as well.  It means to add support for:

  int fprintf_l (FILE *, locale_t, const char *, ...)
  int fscanf_l (FILE * , locale_t, const char *, ...)
  int printf_l (locale_t, const char *, ...)
  int scanf_l (locale_t, const char *, ...)
  int sprintf_l (char *, locale_t, const char * , ...)
  int sscanf_l (const char *, locale_t, const char * , ...)
  int vfprintf_l (FILE *, locale_t, const char * , __va_list)
  int vprintf_l (locale_t, const char *, __va_list)
  int vsprintf_l (char * , locale_t, const char *, __va_list);
  int snprintf_l (char *, size_t, locale_t, const char * , ...)
  int vfscanf_l (FILE * , locale_t, const char * , __va_list)
  int vscanf_l (locale_t, const char *, __va_list)
  int vsnprintf_l (char *, size_t, locale_t, const char *, va_list)
  int vsscanf_l (const char *, locale_t, const char *, va_list);
  int dprintf_l (int, locale_t, const char *, ...)
  int vdprintf_l (int, locale_t, const char *, __va_list)
  int asprintf_l (char **, locale_t, const char *, ...)
  int vasprintf_l (char **, locale_t, const char *, __va_list)

  int fwprintf_l (FILE *, locale_t, const wchar_t *, ...)
  int fwscanf_l (FILE * , locale_t, const wchar_t *, ...)
  int wprintf_l (locale_t, const wchar_t *, ...)
  int wscanf_l (locale_t, const wchar_t *, ...)
  int wsprintf_l (char *, locale_t, const wchar_t * , ...)
  int swscanf_l (const char *, locale_t, const wchar_t * , ...)
  int vfwprintf_l (FILE *, locale_t, const wchar_t * , __va_list)
  int vwprintf_l (locale_t, const wchar_t *, __va_list)
  int vswprintf_l (wchar_t * , locale_t, const wchar_t *, __va_list);
  int snwprintf_l (wchar_t *, size_t, locale_t, const wchar_t * , ...)
  int vfwscanf_l (FILE * , locale_t, const wchar_t * , __va_list)
  int vwscanf_l (locale_t, const char *, __va_list)
  int vsnwprintf_l (wchar_t *, size_t, locale_t, const wchar_t *, va_list)
  int vswscanf_l (const wchar_t *, locale_t, const wchar_t *, va_list);
  int dwprintf_l (int, locale_t, const wchar_t *, ...)
  int vdwprintf_l (int, locale_t, const wchar_t *, __va_list)
  int aswprintf_l (wchar_t **, locale_t, const wchar_t *, ...)
  int vaswprintf_l (wchar_t **, locale_t, const wchar_t *, __va_list)

What I am not sure if this would require to add the remaining stdio interfaces
that depends on the LC_CTYPE category of the current locale, such as 
getwc/fgetwc, btowc, etc.  I would say it makes sense to add them as well.

It would also require to add proper tests to check the added interfaces.

> 
> For a similar reasoning, vprintf_l (in addition to vsnprintf_l) would also be a function I'd really love to use. I already use strtod_l, which thankfully does seem to be available in glibc already.
> 
> Best regards and happy C writing,
> 
> ell1e
> 
> PS: I hope I didn't typo the function names anywhere, I tend to mess up the letters sometimes xD I apologize if that happened at any point

I don't know any glibc contributor that had showed interest in this
specific task. It is not that simple, but it is also mostly plumbing
work on moving the generic implementation to accept the locale_t as 
argument, create the internal alias and implement the default printf 
symbol to call the print_l one.

If you want to implement it, I can help you with the patch submission,
review and testing.

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

* Re: Is there a way to use vsnprintf_l? Would it possibly be considered for an addition one day, if missing?
  2020-10-19 21:01 ` Adhemerval Zanella
@ 2020-10-20  5:45   ` Florian Weimer
  2020-10-20 13:30     ` Adhemerval Zanella
  0 siblings, 1 reply; 6+ messages in thread
From: Florian Weimer @ 2020-10-20  5:45 UTC (permalink / raw)
  To: Adhemerval Zanella via Libc-help

* Adhemerval Zanella via Libc-help:

> If the idea is to provide a concise support for printf_l family function,
> I think we should add not only snprintf_l or vsnprintf_l, but rather all the 
> stdio functions for both printf *and* scanf along with the wchar_t family
> as well.  It means to add support for:
>
>   int fprintf_l (FILE *, locale_t, const char *, ...)
>   int fscanf_l (FILE * , locale_t, const char *, ...)
>   int printf_l (locale_t, const char *, ...)
>   int scanf_l (locale_t, const char *, ...)
>   int sprintf_l (char *, locale_t, const char * , ...)
>   int sscanf_l (const char *, locale_t, const char * , ...)
>   int vfprintf_l (FILE *, locale_t, const char * , __va_list)
>   int vprintf_l (locale_t, const char *, __va_list)
>   int vsprintf_l (char * , locale_t, const char *, __va_list);
>   int snprintf_l (char *, size_t, locale_t, const char * , ...)
>   int vfscanf_l (FILE * , locale_t, const char * , __va_list)
>   int vscanf_l (locale_t, const char *, __va_list)
>   int vsnprintf_l (char *, size_t, locale_t, const char *, va_list)
>   int vsscanf_l (const char *, locale_t, const char *, va_list);
>   int dprintf_l (int, locale_t, const char *, ...)
>   int vdprintf_l (int, locale_t, const char *, __va_list)
>   int asprintf_l (char **, locale_t, const char *, ...)
>   int vasprintf_l (char **, locale_t, const char *, __va_list)
>
>   int fwprintf_l (FILE *, locale_t, const wchar_t *, ...)
>   int fwscanf_l (FILE * , locale_t, const wchar_t *, ...)
>   int wprintf_l (locale_t, const wchar_t *, ...)
>   int wscanf_l (locale_t, const wchar_t *, ...)
>   int wsprintf_l (char *, locale_t, const wchar_t * , ...)
>   int swscanf_l (const char *, locale_t, const wchar_t * , ...)
>   int vfwprintf_l (FILE *, locale_t, const wchar_t * , __va_list)
>   int vwprintf_l (locale_t, const wchar_t *, __va_list)
>   int vswprintf_l (wchar_t * , locale_t, const wchar_t *, __va_list);
>   int snwprintf_l (wchar_t *, size_t, locale_t, const wchar_t * , ...)
>   int vfwscanf_l (FILE * , locale_t, const wchar_t * , __va_list)
>   int vwscanf_l (locale_t, const char *, __va_list)
>   int vsnwprintf_l (wchar_t *, size_t, locale_t, const wchar_t *, va_list)
>   int vswscanf_l (const wchar_t *, locale_t, const wchar_t *, va_list);
>   int dwprintf_l (int, locale_t, const wchar_t *, ...)
>   int vdwprintf_l (int, locale_t, const wchar_t *, __va_list)
>   int aswprintf_l (wchar_t **, locale_t, const wchar_t *, ...)
>   int vaswprintf_l (wchar_t **, locale_t, const wchar_t *, __va_list)

Not sure how you generated this list.  swprintf already behaves like
snprintf, so there is no need for a separate “n” variant.  A
wide-character version of dprintf seems unlikely to be implemented
anytime soon.

You did not list the fortify variants of these functions.

I would prefer to wait for standardization: it is not quite clear what
should happen if you use fwprintf_l on a wide stream that already uses a
different charset.

Thanks,
Florian
-- 
Red Hat GmbH, https://de.redhat.com/ , Registered seat: Grasbrunn,
Commercial register: Amtsgericht Muenchen, HRB 153243,
Managing Directors: Charles Cachera, Brian Klemm, Laurie Krebs, Michael O'Neill


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

* Re: Is there a way to use vsnprintf_l? Would it possibly be considered for an addition one day, if missing?
  2020-10-20  5:45   ` Florian Weimer
@ 2020-10-20 13:30     ` Adhemerval Zanella
  2020-10-29 15:48       ` Florian Weimer
  2020-10-29 16:02       ` ell1e
  0 siblings, 2 replies; 6+ messages in thread
From: Adhemerval Zanella @ 2020-10-20 13:30 UTC (permalink / raw)
  To: Florian Weimer, Adhemerval Zanella via Libc-help



On 20/10/2020 02:45, Florian Weimer wrote:
> * Adhemerval Zanella via Libc-help:
> 
>> If the idea is to provide a concise support for printf_l family function,
>> I think we should add not only snprintf_l or vsnprintf_l, but rather all the 
>> stdio functions for both printf *and* scanf along with the wchar_t family
>> as well.  It means to add support for:
>>
>>   int fprintf_l (FILE *, locale_t, const char *, ...)
>>   int fscanf_l (FILE * , locale_t, const char *, ...)
>>   int printf_l (locale_t, const char *, ...)
>>   int scanf_l (locale_t, const char *, ...)
>>   int sprintf_l (char *, locale_t, const char * , ...)
>>   int sscanf_l (const char *, locale_t, const char * , ...)
>>   int vfprintf_l (FILE *, locale_t, const char * , __va_list)
>>   int vprintf_l (locale_t, const char *, __va_list)
>>   int vsprintf_l (char * , locale_t, const char *, __va_list);
>>   int snprintf_l (char *, size_t, locale_t, const char * , ...)
>>   int vfscanf_l (FILE * , locale_t, const char * , __va_list)
>>   int vscanf_l (locale_t, const char *, __va_list)
>>   int vsnprintf_l (char *, size_t, locale_t, const char *, va_list)
>>   int vsscanf_l (const char *, locale_t, const char *, va_list);
>>   int dprintf_l (int, locale_t, const char *, ...)
>>   int vdprintf_l (int, locale_t, const char *, __va_list)
>>   int asprintf_l (char **, locale_t, const char *, ...)
>>   int vasprintf_l (char **, locale_t, const char *, __va_list)
>>
>>   int fwprintf_l (FILE *, locale_t, const wchar_t *, ...)
>>   int fwscanf_l (FILE * , locale_t, const wchar_t *, ...)
>>   int wprintf_l (locale_t, const wchar_t *, ...)
>>   int wscanf_l (locale_t, const wchar_t *, ...)
>>   int wsprintf_l (char *, locale_t, const wchar_t * , ...)
>>   int swscanf_l (const char *, locale_t, const wchar_t * , ...)
>>   int vfwprintf_l (FILE *, locale_t, const wchar_t * , __va_list)
>>   int vwprintf_l (locale_t, const wchar_t *, __va_list)
>>   int vswprintf_l (wchar_t * , locale_t, const wchar_t *, __va_list);
>>   int snwprintf_l (wchar_t *, size_t, locale_t, const wchar_t * , ...)
>>   int vfwscanf_l (FILE * , locale_t, const wchar_t * , __va_list)
>>   int vwscanf_l (locale_t, const char *, __va_list)
>>   int vsnwprintf_l (wchar_t *, size_t, locale_t, const wchar_t *, va_list)
>>   int vswscanf_l (const wchar_t *, locale_t, const wchar_t *, va_list);
>>   int dwprintf_l (int, locale_t, const wchar_t *, ...)
>>   int vdwprintf_l (int, locale_t, const wchar_t *, __va_list)
>>   int aswprintf_l (wchar_t **, locale_t, const wchar_t *, ...)
>>   int vaswprintf_l (wchar_t **, locale_t, const wchar_t *, __va_list)
> 
> Not sure how you generated this list.  swprintf already behaves like
> snprintf, so there is no need for a separate “n” variant.  A
> wide-character version of dprintf seems unlikely to be implemented
> anytime soon.

I didn't give much though to the list indeed and most likely this list
will need to be revised.  It was more to give the required complexity
and work to provide such functionality to glibc.

> 
> You did not list the fortify variants of these functions.

Yes, this will require even more wrapper implementations.

> 
> I would prefer to wait for standardization: it is not quite clear what
> should happen if you use fwprintf_l on a wide stream that already uses a
> different charset.

Is this being discussed somewhere? We already have at least one implementation
(not sure if MacOSX one is not the FreeBSD imported one and if Windows one
does follow the expected interface).  Should we block a possible implementation
without proper standardization? What about a non wide set of implementations,
would be feasible?

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

* Re: Is there a way to use vsnprintf_l? Would it possibly be considered for an addition one day, if missing?
  2020-10-20 13:30     ` Adhemerval Zanella
@ 2020-10-29 15:48       ` Florian Weimer
  2020-10-29 16:02       ` ell1e
  1 sibling, 0 replies; 6+ messages in thread
From: Florian Weimer @ 2020-10-29 15:48 UTC (permalink / raw)
  To: Adhemerval Zanella; +Cc: Adhemerval Zanella via Libc-help, ell1e

* Adhemerval Zanella:

>> I would prefer to wait for standardization: it is not quite clear what
>> should happen if you use fwprintf_l on a wide stream that already uses a
>> different charset.
>
> Is this being discussed somewhere? We already have at least one
> implementation (not sure if MacOSX one is not the FreeBSD imported one
> and if Windows one does follow the expected interface).  Should we
> block a possible implementation without proper standardization? What
> about a non wide set of implementations, would be feasible?

There is no strictly non-wide set of functions, due to %ls.

Thanks,
Florian
-- 
Red Hat GmbH, https://de.redhat.com/ , Registered seat: Grasbrunn,
Commercial register: Amtsgericht Muenchen, HRB 153243,
Managing Directors: Charles Cachera, Brian Klemm, Laurie Krebs, Michael O'Neill


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

* Re: Is there a way to use vsnprintf_l? Would it possibly be considered for an addition one day, if missing?
  2020-10-20 13:30     ` Adhemerval Zanella
  2020-10-29 15:48       ` Florian Weimer
@ 2020-10-29 16:02       ` ell1e
  1 sibling, 0 replies; 6+ messages in thread
From: ell1e @ 2020-10-29 16:02 UTC (permalink / raw)
  To: libc-help

Regarding wide implementations:

For what it's worth, I personally only use stdio string formatting
functions when about to head out to stdout/through a networking pipe/to
UI/... as a final export step where I assume utf-8 in C strings in my
programs usually. In other more internal places I usually use byte
buffers with length anyway that don't work with anything usual, for null
bytes compatibility, possibly use of utf-32, and more. (This is also why
I prefer locale independent behavior for my stdio string handling with
the help of *_l() functions, I don't work with alternate encodings
really if I can help it but just utf-8 everywhere.)

So I'm wondering, is wide string usage still common? I guess this
question is possibly quite off-topic, just curious since it seems
vaguely relevant to the potential demand regarding some of these missing
functions.

I assumed since the world realized UTF-16 is its way out and given one
code point isn't one character even in UTF-32 anyway, people tend to go
more back to just raw UTF-8 these days anyway. But I didn't actually run
a poll, just my personal impression. When I asked for vsnprintf_l it was
also in particular because that one I was kind of sorely lacking in
practical usage, so although I get the desire to be complete for the
sake of it I am coming to this topic from a quite practical viewpoint.

Regards,

ell1e

On 10/20/20 3:30 PM, Adhemerval Zanella wrote:
> 
> 
> On 20/10/2020 02:45, Florian Weimer wrote:
>> * Adhemerval Zanella via Libc-help:
>>
>>> If the idea is to provide a concise support for printf_l family function,
>>> I think we should add not only snprintf_l or vsnprintf_l, but rather all the 
>>> stdio functions for both printf *and* scanf along with the wchar_t family
>>> as well.  It means to add support for:
>>>
>>>   int fprintf_l (FILE *, locale_t, const char *, ...)
>>>   int fscanf_l (FILE * , locale_t, const char *, ...)
>>>   int printf_l (locale_t, const char *, ...)
>>>   int scanf_l (locale_t, const char *, ...)
>>>   int sprintf_l (char *, locale_t, const char * , ...)
>>>   int sscanf_l (const char *, locale_t, const char * , ...)
>>>   int vfprintf_l (FILE *, locale_t, const char * , __va_list)
>>>   int vprintf_l (locale_t, const char *, __va_list)
>>>   int vsprintf_l (char * , locale_t, const char *, __va_list);
>>>   int snprintf_l (char *, size_t, locale_t, const char * , ...)
>>>   int vfscanf_l (FILE * , locale_t, const char * , __va_list)
>>>   int vscanf_l (locale_t, const char *, __va_list)
>>>   int vsnprintf_l (char *, size_t, locale_t, const char *, va_list)
>>>   int vsscanf_l (const char *, locale_t, const char *, va_list);
>>>   int dprintf_l (int, locale_t, const char *, ...)
>>>   int vdprintf_l (int, locale_t, const char *, __va_list)
>>>   int asprintf_l (char **, locale_t, const char *, ...)
>>>   int vasprintf_l (char **, locale_t, const char *, __va_list)
>>>
>>>   int fwprintf_l (FILE *, locale_t, const wchar_t *, ...)
>>>   int fwscanf_l (FILE * , locale_t, const wchar_t *, ...)
>>>   int wprintf_l (locale_t, const wchar_t *, ...)
>>>   int wscanf_l (locale_t, const wchar_t *, ...)
>>>   int wsprintf_l (char *, locale_t, const wchar_t * , ...)
>>>   int swscanf_l (const char *, locale_t, const wchar_t * , ...)
>>>   int vfwprintf_l (FILE *, locale_t, const wchar_t * , __va_list)
>>>   int vwprintf_l (locale_t, const wchar_t *, __va_list)
>>>   int vswprintf_l (wchar_t * , locale_t, const wchar_t *, __va_list);
>>>   int snwprintf_l (wchar_t *, size_t, locale_t, const wchar_t * , ...)
>>>   int vfwscanf_l (FILE * , locale_t, const wchar_t * , __va_list)
>>>   int vwscanf_l (locale_t, const char *, __va_list)
>>>   int vsnwprintf_l (wchar_t *, size_t, locale_t, const wchar_t *, va_list)
>>>   int vswscanf_l (const wchar_t *, locale_t, const wchar_t *, va_list);
>>>   int dwprintf_l (int, locale_t, const wchar_t *, ...)
>>>   int vdwprintf_l (int, locale_t, const wchar_t *, __va_list)
>>>   int aswprintf_l (wchar_t **, locale_t, const wchar_t *, ...)
>>>   int vaswprintf_l (wchar_t **, locale_t, const wchar_t *, __va_list)
>>
>> Not sure how you generated this list.  swprintf already behaves like
>> snprintf, so there is no need for a separate “n” variant.  A
>> wide-character version of dprintf seems unlikely to be implemented
>> anytime soon.
> 
> I didn't give much though to the list indeed and most likely this list
> will need to be revised.  It was more to give the required complexity
> and work to provide such functionality to glibc.
> 
>>
>> You did not list the fortify variants of these functions.
> 
> Yes, this will require even more wrapper implementations.
> 
>>
>> I would prefer to wait for standardization: it is not quite clear what
>> should happen if you use fwprintf_l on a wide stream that already uses a
>> different charset.
> 
> Is this being discussed somewhere? We already have at least one implementation
> (not sure if MacOSX one is not the FreeBSD imported one and if Windows one
> does follow the expected interface).  Should we block a possible implementation
> without proper standardization? What about a non wide set of implementations,
> would be feasible?
> 

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

end of thread, other threads:[~2020-10-29 16:02 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-10-17 11:33 Is there a way to use vsnprintf_l? Would it possibly be considered for an addition one day, if missing? ell1e
2020-10-19 21:01 ` Adhemerval Zanella
2020-10-20  5:45   ` Florian Weimer
2020-10-20 13:30     ` Adhemerval Zanella
2020-10-29 15:48       ` Florian Weimer
2020-10-29 16:02       ` ell1e

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 read-only IMAP folder(s) and NNTP newsgroup(s).