public inbox for gentoo-user@lists.gentoo.org
 help / color / mirror / Atom feed
From: Alan Mackenzie <acm@muc.de>
To: gentoo-user@lists.gentoo.org
Subject: Re: [gentoo-user] Console scrollback
Date: Fri, 2 Apr 2021 20:49:34 +0000	[thread overview]
Message-ID: <YGeDXlb6eSgb7IW1@ACM> (raw)
In-Reply-To: <20210402193204.749098246B59@turkos.aspodata.se>

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

Hello, Karl.

Thanks for the reply.

On Fri, Apr 02, 2021 at 21:32:04 +0200, karl@aspodata.se wrote:
> Alan Mackenzie:
> ...
> > I've now cobbled together a working console scroll on Linux
> > 5.4.80-gentoo-r1.  In the end, I reused much of the old machinery which
> > was still present in 4.19.97.  Once I've tidied it up, I hope that the
> > resulting patch file will apply cleanly also to later versions than
> > 5.4.80-r1.
> ...

> Nice, where is the patch so I can try ?

I've attached a patch to this post.  To apply it, the following seems to
work from the top directory of a kernel source tree, e.g. 5.4.80-r1:

    $ patch -p0 < diff.20210402.diff

..  Then you'll need to run make menuconfig (or whatever), going down to
Device Drivers/Graphic support/Console display driver support and there
accepting the defaults for the new config variables.  (This is assuming
you've got FRAMEBUFFER support enabled in a neighbouring page.)

Then build the kernel as normal, and put the new version into whatever
boot loader you use.  It should (??) run, with a scrollback buffer on
each virtual terminal.

Just a word about the current state of the source - it is rough, with
things unfinished.  The "word" STOUGH (pronounced "stuff") is just a
word I use which appears nowhere else and enables me to find changes
quickly and unambiguously.

As I said, I'm not finished with the changes, and swapping from and back
to a scrolled tty isn't completely satisfactory.  Nevertheless, I hope
it works for you and you have fun with it.

> > (i) The scrolling doesn't work on /dev/tty1 aka the console.
> > (ii) When, e.g., /dev/tty6 has been scrolled upwards a bit, and then
> >   <Alt><F2> pressed to go to /dev/tty2, on returning to /dev/tty6, the
> >   scrolling has been cancelled and the cursor is no longer visible.
> >   However, the scrollback buffer is still present.
> > 
> > I think I'm fairly likely to be able to solve (ii).  However, (i), the
> > problem with /dev/tty1, has me baffled.  I don't know where to start
> > looking for the problem.  If anybody with some kernel knowledge could
> > make any suggestions, I'd be very grateful.

> What happens if you set /dev/console to be /dev/ttyS0, i.e. make sure 
> that tty1 is the only one using the first virtual console.

I will try than.  Thanks!

> Regards,
> /Karl Hammar

-- 
Alan Mackenzie (Nuremberg, Germany).


[-- Attachment #2: diff.20210402.diff --]
[-- Type: text/plain, Size: 15009 bytes --]

--- drivers/video/console/Kconfig.orig	2021-03-31 19:14:48.186140856 +0000
+++ drivers/video/console/Kconfig	2021-03-31 12:39:08.090301096 +0000
@@ -79,6 +79,52 @@
 	help
 	  Low-level framebuffer-based console driver.
 
+config FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+       bool "Enable Scrollback Buffer in System RAM"
+       depends on FB=y && FRAMEBUFFER_CONSOLE
+       default y
+       help
+         This option creates scrollback buffers for each framebuffer console,
+         or one buffer for them all.  These buffers are allocated dynamically
+         during initialisation.
+
+	 If you want this feature, say 'Y' here and enter the amount of
+	 RAM to allocate for this buffer.  If unsure, say 'N'.
+
+config FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_SIZE
+       int "Scrollback Buffer Size (in KB)"
+       depends on FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+       range 1 1024
+       default "128"
+       help
+       	  Enter the amount of System RAM to allocate for scrollback buffers of
+	  framebuffer consoles.  Each character position on the video takes 2
+	  bytes of storage.  128k will give you approximately 4 240x67
+	  screenfuls of scrollback buffer.
+
+config FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_PERSISTENT_ENABLE_BY_DEFAULT
+	bool "Persistent Scrollback History for each framebuffer console by default"
+	depends on FB=y && FRAMEBUFFER_CONSOLE && FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+	default y
+	help
+        
+	  Say Y here if the scrollback history should persist by default when
+	  switching between consoles. Otherwise, the scrollback history will
+	  be flushed the first time a scroll-up operation occurs on the new
+	  console after the console is switched. STOUGH!!!  FIXME!!! This
+	  feature can also be enabled using the boot command line parameter
+	  'vgacon.scrollback_persistent=1'.
+
+	  This feature might break your tool of choice to flush the scrollback
+	  buffer, e.g. clear(1) will work fine but Debian's clear_console(1)
+	  will be broken, which might cause security issues.
+	  You can use the escape sequence \e[3J instead if this feature is
+	  activated.
+
+	  Note that a buffer of VGACON_SOFT_SCROLLBACK_SIZE is taken for each
+	  created tty device.
+	  So if you use a RAM-constrained system, say N here.
+
 config FRAMEBUFFER_CONSOLE_DETECT_PRIMARY
        bool "Map the console to the primary display device"
        depends on FRAMEBUFFER_CONSOLE
--- drivers/tty/vt/vt.orig.c	2020-11-28 17:14:38.523649992 +0000
+++ drivers/tty/vt/vt.c	2021-04-02 15:28:53.094607272 +0000
@@ -142,6 +142,13 @@
 #define DEFAULT_BELL_DURATION	(HZ/8)
 #define DEFAULT_CURSOR_BLINK_MS	200
 
+/* NEW STOUGH, 2021-04-01 */
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+static unsigned int console_soft_scrollback_size =
+        1024 * CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_SIZE;
+#endif
+/* END OF NEW STOUGH */
+
 struct vc vc_cons [MAX_NR_CONSOLES];
 
 #ifndef VT_SINGLE_DRIVER
@@ -623,6 +630,205 @@
 	}
 }
 
+/* NEW STOUGH, 2021-03-31 */
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+/* NEW STOUGH, 2021-04-01 */
+static void con_update_softback(struct vc_data *vc)
+{
+	int l = vc->vc_softback_size / vc->vc_size_row;
+
+	if (l > 5)
+		vc->vc_softback_end = vc->vc_softback_buf + l * vc->vc_size_row;
+	else
+		/* Smaller scrollback makes no sense, and 0 would screw
+		   the operation totally */
+		vc->vc_softback_top = 0;
+}
+
+static int concon_set_origin(struct vc_data *vc)
+{
+        if (vc->vc_softback_lines)
+		concon_scrolldelta(vc, vc->vc_softback_lines);
+	return 0;
+}
+/* END OF NEW STOUGH */
+
+#define advance_row(p, delta) (unsigned short *)((unsigned long)(p) + (delta) * vc->vc_size_row)
+
+static void con_redraw_softback(struct vc_data *vc, /* struct display *p, */
+                                long delta)
+{
+	int count = vc->vc_rows;
+	unsigned short *d, *s;
+	unsigned long n;
+	int line = 0;
+
+	d = (u16 *) vc->vc_softback_curr;
+	if (d == (u16 *) vc->vc_softback_in)
+		d = (u16 *) vc->vc_origin;
+	n = vc->vc_softback_curr + delta * vc->vc_size_row;
+	vc->vc_softback_lines -= delta;
+	if (delta < 0) {
+                if (vc->vc_softback_curr < vc->vc_softback_top
+                    && n < vc->vc_softback_buf) {
+			n += vc->vc_softback_end - vc->vc_softback_buf;
+			if (n < vc->vc_softback_top) {
+				vc->vc_softback_lines -=
+				    (vc->vc_softback_top - n) / vc->vc_size_row;
+				n = vc->vc_softback_top;
+			}
+		} else if (vc->vc_softback_curr >= vc->vc_softback_top
+			   && n < vc->vc_softback_top) {
+			vc->vc_softback_lines -=
+			    (vc->vc_softback_top - n) / vc->vc_size_row;
+			n = vc->vc_softback_top;
+		}
+	} else {
+		if (vc->vc_softback_curr > vc->vc_softback_in
+                    && n >= vc->vc_softback_end) {
+			n += vc->vc_softback_buf - vc->vc_softback_end;
+			if (n > vc->vc_softback_in) {
+				n = vc->vc_softback_in;
+				vc->vc_softback_lines = 0;
+			}
+		} else if (vc->vc_softback_curr <= vc->vc_softback_in
+                           && n > vc->vc_softback_in) {
+			n = vc->vc_softback_in;
+			vc->vc_softback_lines = 0;
+		}
+	}
+	if (n == vc->vc_softback_curr)
+		return;
+	vc->vc_softback_curr = n;
+	s = (u16 *) vc->vc_softback_curr;
+	if (s == (u16 *) vc->vc_softback_in)
+		s = (u16 *) vc->vc_origin;
+	while (count--) {
+		unsigned short *start;
+		unsigned short *le;
+		unsigned short c;
+		int x = 0;
+		unsigned short attr = 1;
+
+		start = s;
+		le = advance_row(s, 1);
+		do {
+			c = scr_readw(s);
+			if (attr != (c & 0xff00)) {
+				attr = c & 0xff00;
+				if (s > start) {
+					vc->vc_sw->con_putcs(
+                                                vc, start, s - start,
+                                                line, x);
+					x += s - start;
+					start = s;
+				}
+			}
+			if (c == scr_readw(d)) {
+				if (s > start) {
+					vc->vc_sw->con_putcs(
+                                                vc, start, s - start,
+                                                line, x);
+					x += s - start + 1;
+					start = s + 1;
+				} else {
+					x++;
+					start++;
+				}
+			}
+			s++;
+			d++;
+		} while (s < le);
+		if (s > start)
+			vc->vc_sw->con_putcs(vc, start, s - start, line, x);
+		line++;
+		if (d == (u16 *) vc->vc_softback_end)
+			d = (u16 *) vc->vc_softback_buf;
+		if (d == (u16 *) vc->vc_softback_in)
+			d = (u16 *) vc->vc_origin;
+		if (s == (u16 *) vc->vc_softback_end)
+			s = (u16 *) vc->vc_softback_buf;
+		if (s == (u16 *) vc->vc_softback_in)
+			s = (u16 *) vc->vc_origin;
+	}
+}
+/* END OF NEW STOUGH */
+
+/* NEW STOUGH, 2021-04-01 */
+static inline void con_softback_note(struct vc_data *vc, int t,
+                                     int count)
+{
+	unsigned short *p;
+
+	if (vc->vc_num != fg_console)
+		return;
+	p = (unsigned short *) (vc->vc_origin + t * vc->vc_size_row);
+
+	while (count) {
+		scr_memcpyw((u16 *) vc->vc_softback_in, p, vc->vc_size_row);
+		count--;
+		p = advance_row(p, 1);
+		vc->vc_softback_in += vc->vc_size_row;
+		if (vc->vc_softback_in == vc->vc_softback_end)
+			vc->vc_softback_in = vc->vc_softback_buf;
+		if (vc->vc_softback_in == vc->vc_softback_top) {
+			vc->vc_softback_top += vc->vc_size_row;
+			if (vc->vc_softback_top == vc->vc_softback_end)
+				vc->vc_softback_top = vc->vc_softback_buf;
+		}
+	}
+	vc->vc_softback_curr = vc->vc_softback_in;
+}
+
+void concon_scrolldelta(struct vc_data *vc, int lines)
+{
+	/* struct display *disp = &fb_display[fg_console]; */
+	/* int offset, limit, scrollback_old; */
+
+	if (vc->vc_softback_top) {
+		if (vc->vc_num != fg_console)
+			return;
+		if (vc->vc_mode != KD_TEXT || !lines)
+			return;
+#if 0
+		if (logo_shown >= 0) {
+			struct vc_data *conp2 = vc_cons[logo_shown].d;
+
+			if (conp2->vc_top == logo_lines
+			    && conp2->vc_bottom == conp2->vc_rows)
+				conp2->vc_top = 0;
+			if (logo_shown == vc->vc_num) {
+				unsigned long p, q;
+				int i;
+
+				p = vc->vc_softback_in;
+				q = vc->vc_origin +
+				    logo_lines * vc->vc_size_row;
+				for (i = 0; i < logo_lines; i++) {
+					if (p == vc->vc_softback_top)
+						break;
+					if (p == vc->vc_softback_buf)
+						p = vc->vc_softback_end;
+					p -= vc->vc_size_row;
+					q -= vc->vc_size_row;
+					scr_memcpyw((u16 *) q, (u16 *) p,
+						    vc->vc_size_row);
+				}
+				vc->vc_softback_in = vc->vc_softback_curr = p;
+				update_region(vc, vc->vc_origin,
+					      logo_lines * vc->vc_cols);
+			}
+			logo_shown = FBCON_LOGO_CANSHOW;
+		}
+#endif
+		vc->vc_sw->con_cursor(vc, CM_ERASE /* | CM_SOFTBACK */);
+		con_redraw_softback(vc, /* disp, */ lines);
+		vc->vc_sw->con_cursor(vc, CM_DRAW /* | CM_SOFTBACK */);
+	}
+}
+
+/* END OF NEW STOUGH */
+#endif  /* CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK */
 
 static void con_scroll(struct vc_data *vc, unsigned int t, unsigned int b,
 		enum con_scroll dir, unsigned int nr)
@@ -633,6 +839,12 @@
 		nr = b - t - 1;
 	if (b > vc->vc_rows || t >= b || nr < 1)
 		return;
+        /* NEW STOUGH, 2021-04-01 */
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+        if (dir == SM_UP && vc->vc_softback_top)
+                con_softback_note (vc, t, nr);
+#endif
+        /* END OF NEW STOUGH */
 	vc_uniscr_scroll(vc, t, b, dir, nr);
 	if (con_is_visible(vc) && vc->vc_sw->con_scroll(vc, t, b, dir, nr))
 		return;
@@ -900,6 +1112,10 @@
 		clear_selection();
 
 	vc->vc_sw->con_cursor(vc, CM_ERASE);
+        /* NEW STOUGH, 2021-04-01 */
+        /* if (vc->vc_softback_lines) */
+        /*         concon_set_origin (vc); */
+        /* END OF NEW STOUGH */
 	hide_softcursor(vc);
 }
 
@@ -912,7 +1128,15 @@
 			clear_selection();
 		add_softcursor(vc);
 		if ((vc->vc_cursor_type & 0x0f) != 1)
-			vc->vc_sw->con_cursor(vc, CM_DRAW);
+			/* NEW STOUGH, 2021-04-01 */
+                {
+                        /* if (vc->vc_softback_lines) */
+                        /*         concon_set_origin (vc); */
+                        /* END OF NEW STOUGH */
+                        vc->vc_sw->con_cursor(vc, CM_DRAW);
+                        /* NEW STOUGH, 2021-04-01 */
+                }
+                /* END OF NEW STOUGH */
 	} else
 		hide_cursor(vc);
 }
@@ -922,8 +1146,19 @@
 	WARN_CONSOLE_UNLOCKED();
 
 	if (!con_is_visible(vc) ||
-	    !vc->vc_sw->con_set_origin ||
-	    !vc->vc_sw->con_set_origin(vc))
+	    /* NEW STOUGH, 2021-04-01 */
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+            (
+             !concon_set_origin (vc) &&
+             (
+#endif
+            /* END OF NEW STOUGH */
+            !vc->vc_sw->con_set_origin ||
+	    !vc->vc_sw->con_set_origin(vc)
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+                     ))
+#endif
+                                          )
 		vc->vc_origin = (unsigned long)vc->vc_screenbuf;
 	vc->vc_visible_origin = vc->vc_origin;
 	vc->vc_scr_end = vc->vc_origin + vc->vc_screenbuf_size;
