Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
From: Jan Kratochvil <jan.kratochvil@redhat.com>
To: gdb-patches@sourceware.org
Subject: Re: [RFC] print/x of a float/double should printf ("%a")
Date: Fri, 23 Feb 2007 16:15:00 -0000	[thread overview]
Message-ID: <20070223161453.GA479@host0.dyn.jankratochvil.net> (raw)
In-Reply-To: <uirdzjjql.fsf@gnu.org>

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

On Sun, 18 Feb 2007 21:25:54 +0100, Eli Zaretskii wrote:
> > Date: Sun, 18 Feb 2007 16:37:06 +0100
> > From: Jan Kratochvil <jan.kratochvil@redhat.com>
> > 
> > currently "print/x (float) a" does an integer "print/x (long) a".
> > 
> > Ulrich Drepper requested to use instead printf ("%a") (man excerpt below):
...
>   . Please submit an appropriate change for the manual.

Attached.


> > Systems not supporting "%a" will print the value as
> > 	print (float) a
> > which differs from the current way there:
> > 	print/x (long) a
> 
> This makes this an incompatible change.  For this reason, I'd prefer a
> new format letter, like Andreas suggested.

I believe this change is to make the most common default action more valid.
Also some hardly noticeable changes are what separate GDB stable release are
for, aren't they?

If someone has very specific printing needs s/he still can use the GDB function
`printf' (instead of `print') or even to use `call printf' with the right
format strings and parameters casting.


