public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH] libffi support for CRIS
@ 2004-10-25 13:39 Simon Posnjak
  2004-10-26  3:43 ` Hans-Peter Nilsson
  0 siblings, 1 reply; 13+ messages in thread
From: Simon Posnjak @ 2004-10-25 13:39 UTC (permalink / raw)
  To: GCC Patches

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

Hi all,

This patch adds support for CRIS platform to libffi.

The added initialize_aggregate_packed_struct function in 
libffi/src/prep_cif.c is needed because CRIS uses *packed* structs.


	Regards Simon



[-- Attachment #2: gcc_cris_libffi_submit.diff --]
[-- Type: text/x-patch, Size: 16988 bytes --]

diff -uNr gcc/libffi/Makefile.am gcc_cris/libffi/Makefile.am
--- gcc/libffi/Makefile.am	2004-10-13 19:20:21.000000000 +0200
+++ gcc_cris/libffi/Makefile.am	2004-10-25 12:58:24.101724568 +0200
@@ -7,6 +7,7 @@
 EXTRA_DIST = LICENSE ChangeLog.v1 \
 	src/alpha/ffi.c src/alpha/osf.S src/alpha/ffitarget.h \
 	src/arm/ffi.c src/arm/sysv.S src/arm/ffitarget.h \
+	src/cris/ffi.c src/cris/sysv.S src/cris/ffitarget.h \
 	src/mips/ffi.c src/mips/n32.S src/mips/o32.S \
 	src/mips/ffitarget.h \
 	src/m32r/ffi.c src/m32r/sysv.S src/m32r/ffitarget.h \
@@ -120,6 +121,9 @@
 if ARM
 nodist_libffi_la_SOURCES += src/arm/sysv.S src/arm/ffi.c
 endif
+if CRIS32
+nodist_libffi_la_SOURCES += src/cris/sysv.S src/cris/ffi.c
+endif
 if FRV
 nodist_libffi_la_SOURCES += src/frv/eabi.S src/frv/ffi.c
 endif
diff -uNr gcc/libffi/configure.ac gcc_cris/libffi/configure.ac
--- gcc/libffi/configure.ac	2004-10-13 19:20:23.000000000 +0200
+++ gcc_cris/libffi/configure.ac	2004-10-25 12:58:24.102724416 +0200
@@ -68,6 +68,7 @@
 rs6000-*-aix*) TARGET=POWERPC_AIX; TARGETDIR=powerpc;;
 arm*-*-linux-*) TARGET=ARM; TARGETDIR=arm;;
 arm*-*-netbsdelf* | arm*-*-knetbsd*-gnu) TARGET=ARM; TARGETDIR=arm;;
+cris*-*-linux-*) TARGET=CRIS32; TARGETDIR=cris;;
 s390-*-linux-*) TARGET=S390; TARGETDIR=s390;;
 s390x-*-linux-*) TARGET=S390; TARGETDIR=s390;;
 x86_64-*-linux* | x86_64-*-freebsd* | x86_64-*-kfreebsd*-gnu) TARGET=X86_64; TARGETDIR=x86;;
@@ -95,6 +96,7 @@
 AM_CONDITIONAL(POWERPC_AIX, test x$TARGET = xPOWERPC_AIX)
 AM_CONDITIONAL(POWERPC_DARWIN, test x$TARGET = xPOWERPC_DARWIN)
 AM_CONDITIONAL(ARM, test x$TARGET = xARM)
+AM_CONDITIONAL(CRIS32, test x$TARGET = xCRIS32)
 AM_CONDITIONAL(FRV, test x$TARGET = xFRV)
 AM_CONDITIONAL(S390, test x$TARGET = xS390)
 AM_CONDITIONAL(X86_64, test x$TARGET = xX86_64)
diff -uNr gcc/libffi/src/cris/ffi.c gcc_cris/libffi/src/cris/ffi.c
--- gcc/libffi/src/cris/ffi.c	1970-01-01 01:00:00.000000000 +0100
+++ gcc_cris/libffi/src/cris/ffi.c	2004-10-25 12:58:24.103724264 +0200
@@ -0,0 +1,199 @@
+/* -----------------------------------------------------------------------
+   ffi.c - Copyright (c) 1998 Cygnus Solutions
+           Copyright (c) 2004 Simon Posnjak
+
+   CRIS Foreign Function Interface
+
+   Permission is hereby granted, free of charge, to any person obtaining
+   a copy of this software and associated documentation files (the
+   ``Software''), to deal in the Software without restriction, including
+   without limitation the rights to use, copy, modify, merge, publish,
+   distribute, sublicense, and/or sell copies of the Software, and to
+   permit persons to whom the Software is furnished to do so, subject to
+   the following conditions:
+
+   The above copyright notice and this permission notice shall be included
+   in all copies or substantial portions of the Software.
+
+   THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, EXPRESS
+   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+   IN NO EVENT SHALL CYGNUS SOLUTIONS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+   OTHER DEALINGS IN THE SOFTWARE.
+   ----------------------------------------------------------------------- */
+
+#include <ffi.h>
+#include <ffi_common.h>
+
+/* ffi_prep_args is called by the assembly routine once stack space
+   has been allocated for the function's arguments */
+
+/*@-exportheader@*/
+int ffi_prep_args (char *stack, extended_cif * ecif)
+/*@=exportheader@*/
+{
+  register unsigned int i;
+  register unsigned int struct_count = 0;
+  register void **p_argv;
+  register char *argp;
+  register ffi_type **p_arg;
+
+  argp = stack;
+
+//  if (ecif->cif->rtype->type == FFI_TYPE_STRUCT ) {
+//    *(void **) argp = ecif->rvalue;
+//    argp += 4;
+//  }
+
+  p_argv = ecif->avalue;
+
+  for (i = ecif->cif->nargs, p_arg = ecif->cif->arg_types;
+       (i != 0); i--, p_arg++)
+    {
+      size_t z;
+
+      /* Align if necessary */
+      if (((*p_arg)->alignment - 1) & (unsigned) argp)
+	{
+	  argp = (char *) ALIGN (argp, (*p_arg)->alignment);
+	}
+
+      switch ((*p_arg)->type)
+	{
+	case FFI_TYPE_STRUCT:
+	  {
+	    z = (*p_arg)->size;
+	    if (z <= 8)
+	      {
+		memcpy (argp, *p_argv, z);
+		z += (4 - (z % 4));
+	      }
+	    else
+	      {
+		unsigned int uiLocOnStack;
+		
+		uiLocOnStack  = 4 * ecif->cif->nargs + struct_count;
+		struct_count = struct_count + (*p_arg)->size;
+		*(unsigned int *) argp =
+		  (unsigned int) (UINT32 *) (stack + uiLocOnStack);
+		memcpy ((stack + uiLocOnStack), *p_argv, (*p_arg)->size);
+	      }
+	    break;
+	  }
+	default:
+	  z = (*p_arg)->size;
+	  if (z < sizeof (int))
+	    {
+	      z = sizeof (int);
+	      switch ((*p_arg)->type)
+		{
+		case FFI_TYPE_SINT8:
+		  *(signed int *) argp = (signed int) *(SINT8 *) (*p_argv);
+		  break;
+
+		case FFI_TYPE_UINT8:
+		  *(unsigned int *) argp =
+		    (unsigned int) *(UINT8 *) (*p_argv);
+		  break;
+
+		case FFI_TYPE_SINT16:
+		  *(signed int *) argp = (signed int) *(SINT16 *) (*p_argv);
+		  break;
+
+		case FFI_TYPE_UINT16:
+		  *(unsigned int *) argp =
+		    (unsigned int) *(UINT16 *) (*p_argv);
+		  break;
+
+		default:
+		  FFI_ASSERT (0);
+		}
+	    }
+	  else if (z == sizeof (int))
+	    {
+	      *(unsigned int *) argp = (unsigned int) *(UINT32 *) (*p_argv);
+	    }
+	  else
+	    {
+	      memcpy (argp, *p_argv, z);
+	    }
+	  break;
+	}
+      p_argv++;
+      argp += z;
+    }
+
+  return (struct_count);
+}
+
+/* Perform machine dependent cif processing */
+ffi_status ffi_prep_cif_machdep (ffi_cif * cif)
+{
+  switch (cif->rtype->type)
+    {
+    case FFI_TYPE_VOID:
+    case FFI_TYPE_STRUCT:
+    case FFI_TYPE_FLOAT:
+    case FFI_TYPE_DOUBLE:
+    case FFI_TYPE_SINT64:
+    case FFI_TYPE_UINT64:
+      cif->flags = (unsigned) cif->rtype->type;
+      break;
+
+    default:
+      cif->flags = FFI_TYPE_INT;
+      break;
+    }
+
+  return FFI_OK;
+}
+
+
+/*@-declundef@*/
+/*@-exportheader@*/
+extern void ffi_call_SYSV (void (*)(char *, extended_cif *),
+			   /*@out@ */ extended_cif *,
+			   unsigned, unsigned,
+			   /*@out@ */ unsigned *,
+			   void (*fn) ());
+/*@=declundef@*/
+/*@=exportheader@*/
+
+void ffi_call ( /*@dependent@ */ ffi_cif * cif,
+	  void (*fn) (),
+	  /*@out@ */ void *rvalue,
+	  /*@dependent@ */ void **avalue)
+{
+  extended_cif ecif;
+
+  ecif.cif = cif;
+  ecif.avalue = avalue;
+
+  /* If the return value is a struct and we don't have a return */
+  /* value address then we need to make one                     */
+
+  if ((rvalue == NULL) && (cif->rtype->type == FFI_TYPE_STRUCT))
+    {
+      /*@-sysunrecog@ */
+      ecif.rvalue = alloca (cif->rtype->size);
+      /*@=sysunrecog@ */
+    }
+  else
+    ecif.rvalue = rvalue;
+
+
+  switch (cif->abi)
+    {
+    case FFI_SYSV:
+      /*@-usedef@ */
+      ffi_call_SYSV (ffi_prep_args, &ecif, cif->bytes,
+		     cif->flags, ecif.rvalue, fn);
+      /*@=usedef@ */
+      break;
+    default:
+      FFI_ASSERT (0);
+      break;
+    }
+}
diff -uNr gcc/libffi/src/cris/ffitarget.h gcc_cris/libffi/src/cris/ffitarget.h
--- gcc/libffi/src/cris/ffitarget.h	1970-01-01 01:00:00.000000000 +0100
+++ gcc_cris/libffi/src/cris/ffitarget.h	2004-10-25 12:58:24.104724112 +0200
@@ -0,0 +1,47 @@
+/* -----------------------------------------------------------------*-C-*-
+   ffitarget.h - Copyright (c) 1996-2003  Red Hat, Inc.
+   Target configuration macros for CRIS.
+
+   Permission is hereby granted, free of charge, to any person obtaining
+   a copy of this software and associated documentation files (the
+   ``Software''), to deal in the Software without restriction, including
+   without limitation the rights to use, copy, modify, merge, publish,
+   distribute, sublicense, and/or sell copies of the Software, and to
+   permit persons to whom the Software is furnished to do so, subject to
+   the following conditions:
+
+   The above copyright notice and this permission notice shall be included
+   in all copies or substantial portions of the Software.
+
+   THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, EXPRESS
+   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+   IN NO EVENT SHALL CYGNUS SOLUTIONS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+   OTHER DEALINGS IN THE SOFTWARE.
+
+   ----------------------------------------------------------------------- */
+
+#ifndef LIBFFI_TARGET_H
+#define LIBFFI_TARGET_H
+
+#ifndef LIBFFI_ASM
+typedef unsigned long          ffi_arg;
+typedef signed long            ffi_sarg;
+
+typedef enum ffi_abi {
+  FFI_FIRST_ABI = 0,
+  FFI_SYSV,
+  FFI_DEFAULT_ABI = FFI_SYSV,
+  FFI_LAST_ABI = FFI_DEFAULT_ABI + 1
+} ffi_abi;
+#endif
+
+/* ---- Definitions for closures ----------------------------------------- */
+
+#define FFI_CLOSURES 0
+#define FFI_NATIVE_RAW_API 0
+
+#endif
+
diff -uNr gcc/libffi/src/cris/sysv.S gcc_cris/libffi/src/cris/sysv.S
--- gcc/libffi/src/cris/sysv.S	1970-01-01 01:00:00.000000000 +0100
+++ gcc_cris/libffi/src/cris/sysv.S	2004-10-25 12:58:24.105723960 +0200
@@ -0,0 +1,163 @@
+/* -----------------------------------------------------------------------
+   sysv.S - Copyright (c) 2004 Simon Posnjak
+
+   CRIS Foreign Function Interface
+
+   Permission is hereby granted, free of charge, to any person obtaining
+   a copy of this software and associated documentation files (the
+   ``Software''), to deal in the Software without restriction, including
+   without limitation the rights to use, copy, modify, merge, publish,
+   distribute, sublicense, and/or sell copies of the Software, and to
+   permit persons to whom the Software is furnished to do so, subject to
+   the following conditions:
+
+   The above copyright notice and this permission notice shall be included
+   in all copies or substantial portions of the Software.
+
+   THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, EXPRESS
+   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+   IN NO EVENT SHALL CYGNUS SOLUTIONS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+   OTHER DEALINGS IN THE SOFTWARE.
+   ----------------------------------------------------------------------- */
+
+#define LIBFFI_ASM
+#include <ffi.h>
+
+	.text
+
+	;; OK, when we get called we should have this (according to 
+	;; AXIS ETRAX 100LX Programmer's Manual chapter 6.3)
+	;; 
+	;; R10:	 ffi_prep_args (func. pointer)
+	;; R11:  &ecif
+	;; R12:  cif->bytes
+	;; R13:  fig->flags
+	;; sp+0: ecif.rvalue
+	;; sp+4: fn (function pointer to the function that we need to call)
+
+	.globl  ffi_call_SYSV
+	.type   ffi_call_SYSV,@function
+
+ffi_call_SYSV:
+	;; save the regs to the stack
+	Push $srp
+	push $r6
+	push $r7
+	push $r8
+	push $r13
+	move.d $sp,$r8
+	move.d $sp,$r6
+	
+	;; move address of ffi_prep_args to r13
+	move.d $r10, $r13
+
+	;; make room on the stack for the args of fn
+	sub.d  $r12, $sp
+	
+	;; void ffi_prep_args(char *stack, extended_cif *ecif)
+	;; so:
+	;; r10 <-- stack pointer
+	;; r11 <-- &ecif (already there)
+	move.d $sp,$r10
+	
+	;; load the ffi_prep_args function pointer into r13
+	;; call the function
+	jsr $r13
+	
+	;; save the size of the structures which are passed by stack
+	move.d $r10, $r7
+	
+	;; move first four args in to r10..r13
+	move.d [$sp+0], $r10
+	move.d [$sp+4], $r11
+	move.d [$sp+8], $r12
+	move.d [$sp+12], $r13
+	
+	;; adjust the stack
+	addq 16, $sp
+	sub.d $r7, $r6
+	cmp.d $sp, $r6
+	
+	bmi go_on_no_params_on_stack
+	nop
+	
+        beq go_on_no_params_on_stack
+        nop
+	
+	ba go_on
+	nop
+	
+go_on_no_params_on_stack:
+	move.d $r6, $sp
+	
+go_on:
+	;; discover if we need to put rval address in to $r9
+        move.d [$r8 + 0], $r7
+	cmpq FFI_TYPE_STRUCT, $r7
+	bne call_now
+	nop
+	
+	move.d [$r8 + 20], $r9
+
+call_now:
+	;; move address of the function in to r7
+	move.d [$r8 + 24], $r7
+	
+	;; call the function
+	jsr $r7
+	
+	;; reset stack
+	move.d $r8, $sp
+	
+	;; Load rval type in to r13
+	pop $r13
+	
+	;; detect rval type
+	cmpq FFI_TYPE_VOID, $r13
+	beq epilogue
+	nop
+
+	cmpq FFI_TYPE_STRUCT, $r13
+	beq epilogue
+	nop
+	
+	cmpq FFI_TYPE_DOUBLE, $r13
+	beq return_double_or_longlong
+	nop
+
+	cmpq FFI_TYPE_UINT64, $r13
+	beq return_double_or_longlong
+	nop
+
+	cmpq FFI_TYPE_SINT64, $r13
+	beq return_double_or_longlong
+	nop
+
+	;; just return the 32 bit value
+	ba return
+	nop
+		
+return_double_or_longlong:
+	move.d [$sp+16], $r13
+	move.d $r10, [$r13]
+	addq 4, $r13
+	move.d $r11, [$r13]
+	ba epilogue
+	nop	
+	
+return:
+	move.d [$sp+16], $r13
+	move.d $r10, [$r13]
+	ba epilogue
+	nop
+		
+epilogue:
+	pop $r8
+	pop $r7
+	pop $r6
+	Jump [$sp+]
+	
+        .size   ffi_call_SYSV,.-ffi_call_SYSV
diff -uNr gcc/libffi/src/prep_cif.c gcc_cris/libffi/src/prep_cif.c
--- gcc/libffi/src/prep_cif.c	2004-03-19 23:34:17.000000000 +0100
+++ gcc_cris/libffi/src/prep_cif.c	2004-10-25 12:58:24.106723808 +0200
@@ -33,6 +33,44 @@
 /* Perform machine independent initialization of aggregate type
    specifications. */
 
+ffi_status initialize_aggregate_packed_struct ( /*@out@ */ ffi_type * arg)
+{
+  ffi_type **ptr;
+
+  FFI_ASSERT (arg != NULL);
+
+  /*@-usedef@ */
+
+  FFI_ASSERT (arg->elements != NULL);
+  FFI_ASSERT (arg->size == 0);
+  FFI_ASSERT (arg->alignment == 0);
+
+  ptr = &(arg->elements[0]);
+
+  while ((*ptr) != NULL)    {
+      if (((*ptr)->size == 0)
+          && (initialize_aggregate_packed_struct ((*ptr)) != FFI_OK))
+        return FFI_BAD_TYPEDEF;
+
+      /* Perform a sanity check on the argument type */
+      FFI_ASSERT (ffi_type_test ((*ptr)));
+
+      arg->size += (*ptr)->size;
+
+      arg->alignment = (arg->alignment > (*ptr)->alignment) ?
+        arg->alignment : (*ptr)->alignment;
+
+      ptr++;
+    }
+
+  if (arg->size == 0)
+    return FFI_BAD_TYPEDEF;
+  else
+    return FFI_OK;
+
+  /*@=usedef@ */
+}
+
 static ffi_status initialize_aggregate(/*@out@*/ ffi_type *arg)
 {
   ffi_type **ptr; 
@@ -113,7 +151,7 @@
   FFI_ASSERT_VALID_TYPE(cif->rtype);
 
   /* x86-64 and s390 stack space allocation is handled in prep_machdep.  */
-#if !defined M68K && !defined __x86_64__ && !defined S390 && !defined PA
+#if !defined M68K && !defined __x86_64__ && !defined S390 && !defined PA && !defined CRIS32
   /* Make space for the return structure pointer */
   if (cif->rtype->type == FFI_TYPE_STRUCT
 #ifdef SPARC
@@ -127,8 +165,20 @@
     {
 
       /* Initialize any uninitialized aggregate type definitions */
+#ifdef CRIS32
+      if ((*ptr)->type == FFI_TYPE_STRUCT)
+      {
+        if (((*ptr)->size == 0) && (initialize_aggregate_packed_struct((*ptr)) != FFI_OK))
+           return FFI_BAD_TYPEDEF;
+      }
+      else
+      {
+#endif
       if (((*ptr)->size == 0) && (initialize_aggregate((*ptr)) != FFI_OK))
 	return FFI_BAD_TYPEDEF;
+#ifdef CRIS32
+      }
+#endif
 
       /* Perform a sanity check on the argument type, do this 
 	 check after the initialization.  */
@@ -147,8 +197,29 @@
 	  /* Add any padding if necessary */
 	  if (((*ptr)->alignment - 1) & bytes)
 	    bytes = ALIGN(bytes, (*ptr)->alignment);
-	  
+#ifdef CRIS32
+          if ((*ptr)->type == FFI_TYPE_STRUCT)
+          {
+           if ((*ptr)->size > 8)
+           {
+               bytes += (*ptr)->size;
+               bytes += sizeof (void *);
+           }
+           else
+           {
+               if ((*ptr)->size > 4)
+                   bytes += 8;
+               else
+                   bytes += 4;
+           }
+         }
+         else
+         {
+           bytes += STACK_ARG_SIZE((*ptr)->size);
+         }
+#else
 	  bytes += STACK_ARG_SIZE((*ptr)->size);
+#endif
 	}
 #endif
     }
diff -uNr gcc/libffi/src/types.c gcc_cris/libffi/src/types.c
--- gcc/libffi/src/types.c	2004-10-13 19:20:24.000000000 +0200
+++ gcc_cris/libffi/src/types.c	2004-10-25 12:58:24.106723808 +0200
@@ -53,7 +53,7 @@
 
 #endif
 
-#if defined X86 || defined ARM || defined M68K
+#if defined X86 || defined ARM || defined M68K || defined CRIS32
 
 FFI_INTEGRAL_TYPEDEF(uint64, 8, 4, FFI_TYPE_UINT64);
 FFI_INTEGRAL_TYPEDEF(sint64, 8, 4, FFI_TYPE_SINT64);
@@ -80,7 +80,7 @@
 #endif
 FFI_INTEGRAL_TYPEDEF(longdouble, 12, 4, FFI_TYPE_LONGDOUBLE);
 
-#elif defined ARM || defined SH || defined POWERPC_AIX || defined M32R
+#elif defined ARM || defined SH || defined POWERPC_AIX || defined M32R || defined CRIS32
 
 FFI_INTEGRAL_TYPEDEF(double, 8, 4, FFI_TYPE_DOUBLE);
 FFI_INTEGRAL_TYPEDEF(longdouble, 8, 4, FFI_TYPE_LONGDOUBLE);

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

end of thread, other threads:[~2004-11-16 16:54 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2004-10-25 13:39 [PATCH] libffi support for CRIS Simon Posnjak
2004-10-26  3:43 ` Hans-Peter Nilsson
2004-10-26 11:53   ` Simon Posnjak
2004-10-28  4:23     ` Hans-Peter Nilsson
2004-10-28  5:06       ` Hans Boehm
2004-10-28  5:21         ` Hans-Peter Nilsson
2004-10-28  5:25           ` Hans Boehm
2004-10-28  7:23             ` Hans-Peter Nilsson
2004-10-28 10:22       ` Simon Posnjak
2004-11-06 18:55         ` Hans-Peter Nilsson
2004-11-08  9:27           ` Simon Posnjak
2004-11-09  0:41             ` Hans-Peter Nilsson
2004-11-16 17:33           ` Tom Tromey

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).