@@ -998,7 +1233,9 @@
 		hide_cursor(old_vc);
 		if (!con_is_visible(old_vc)) {
 			save_screen(old_vc);
-			set_origin(old_vc);
+			/* OLD STOUGH, 2021-04-02 */
+                        /* set_origin(old_vc); */
+                        /* END OF OLD STOUGH */
 		}
 		if (tty0dev)
 			sysfs_notify(&tty0dev->kobj, NULL, "active");
@@ -1011,7 +1248,9 @@
 		int update;
 		int old_was_color = vc->vc_can_do_color;
 
-		set_origin(vc);
+		/* OLD STOUGH, 2021-04-02 */
+                /* set_origin(vc); */
+                /* END OF OLD STOUGH */
 		update = vc->vc_sw->con_switch(vc);
 		set_palette(vc);
 		/*
@@ -1152,6 +1391,20 @@
 	vcs_make_sysfs(currcons);
 	atomic_notifier_call_chain(&vt_notifier_list, VT_ALLOCATE, &param);
 
+        /* NEW STOUGH, 2021-04-01 */
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+        vc->vc_softback_size = console_soft_scrollback_size;
+        err = -ENOMEM;
+        vc->vc_softback_buf =
+                (unsigned long)kzalloc(vc->vc_softback_size, GFP_KERNEL);
+        if (!vc->vc_softback_buf)
+                goto err_free;
+        vc->vc_softback_in = vc->vc_softback_top = vc->vc_softback_curr =
+                vc->vc_softback_buf;
+        vc->vc_softback_lines = 0;
+        con_update_softback(vc);
+#endif
+        /* END OF NEW STOUGH */
 	return 0;
 err_free:
 	visual_deinit(vc);
--- include/linux/console_struct.h.orig	2021-04-02 19:55:52.696177657 +0000
+++ include/linux/console_struct.h	2021-04-02 14:30:20.792800814 +0000
@@ -70,6 +70,18 @@
 	unsigned short	*vc_screenbuf;		/* In-memory character/attribute buffer */
 	unsigned int	vc_screenbuf_size;
 	unsigned char	vc_mode;		/* KD_TEXT, ... */
+        /* NEW STOUGH, 2021-03-31 */
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+        unsigned int    vc_softback_size;       /* Size in bytes of scrollback buffer. */
+        unsigned long   vc_softback_buf;        /* Address of scrollback buffer. */
+        unsigned long   vc_softback_end;        /* (Just past) end of buffer. */
+        unsigned long   vc_softback_in;         /* Head pointer into circular buffer. */
+        unsigned long   vc_softback_top;        /* Tail pointer into circular buffer. */
+        unsigned long   vc_softback_curr;       /* Pos in vc_screenbuf or vc_softback_buf
+                                                   corresponding to visible screen. */
+        int             vc_softback_lines;      /* Number of lines currently scrolled. */
+#endif
+        /* END OF NEW STOUGH */
 	/* attributes for all characters on screen */
 	unsigned char	vc_attr;		/* Current attributes */
 	unsigned char	vc_def_color;		/* Default colors */
--- drivers/video/fbdev/core/fbcon.c.orig	2021-04-02 19:58:46.332168089 +0000
+++ drivers/video/fbdev/core/fbcon.c	2021-04-02 00:11:10.090444550 +0000
@@ -1337,6 +1337,11 @@
 	else
 		fbcon_add_cursor_timer(info);
 
+        /* NEW STOUGH, 2021-04-01 */
+        if (vc->vc_softback_lines)
+                mode = CM_ERASE;
+        /* END OF NEW STOUGH */
+        
 	ops->cursor_flash = (mode == CM_ERASE) ? 0 : 1;
 
 	ops->cursor(vc, info, mode, get_color(vc, info, c, 1),
@@ -3115,6 +3120,11 @@
 	.con_font_default	= fbcon_set_def_font,
 	.con_font_copy 		= fbcon_copy_font,
 	.con_set_palette 	= fbcon_set_palette,
+        /* NEW STOUGH, 2021-04-01 */
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+        .con_scrolldelta        = concon_scrolldelta,
+#endif
+        /* END OF NEW STOUGH */
 	.con_set_origin 	= fbcon_set_origin,
 	.con_invert_region 	= fbcon_invert_region,
 	.con_screen_pos 	= fbcon_screen_pos,
--- include/linux/vt_kern.h.orig	2021-04-02 20:01:19.552159645 +0000
+++ include/linux/vt_kern.h	2021-04-01 17:31:45.125471834 +0000
@@ -129,6 +129,11 @@
 /* vt.c */
 void vt_event_post(unsigned int event, unsigned int old, unsigned int new);
 int vt_waitactive(int n);
+/* NEW STOUGH, 2021-04-01 */
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+void concon_scrolldelta(struct vc_data *vc, int lines);
+#endif
+/* END OF NEW STOUGH */
 void change_console(struct vc_data *new_vc);
 void reset_vc(struct vc_data *vc);
 extern int do_unbind_con_driver(const struct consw *csw, int first, int last,

      reply	other threads:[~2021-04-02 20:49 UTC|newest]

Thread overview: 34+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-01-13 10:30 [gentoo-user] Console scrollback Peter Humphrey
2021-01-13 10:38 ` Michael
2021-01-13 10:55   ` Peter Humphrey
2021-01-13 11:21     ` Andreas Fink
2021-01-13 11:46       ` Peter Humphrey
2021-01-13 18:14 ` Alan Mackenzie
2021-01-13 18:44   ` Neil Bothwick
2021-01-14 16:00     ` Alan Mackenzie
2021-01-13 19:14   ` Peter Humphrey
2021-01-13 19:32   ` Grant Taylor
2021-01-13 21:56     ` Alan Mackenzie
2021-01-13 22:14       ` Grant Taylor
2021-01-14 16:39         ` karl
2021-01-13 22:15       ` [gentoo-user] " Grant Edwards
2021-01-13 23:01         ` Neil Bothwick
2021-01-13 23:06           ` Grant Edwards
2021-01-14  0:00             ` Grant Taylor
2021-01-14  1:25               ` Grant Edwards
2021-01-14 17:10                 ` Grant Taylor
2021-01-14  0:12             ` Neil Bothwick
2021-01-14 16:06         ` Alan Mackenzie
2021-01-16 12:01           ` Alan Mackenzie
2021-01-17  5:17             ` [gentoo-user] " Thomas Mueller
     [not found]             ` <20210117051748.E3239E0869@pigeon.gentoo.org>
2021-01-17  9:16               ` Philip Webb
     [not found]             ` <20210117051748.B8178E0863@pigeon.gentoo.org>
2021-01-19 17:09               ` Laurence Perkins
2021-01-20 19:59                 ` Alan Mackenzie
2021-01-21  9:32                   ` Wols Lists
2021-01-21  8:58                     ` Jorge Almeida
2021-01-21 12:40                       ` Remco Rijnders
2021-01-21 13:00                         ` Jorge Almeida
2021-01-25 12:42                     ` Alan Mackenzie
2021-04-02 18:30                   ` Alan Mackenzie
2021-04-02 19:32                     ` karl
2021-04-02 20:49                       ` Alan Mackenzie [this message]

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=YGeDXlb6eSgb7IW1@ACM \
    --to=acm@muc.de \
    --cc=gentoo-user@lists.gentoo.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