As I did not find general consensus here I just updated the patch by the doc
part and also implemented a forgotten "%a"/"%A" for the GDB `printf' command.


Regards,
Jan

[-- Attachment #2: gdb-printf-x-2.patch --]
[-- Type: text/plain, Size: 17295 bytes --]

2007-02-23  Jan Kratochvil <jan.kratochvil@redhat.com>

	* configure.ac (PRINTF_HAS_HEX_DOUBLE): Test for printf ("%a").
	* configure, config.in: Regenerate.
	* printcmd.c (printf_command): Support "%a"/"%A" formatting.
	(print_scalar_formatted): Print TYPE_CODE_FLT/x
	by print_floating (format='x').
	* valprint.c (print_floating): Add FORMAT parameter.
	* value.h (print_floating): Update prototype.
	* ada-valprint.c (ada_print_floating): Callers changed.
	* c-valprint.c (c_val_print): Likewise.
	* f-valprint.c (f_val_print): Likewise.
	* m2-valprint.c (m2_val_print): Likewise.
	* p-valprint.c (pascal_val_print): Likewise.

2007-02-23  Jan Kratochvil <jan.kratochvil@redhat.com>

	* gdb.texinfo (Output formats): Described hexadecimal floats printing.

2007-02-18  Jan Kratochvil <jan.kratochvil@redhat.com>

	* gdb.arch/i386-sse.exp: Check $xmm.v4_float and $xmm.v2_double
	printing in hexadecimal format.
	* gdb.base/gdb1821.exp: Update the DOUBLE/x print check.


Index: gdb/configure.ac
===================================================================
RCS file: /cvs/src/src/gdb/configure.ac,v
retrieving revision 1.42
diff -u -p -r1.42 configure.ac
--- gdb/configure.ac	2 Feb 2007 22:54:09 -0000	1.42
+++ gdb/configure.ac	18 Feb 2007 04:35:38 -0000
@@ -955,6 +955,23 @@ aix*)
   ;;
 esac
 
+# Check if the compiler and runtime support printing doubles in hex.
+
+AC_CACHE_CHECK([for hex double support in printf],
+               gdb_cv_printf_has_hex_double,
+               [AC_RUN_IFELSE([AC_LANG_PROGRAM([AC_INCLUDES_DEFAULT],
+[[char buf[16];
+  double f = 3.141592653;
+  sprintf (buf, "%a", f);
+  return (strncmp ("0x1.921fb", buf, 9));]])],
+                              gdb_cv_printf_has_hex_double=yes,
+                              gdb_cv_printf_has_hex_double=no,
+                              gdb_cv_printf_has_hex_double=no)])
+if test $gdb_cv_printf_has_hex_double = yes; then
+  AC_DEFINE(PRINTF_HAS_HEX_DOUBLE, 1,
+            [Define to 1 if the "%a" format works to print doubles in hex.])
+fi
+
 
 dnl For certain native configurations, we need to check whether thread
 dnl support can be built in or not.
Index: gdb/ada-valprint.c
===================================================================
RCS file: /cvs/src/src/gdb/ada-valprint.c,v
retrieving revision 1.30
diff -u -p -r1.30 ada-valprint.c
--- gdb/ada-valprint.c	9 Jan 2007 17:58:49 -0000	1.30
+++ gdb/ada-valprint.c	18 Feb 2007 04:35:32 -0000
@@ -308,7 +308,7 @@ ada_print_floating (const gdb_byte *vala
   struct ui_file *tmp_stream = mem_fileopen ();
   struct cleanup *cleanups = make_cleanup_ui_file_delete (tmp_stream);
 
-  print_floating (valaddr, type, tmp_stream);
+  print_floating (valaddr, type, tmp_stream, 0);
   ui_file_put (tmp_stream, ui_memcpy, buffer);
   do_cleanups (cleanups);
 
Index: gdb/c-valprint.c
===================================================================
RCS file: /cvs/src/src/gdb/c-valprint.c,v
retrieving revision 1.42
diff -u -p -r1.42 c-valprint.c
--- gdb/c-valprint.c	26 Jan 2007 20:54:16 -0000	1.42
+++ gdb/c-valprint.c	18 Feb 2007 04:35:32 -0000
@@ -432,7 +432,7 @@ c_val_print (struct type *type, const gd
 	}
       else
 	{
-	  print_floating (valaddr + embedded_offset, type, stream);
+	  print_floating (valaddr + embedded_offset, type, stream, 0);
 	}
       break;
 
@@ -458,7 +458,7 @@ c_val_print (struct type *type, const gd
 				format, 0, stream);
       else
 	print_floating (valaddr + embedded_offset, TYPE_TARGET_TYPE (type),
-			stream);
+			stream, 0);
       fprintf_filtered (stream, " + ");
       if (format)
 	print_scalar_formatted (valaddr + embedded_offset
@@ -469,7 +469,7 @@ c_val_print (struct type *type, const gd
 	print_floating (valaddr + embedded_offset
 			+ TYPE_LENGTH (TYPE_TARGET_TYPE (type)),
 			TYPE_TARGET_TYPE (type),
-			stream);
+			stream, 0);
       fprintf_filtered (stream, " * I");
       break;
 
Index: gdb/f-valprint.c
===================================================================
RCS file: /cvs/src/src/gdb/f-valprint.c,v
retrieving revision 1.34
diff -u -p -r1.34 f-valprint.c
--- gdb/f-valprint.c	9 Jan 2007 17:58:50 -0000	1.34
+++ gdb/f-valprint.c	18 Feb 2007 04:35:38 -0000
@@ -495,7 +495,7 @@ f_val_print (struct type *type, const gd
       if (format)
 	print_scalar_formatted (valaddr, type, format, 0, stream);
       else
-	print_floating (valaddr, type, stream);
+	print_floating (valaddr, type, stream, 0);
       break;
 
     case TYPE_CODE_VOID:
@@ -571,9 +571,9 @@ f_val_print (struct type *type, const gd
 	  error (_("Cannot print out complex*%d variables"), TYPE_LENGTH (type));
 	}
       fputs_filtered ("(", stream);
-      print_floating (valaddr, type, stream);
+      print_floating (valaddr, type, stream, 0);
       fputs_filtered (",", stream);
-      print_floating (valaddr + TYPE_LENGTH (type), type, stream);
+      print_floating (valaddr + TYPE_LENGTH (type), type, stream, 0);
       fputs_filtered (")", stream);
       break;
 
Index: gdb/m2-valprint.c
===================================================================
RCS file: /cvs/src/src/gdb/m2-valprint.c,v
retrieving revision 1.12
diff -u -p -r1.12 m2-valprint.c
--- gdb/m2-valprint.c	9 Jan 2007 17:58:51 -0000	1.12
+++ gdb/m2-valprint.c	18 Feb 2007 04:35:38 -0000
@@ -479,7 +479,7 @@ m2_val_print (struct type *type, const g
 	print_scalar_formatted (valaddr + embedded_offset, type,
 				format, 0, stream);
       else
-	print_floating (valaddr + embedded_offset, type, stream);
+	print_floating (valaddr + embedded_offset, type, stream, 0);
       break;
 
     case TYPE_CODE_METHOD:
Index: gdb/p-valprint.c
===================================================================
RCS file: /cvs/src/src/gdb/p-valprint.c,v
retrieving revision 1.47
diff -u -p -r1.47 p-valprint.c
--- gdb/p-valprint.c	8 Feb 2007 14:20:56 -0000	1.47
+++ gdb/p-valprint.c	18 Feb 2007 04:35:38 -0000
@@ -426,7 +426,7 @@ pascal_val_print (struct type *type, con
 	}
       else
 	{
-	  print_floating (valaddr + embedded_offset, type, stream);
+	  print_floating (valaddr + embedded_offset, type, stream, 0);
 	}
       break;
 
Index: gdb/printcmd.c
===================================================================
RCS file: /cvs/src/src/gdb/printcmd.c,v
retrieving revision 1.100
diff -u -p -r1.100 printcmd.c
--- gdb/printcmd.c	9 Jan 2007 17:58:56 -0000	1.100
+++ gdb/printcmd.c	18 Feb 2007 04:35:38 -0000
@@ -362,23 +362,36 @@ print_scalar_formatted (const void *vala
   switch (format)
     {
     case 'x':
-      if (!size)
-	{
-	  /* No size specified, like in print.  Print varying # of digits.  */
-	  print_longest (stream, 'x', 1, val_long);
-	}
-      else
-	switch (size)
-	  {
-	  case 'b':
-	  case 'h':
-	  case 'w':
-	  case 'g':
-	    print_longest (stream, size, 1, val_long);
-	    break;
-	  default:
-	    error (_("Undefined output size \"%c\"."), size);
-	  }
+      /* TYPE_CODE_FLT gets printed in hex by print_floating() below.  */
+      if (TYPE_CODE (type) != TYPE_CODE_FLT)
+        {
+	  if (!size)
+	    {
+	      /* No size specified, like in print.  Print varying # of digits.  */
+	      print_longest (stream, 'x', 1, val_long);
+	    }
+	  else
+	    switch (size)
+	      {
+	      case 'b':
+	      case 'h':
+	      case 'w':
+	      case 'g':
+		print_longest (stream, size, 1, val_long);
+		break;
+	      default:
+		error (_("Undefined output size \"%c\"."), size);
+	      }
+	  break;
+        }
+    case 'f':
+      if (len == TYPE_LENGTH (builtin_type_float))
+        type = builtin_type_float;
+      else if (len == TYPE_LENGTH (builtin_type_double))
+        type = builtin_type_double;
+      else if (len == TYPE_LENGTH (builtin_type_long_double))
+        type = builtin_type_long_double;
+      print_floating (valaddr, type, stream, format);
       break;
 
     case 'd':
@@ -408,16 +421,6 @@ print_scalar_formatted (const void *vala
 		   stream, 0, Val_pretty_default);
       break;
 
-    case 'f':
-      if (len == TYPE_LENGTH (builtin_type_float))
-        type = builtin_type_float;
-      else if (len == TYPE_LENGTH (builtin_type_double))
-        type = builtin_type_double;
-      else if (len == TYPE_LENGTH (builtin_type_long_double))
-        type = builtin_type_long_double;
-      print_floating (valaddr, type, stream);
-      break;
-
     case 0:
       internal_error (__FILE__, __LINE__,
 		      _("failed internal consistency check"));
@@ -1940,6 +1943,10 @@ printf_command (char *arg, int from_tty)
 		bad = 1;
 	      break;
 
+#ifdef PRINTF_HAS_HEX_DOUBLE
+	    case 'a':
+	    case 'A':
+#endif
 	    case 'e':
 	    case 'f':
 	    case 'g':
Index: gdb/valprint.c
===================================================================
RCS file: /cvs/src/src/gdb/valprint.c,v
retrieving revision 1.64
diff -u -p -r1.64 valprint.c
--- gdb/valprint.c	9 Jan 2007 17:58:59 -0000	1.64
+++ gdb/valprint.c	18 Feb 2007 04:35:45 -0000
@@ -432,16 +432,18 @@ longest_to_int (LONGEST arg)
 }
 
 /* Print a floating point value of type TYPE (not always a
-   TYPE_CODE_FLT), pointed to in GDB by VALADDR, on STREAM.  */
+   TYPE_CODE_FLT), pointed to in GDB by VALADDR, on STREAM.
+   Print in hex if supported on host and requested by FORMAT 'x'.  */
 
 void
 print_floating (const gdb_byte *valaddr, struct type *type,
-		struct ui_file *stream)
+		struct ui_file *stream, int format)
 {
   DOUBLEST doub;
   int inv;
   const struct floatformat *fmt = NULL;
   unsigned len = TYPE_LENGTH (type);
+  const char *format_string;
 
   /* If it is a floating-point, check for obvious problems.  */
   if (TYPE_CODE (type) == TYPE_CODE_FLT)
@@ -481,17 +483,32 @@ print_floating (const gdb_byte *valaddr,
      instead uses the type's length to determine the precision of the
      floating-point value being printed.  */
 
+  /* Do not use a format string variable to permit arguments checking.  */
+#define FORMAT_PREFIX "%.*"
+#ifdef PRINTF_HAS_LONG_DOUBLE
+# define FORMAT_LONG FORMAT_PREFIX "L"
+#else
+# define FORMAT_LONG FORMAT_PREFIX
+#endif
+#ifdef PRINTF_HAS_HEX_DOUBLE
+# define FORMAT_MAY_HEX (format == 'x' ? FORMAT_PREFIX "a" : FORMAT_PREFIX "g")
+# define FORMAT_LONG_MAY_HEX (format == 'x' ? FORMAT_LONG "a" : FORMAT_LONG "g")
+#else
+# define FORMAT_MAY_HEX FORMAT_PREFIX "g"
+# define FORMAT_LONG_MAY_HEX FORMAT_LONG "g"
+#endif
+
   if (len < sizeof (double))
-      fprintf_filtered (stream, "%.9g", (double) doub);
+      fprintf_filtered (stream, FORMAT_MAY_HEX, 9, (double) doub);
   else if (len == sizeof (double))
-      fprintf_filtered (stream, "%.17g", (double) doub);
+      fprintf_filtered (stream, FORMAT_MAY_HEX, 17, (double) doub);
   else
 #ifdef PRINTF_HAS_LONG_DOUBLE
-    fprintf_filtered (stream, "%.35Lg", doub);
+    fprintf_filtered (stream, FORMAT_LONG_MAY_HEX, 35, doub);
 #else
     /* This at least wins with values that are representable as
        doubles.  */
-    fprintf_filtered (stream, "%.17g", (double) doub);
+    fprintf_filtered (stream, FORMAT_MAY_HEX, 17, (double) doub);
 #endif
 }
 
Index: gdb/value.h
===================================================================
RCS file: /cvs/src/src/gdb/value.h,v
retrieving revision 1.96
diff -u -p -r1.96 value.h
--- gdb/value.h	9 Jan 2007 17:58:59 -0000	1.96
+++ gdb/value.h	18 Feb 2007 04:35:45 -0000
@@ -480,7 +480,7 @@ extern void print_longest (struct ui_fil
 			   int use_local, LONGEST val);
 
 extern void print_floating (const gdb_byte *valaddr, struct type *type,
-			    struct ui_file *stream);
+			    struct ui_file *stream, int format);
 
 extern int value_print (struct value *val, struct ui_file *stream, int format,
 			enum val_prettyprint pretty);
Index: gdb/testsuite/gdb.arch/i386-sse.exp
===================================================================
RCS file: /cvs/src/src/gdb/testsuite/gdb.arch/i386-sse.exp,v
retrieving revision 1.6
diff -u -p -r1.6 i386-sse.exp
--- gdb/testsuite/gdb.arch/i386-sse.exp	26 Jan 2007 20:53:15 -0000	1.6
+++ gdb/testsuite/gdb.arch/i386-sse.exp	18 Feb 2007 04:35:45 -0000
@@ -87,6 +87,12 @@ foreach r {0 1 2 3 4 5 6 7} {
     gdb_test "print \$xmm$r.v16_int8" \
         ".. = \\{(-?\[0-9\]+ '.*', ){15}-?\[0-9\]+ '.*'\\}.*" \
         "check int8 contents of %xmm$r"
+    gdb_test "print/x \$xmm$r.v4_float" \
+        ".. = \\{(-?0x\[0-9a-f\]+\[.\]\[0-9a-f\]+p\[+-\]\[0-9\]+, ){3}-?0x\[0-9a-f\]+\[.\]\[0-9a-f\]+p\[+-\]\[0-9\]+\\}.*" \
+        "check hex float contents of %xmm$r"
+    gdb_test "print/x \$xmm$r.v2_double" \
+        ".. = \\{-?0x\[0-9a-f\]+\[.\]\[0-9a-f\]+p\[+-\]\[0-9\]+, -?0x\[0-9a-f\]+\[.\]\[0-9a-f\]+p\[+-\]\[0-9\]+\\}.*" \
+        "check hex double contents of %xmm$r"
 }
 
 foreach r {0 1 2 3 4 5 6 7} {
Index: gdb/testsuite/gdb.base/gdb1821.exp
===================================================================
RCS file: /cvs/src/src/gdb/testsuite/gdb.base/gdb1821.exp,v
retrieving revision 1.2
diff -u -p -r1.2 gdb1821.exp
--- gdb/testsuite/gdb.base/gdb1821.exp	9 Jan 2007 17:59:11 -0000	1.2
+++ gdb/testsuite/gdb.base/gdb1821.exp	18 Feb 2007 13:04:13 -0000
@@ -39,5 +39,4 @@ if ![runto_main] then {
     perror "couldn't run to breakpoint"
     continue
 } 
-gdb_test "print /x bar" "{x__0 = 0x0, y__0 = 0x0, z__1 = 0x0}" 
-
+gdb_test "print /x bar" "{x__0 = 0x0.0+p\\+0, y__0 = 0x0.0+p\\+0, z__1 = 0x0.0+p\\+0}"
Index: gdb/configure
===================================================================
RCS file: /cvs/src/src/gdb/configure,v
retrieving revision 1.221
diff -u -p -r1.221 configure
--- gdb/configure	2 Feb 2007 22:55:54 -0000	1.221
+++ gdb/configure	18 Feb 2007 12:52:56 -0000
@@ -20858,6 +20858,67 @@ echo "${ECHO_T}$gdb_cv_bigtoc" >&6
   ;;
 esac
 
+# Check if the compiler and runtime support printing doubles in hex.
+
+echo "$as_me:$LINENO: checking for hex double support in printf" >&5
+echo $ECHO_N "checking for hex double support in printf... $ECHO_C" >&6
+if test "${gdb_cv_printf_has_hex_double+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test "$cross_compiling" = yes; then
+  gdb_cv_printf_has_hex_double=no
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+int
+main ()
+{
+char buf[16];
+  double f = 3.141592653;
+  sprintf (buf, "%a", f);
+  return (strncmp ("0x1.921fb", buf, 9));
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  gdb_cv_printf_has_hex_double=yes
+else
+  echo "$as_me: program exited with status $ac_status" >&5
+echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+( exit $ac_status )
+gdb_cv_printf_has_hex_double=no
+fi
+rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+fi
+echo "$as_me:$LINENO: result: $gdb_cv_printf_has_hex_double" >&5
+echo "${ECHO_T}$gdb_cv_printf_has_hex_double" >&6
+if test $gdb_cv_printf_has_hex_double = yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define PRINTF_HAS_HEX_DOUBLE 1
+_ACEOF
+
+fi
+
 
 
 if test ${build} = ${host} -a ${host} = ${target} ; then
Index: gdb/config.in
===================================================================
RCS file: /cvs/src/src/gdb/config.in,v
retrieving revision 1.88
diff -u -p -r1.88 config.in
--- gdb/config.in	2 Feb 2007 22:54:46 -0000	1.88
+++ gdb/config.in	18 Feb 2007 04:35:33 -0000
@@ -497,6 +497,9 @@
 /* Define if the prfpregset_t type is broken. */
 #undef PRFPREGSET_T_BROKEN
 
+/* Define to 1 if the "%a" format works to print doubles in hex. */
+#undef PRINTF_HAS_HEX_DOUBLE
+
 /* Define to 1 if the "%Lg" format works to print long doubles. */
 #undef PRINTF_HAS_LONG_DOUBLE
 
Index: gdb/doc/gdb.texinfo
===================================================================
RCS file: /cvs/src/src/gdb/doc/gdb.texinfo,v
retrieving revision 1.387
diff -u -p -r1.387 gdb.texinfo
--- gdb/doc/gdb.texinfo	18 Feb 2007 08:04:35 -0000	1.387
+++ gdb/doc/gdb.texinfo	23 Feb 2007 15:59:22 -0000
@@ -5744,7 +5744,8 @@ letters supported are:
 @table @code
 @item x
 Regard the bits of the value as an integer, and print the integer in
-hexadecimal.
+hexadecimal.  Floating point numbers get printed in floating point hexadecimal
+format.
 
 @item d
 Print as integer in signed decimal.
@@ -5798,6 +5799,22 @@ p/x $pc
 Note that no space is required before the slash; this is because command
 names in @value{GDBN} cannot contain a slash.
 
+Structured content gets printed recursively, applying the specified format to
+each of its elements as during
+
+@smallexample
+struct s
+  {
+    int i;
+    float fl;
+    double dbl;
+  };
+(gdb) p s
+$1 = {i = 4, fl = 5.5999999, dbl = 7.7999999999999998}
+(gdb) p/x s
+$2 = {i = 0x4, fl = 0x1.666666000p+2, dbl = 0x1.f3333333333330000p+2}
+@end smallexample
+
 To reprint the last value in the value history with a different format,
 you can use the @code{print} command with just a format and no
 expression.  For example, @samp{p/x} reprints the last value in hex.

  parent reply	other threads:[~2007-02-23 16:15 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2007-02-18 15:37 Jan Kratochvil
2007-02-18 15:46 ` Andreas Schwab
2007-02-18 16:09   ` Daniel Jacobowitz
2007-02-18 16:34     ` Andreas Schwab
2007-02-18 20:26 ` Eli Zaretskii
2007-02-18 20:34   ` Daniel Jacobowitz
2007-02-18 22:32     ` Eli Zaretskii
2007-02-23 16:15   ` Jan Kratochvil [this message]
2007-02-23 18:18     ` Eli Zaretskii

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20070223161453.GA479@host0.dyn.jankratochvil.net \
    --to=jan.kratochvil@redhat.com \
    --cc=gdb-patches@sourceware.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox