threads / patch / 41462

v2, 6 partsbisect: read bisect paths with strbuf_getline()

Subject: [PATCH v2 2/6] bisect: read bisect paths with strbuf_getline()

## tl;dr

14 messages between Feb 22, 2016 and Feb 22, 2016. Diffs are folded; open one to read it.

replies: 13people: 3as markdown or json

Moritz Neeb· Feb 22, 2016, 01:00 UTC · lore

[PATCH v2 0/6] replacing strbuf_getline_lf() by strbuf_getline() on trimmed input

This series deals with strbuf_getline_lf() in certain codepaths: Those, where the input that is read, is/was trimmed before doing anything that could possibly expect a CR character. Those places can be assumed to be "text" input, where a CR never would be a meaningful control character.

The purpose of this series is to document these places to have this property, by using strbuf_getline() instead of strbuf_getline_lf(). Also in some codepaths, the CR could be a leftover of an editor and is thus removed.

Every codepath was examined, if after the change it is still necessary to have trimming or if the additional CRLR-removal suffices.

The series is an idea out of [1], where Junio proposed to replace the calls to strbuf_getline_lf() because it 'would [be] a good way to document them as dealing with "text"'. Changes since v1:

* adapting the behaviour of sq_(de)quote to be a one-to-one transformation
* removing some unneccesary trimming calls in:
    * wt-status.c
    * builting/notes.c
    * builtin/clean.c
    * bisect.c
-Moritz
[1] http://thread.gmane.org/gmane.comp.version-control.git/284104
Moritz Neeb (6):
  quote: remove leading space in sq_dequote_step
  bisect: read bisect paths with strbuf_getline()
  clean: read user input with strbuf_getline()
  notes: read copied notes with strbuf_getline()
  remote: read $GIT_DIR/branches/* with strbuf_getline()
  wt-status: read rebase todolist with strbuf_getline()
 bisect.c        |  5 ++---
 builtin/clean.c | 12 +++---------
 builtin/notes.c |  3 +--
 quote.c         |  2 ++
 remote.c        |  2 +-
 wt-status.c     |  3 +--
 6 files changed, 10 insertions(+), 17 deletions(-)
-- 
2.7.1.345.gc14003e
Moritz Neeb· Feb 22, 2016, 01:15 UTC · re: Moritz Neeb · lore

[PATCH v2 1/6] quote: remove leading space in sq_dequote_step

Because sq_quote_argv adds a leading space (which is expected in trace.c), sq_dequote_step should remove this space again, such that the operations of quoting and dequoting are inverse of each other.

This patch is preparing the way to remove some excessive trimming operation in bisect in the following commit.

Signed-off-by: Moritz Neeb <lists@moritzneeb.de>
---
 quote.c | 2 ++
 1 file changed, 2 insertions(+)
Show changes to quote.c +1 −0
diff --git a/quote.c b/quote.c
index fe884d2..2714f27 100644
--- a/quote.c
+++ b/quote.c
@@ -63,6 +63,8 @@ static char *sq_dequote_step(char *arg, char **next)
 	char *src = arg;
 	char c;
 +	if (*src == ' ')
+		src++;
 	if (*src != '\'')
 		return NULL;
 	for (;;) {
-- 
2.7.1.345.gc14003e
Moritz Neeb· Feb 22, 2016, 01:15 UTC · re: Moritz Neeb · lore

The file BISECT_NAMES is written by "git rev-parse --sq-quote" via sq_quote_argv() when starting a bisection. It can contain pathspecs to narrow down the search. When reading it back, it should be expected that sq_dequote_to_argv_array() is able to parse this file. In fact, the previous commit ensures this.

As the content is of type "text", that means there is no logic expecting CR, strbuf_getline_lf() will be replaced by strbuf_getline().

Apart from whitespace added and removed in quote.c, no more whitespaces are expexted. While it is technically possible, we have never advertised this file to be editable by user, or encouraged them to do so, thus the call to strbuf_trim() turns obsolete in various ways.

For the case that this file is modified nonetheless, in an invalid way such that dequoting fails, the error message is broadened to both cases: bad quoting and unexpected whitespace.

Helped-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Moritz Neeb <lists@moritzneeb.de>
---
 bisect.c | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)
Show changes to bisect.c +2 −2
diff --git a/bisect.c b/bisect.c
index 06ec54e..e2df02f 100644
--- a/bisect.c
+++ b/bisect.c
@@ -440,10 +440,9 @@ static void read_bisect_paths(struct argv_array *array)
 	if (!fp)
 		die_errno("Could not open file '%s'", filename);
 -	while (strbuf_getline_lf(&str, fp) != EOF) {
-		strbuf_trim(&str);
+	while (strbuf_getline(&str, fp) != EOF) {
 		if (sq_dequote_to_argv_array(str.buf, array))
-			die("Badly quoted content in file '%s': %s",
+			die("Badly quoted content or unexpected whitespace in file '%s': %s",
 			    filename, str.buf);
 	}
 -- 2.7.1.345.gc14003e
Moritz Neeb· Feb 22, 2016, 01:16 UTC · re: Moritz Neeb · lore

[PATCH v2 4/6] notes: read copied notes with strbuf_getline()

The notes are copied from stdin. They should only contain SHA1s... Not spaces. CR could be there, because the file/the data from stdin could have been written via an editor that adds them.

The notes that are copied from stdin are trimmed with strbuf_rtrim() after splitting by ' '. There is thus no logic expecting CR, so strbuf_getline_lf() can be replaced by its CRLF counterpart.

Signed-off-by: Moritz Neeb <lists@moritzneeb.de>
---
 builtin/notes.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)
Show changes to builtin/notes.c +1 −1
diff --git a/builtin/notes.c b/builtin/notes.c
index ed6f222..706ec11 100644
--- a/builtin/notes.c
+++ b/builtin/notes.c
@@ -290,7 +290,7 @@ static int notes_copy_from_stdin(int force, const char *rewrite_cmd)
 		t = &default_notes_tree;
 	}
 -	while (strbuf_getline_lf(&buf, stdin) != EOF) {
+	while (strbuf_getline(&buf, stdin) != EOF) {
 		unsigned char from_obj[20], to_obj[20];
 		struct strbuf **split;
 		int err;
@@ -299,7 +299,6 @@ static int notes_copy_from_stdin(int force, const char *rewrite_cmd)
 		if (!split[0] || !split[1])
 			die(_("Malformed input line: '%s'."), buf.buf);
 		strbuf_rtrim(split[0]);
-		strbuf_rtrim(split[1]);
 		if (get_sha1(split[0]->buf, from_obj))
 			die(_("Failed to resolve '%s' as a valid ref."), split[0]->buf);
 		if (get_sha1(split[1]->buf, to_obj))
-- 
2.7.1.345.gc14003e
Eric Sunshine· Feb 22, 2016, 02:41 UTC · re: Moritz Neeb · lore

Re: [PATCH v2 4/6] notes: read copied notes with strbuf_getline()

On Sun, Feb 21, 2016 at 8:16 PM, Moritz Neeb <lists@moritzneeb.de> wrote:
Show 24 quoted lines
> The notes are copied from stdin. They should only contain SHA1s... Not
> spaces. CR could be there, because the file/the data from stdin could
> have been written via an editor that adds them.
>
> The notes that are copied from stdin are trimmed with strbuf_rtrim() after
> splitting by ' '. There is thus no logic expecting CR, so strbuf_getline_lf()
> can be replaced by its CRLF counterpart.
>
> Signed-off-by: Moritz Neeb <lists@moritzneeb.de>
> ---
> diff --git a/builtin/notes.c b/builtin/notes.c
> @@ -290,7 +290,7 @@ static int notes_copy_from_stdin(int force, const char *rewrite_cmd)
>                 t = &default_notes_tree;
>         }
>  -      while (strbuf_getline_lf(&buf, stdin) != EOF) {
> +       while (strbuf_getline(&buf, stdin) != EOF) {
>                 unsigned char from_obj[20], to_obj[20];
>                 struct strbuf **split;
>                 int err;
> @@ -299,7 +299,6 @@ static int notes_copy_from_stdin(int force, const char *rewrite_cmd)
>                 if (!split[0] || !split[1])
>                         die(_("Malformed input line: '%s'."), buf.buf);
>                 strbuf_rtrim(split[0]);
> -               strbuf_rtrim(split[1]);

Given the commit message, I understand that this rtrim is effectively redundant, thus can be dropped, however, I'm not sure that doing so improves the code since the reader now has to think extra hard to understand the asymmetry of only trimming split[0] (and that understanding may require blaming this code in order to consult the commit message).

A deeper issue not touched upon by the commit message (but which should be) is that that strbuf_split() leaves the "terminator" (space, in this case) on the component strings, and that is why split[0] must be rtrim'd. Rather than dropping only one of the rtrim's, a cleaner approach might be to convert the code to use string_list_split() which doesn't have the "odd" behavior of leaving the terminator on the split strings, in which case both rtrim's could be retired. This, of course, would be done as a separate preparatory patch.

Show 5 quoted lines
>                 if (get_sha1(split[0]->buf, from_obj))
>                         die(_("Failed to resolve '%s' as a valid ref."), split[0]->buf);
>                 if (get_sha1(split[1]->buf, to_obj))
> --
> 2.7.1.345.gc14003e
Junio C Hamano· Feb 22, 2016, 19:27 UTC · re: Eric Sunshine · lore

Re: [PATCH v2 4/6] notes: read copied notes with strbuf_getline()

Eric Sunshine <sunshine@sunshineco.com> writes:
Show 9 quoted lines
> A deeper issue not touched upon by the commit message (but which
> should be) is that that strbuf_split() leaves the "terminator" (space,
> in this case) on the component strings, and that is why split[0] must
> be rtrim'd. Rather than dropping only one of the rtrim's, a cleaner
> approach might be to convert the code to use string_list_split() which
> doesn't have the "odd" behavior of leaving the terminator on the split
> strings, in which case both rtrim's could be retired.
> This, of course,
> would be done as a separate preparatory patch.
Yeah, this is a good point to raise.
Thanks.
Moritz Neeb· Feb 22, 2016, 01:17 UTC · re: Moritz Neeb · lore

[PATCH v2 6/6] wt-status: read rebase todolist with strbuf_getline()

In read_rebase_todolist() the files $GIT_DIR/rebase-merge/done and $GIT_DIR/rebase-merge/git-rebase-todo are read to collect status information.

The access to this file should always happen via git rebase, e.g. via "git rebase -i" or "git rebase --edit-todo". We can assume, that this interface handles the preprocessing of whitespaces, especially CRLFs correctly. Thus in this codepath we can remove the call to strbuf_trim().

For documenting the input as expecting "text" input, strbuf_getline_lf() is still replaced by strbuf_getline().

Signed-off-by: Moritz Neeb <lists@moritzneeb.de>
---
 wt-status.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)
Show changes to wt-status.c +1 −2
diff --git a/wt-status.c b/wt-status.c
index ab4f80d..8047cf2 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -1076,10 +1076,9 @@ static void read_rebase_todolist(const char *fname, struct string_list *lines)
 	if (!f)
 		die_errno("Could not open file %s for reading",
 			  git_path("%s", fname));
-	while (!strbuf_getline_lf(&line, f)) {
+	while (!strbuf_getline(&line, f)) {
 		if (line.len && line.buf[0] == comment_line_char)
 			continue;
-		strbuf_trim(&line);
 		if (!line.len)
 			continue;
 		abbrev_sha1_in_line(&line);
-- 
2.7.1.345.gc14003e
Junio C Hamano· Feb 22, 2016, 19:30 UTC · re: Moritz Neeb · lore

Re: [PATCH v2 6/6] wt-status: read rebase todolist with strbuf_getline()

Moritz Neeb <lists@moritzneeb.de> writes:
Show 10 quoted lines
> diff --git a/wt-status.c b/wt-status.c
> index ab4f80d..8047cf2 100644
> --- a/wt-status.c
> +++ b/wt-status.c
> @@ -1076,10 +1076,9 @@ static void read_rebase_todolist(const char *fname, struct string_list *lines)
>  	if (!f)
>  		die_errno("Could not open file %s for reading",
>  			  git_path("%s", fname));
> -	while (!strbuf_getline_lf(&line, f)) {
> +	while (!strbuf_getline(&line, f)) {

Not related to the substance of the patch series at all, but all except for this patch in the series seem to be corrupt in that the very first line that is removed in each patch has an extra space before the '-' deletion sign. It is a very curious symptom. Please double check the way you send out patch e-mails (e.g. send them first only to yourself and then try to apply them with "git am").

Thanks.
Moritz Neeb· Feb 22, 2016, 01:20 UTC · re: Moritz Neeb · lore

[PATCH v2 3/6] clean: read user input with strbuf_getline()

The inputs that are read are all answers that are given by the user when interacting with git on the commandline. As these answers are not supposed to contain a meaningful CR it is safe to replace strbuf_getline_lf() can be replaced by strbuf_getline().

Before the user input was trimmed to remove the CR. This would be now redundant. Another effect of the trimming was that some (accidentally) typed spaces were filtered. But here we want to be consistent with similar UIs like interactive adding, which only accepts space-less input.

For the case of filtering by patterns the input is still trimmed in an untouched codepath after it is split up into multiple patterns. This is considered as desirable, because of two reasons: First this fitering is not part of similar UIs and it is way more likely to accidentally type a space in this way of interacting.

Signed-off-by: Moritz Neeb <lists@moritzneeb.de>
---
When playing around with the interactive git clean I noticed that it is
not possible to have a pattern actually containing a space (i.e. escaping it).
Not sure how relevant this is, because I have no feeling how good the support
and demand for smoothly handling space-containing files in git is.
 builtin/clean.c | 12 +++---------
 1 file changed, 3 insertions(+), 9 deletions(-)
Show changes to builtin/clean.c +3 −8
diff --git a/builtin/clean.c b/builtin/clean.c
index 7b08237..01cc2ff 100644
--- a/builtin/clean.c
+++ b/builtin/clean.c
@@ -570,9 +570,7 @@ static int *list_and_choose(struct menu_opts *opts, struct menu_stuff *stuff)
 			       clean_get_color(CLEAN_COLOR_RESET));
 		}
 -		if (strbuf_getline_lf(&choice, stdin) != EOF) {
-			strbuf_trim(&choice);
-		} else {
+		if (strbuf_getline(&choice, stdin) == EOF) {
 			eof = 1;
 			break;
 		}
@@ -652,9 +650,7 @@ static int filter_by_patterns_cmd(void)
 		clean_print_color(CLEAN_COLOR_PROMPT);
 		printf(_("Input ignore patterns>> "));
 		clean_print_color(CLEAN_COLOR_RESET);
-		if (strbuf_getline_lf(&confirm, stdin) != EOF)
-			strbuf_trim(&confirm);
-		else
+		if (strbuf_getline(&confirm, stdin) == EOF)
 			putchar('\n');
  		/* quit filter_by_pattern mode if press ENTER or Ctrl-D */
@@ -750,9 +746,7 @@ static int ask_each_cmd(void)
 			qname = quote_path_relative(item->string, NULL, &buf);
 			/* TRANSLATORS: Make sure to keep [y/N] as is */
 			printf(_("Remove %s [y/N]? "), qname);
-			if (strbuf_getline_lf(&confirm, stdin) != EOF) {
-				strbuf_trim(&confirm);
-			} else {
+			if (strbuf_getline(&confirm, stdin) == EOF) {
 				putchar('\n');
 				eof = 1;
 			}
-- 
2.7.1.345.gc14003e
Eric Sunshine· Feb 22, 2016, 02:27 UTC · re: Moritz Neeb · lore

Re: [PATCH v2 3/6] clean: read user input with strbuf_getline()

On Sun, Feb 21, 2016 at 8:20 PM, Moritz Neeb <lists@moritzneeb.de> wrote:
Show 9 quoted lines
> The inputs that are read are all answers that are given by the user
> when interacting with git on the commandline. As these answers are
> not supposed to contain a meaningful CR it is safe to
> replace strbuf_getline_lf() can be replaced by strbuf_getline().
>
> Before the user input was trimmed to remove the CR. This would be now
> redundant. Another effect of the trimming was that some (accidentally)
> typed spaces were filtered. But here we want to be consistent with similar UIs
> like interactive adding, which only accepts space-less input.

I don't at all insist upon it, but this behavior change feels somewhat like it ought to be in its own commit. I'm also not convinced that making this consistent with the less forgiving behavior of "interactive adding" is desirable (rather the reverse: that that case should be more flexible). However, I wasn't following the discussion with Junio closely, and perhaps missed you two agreeing that this is preferable.

> For the case of filtering by patterns the input is still trimmed in an
> untouched codepath after it is split up into multiple patterns.
> This is considered as desirable, because of two reasons:
s/, because of/ for/
Show 39 quoted lines
> First this fitering is not part of similar UIs and it is way more likely
> to accidentally type a space in this way of interacting.
>
> Signed-off-by: Moritz Neeb <lists@moritzneeb.de>
> ---
> diff --git a/builtin/clean.c b/builtin/clean.c
> @@ -570,9 +570,7 @@ static int *list_and_choose(struct menu_opts *opts, struct menu_stuff *stuff)
>                                clean_get_color(CLEAN_COLOR_RESET));
>                 }
>  -              if (strbuf_getline_lf(&choice, stdin) != EOF) {
> -                       strbuf_trim(&choice);
> -               } else {
> +               if (strbuf_getline(&choice, stdin) == EOF) {
>                         eof = 1;
>                         break;
>                 }
> @@ -652,9 +650,7 @@ static int filter_by_patterns_cmd(void)
>                 clean_print_color(CLEAN_COLOR_PROMPT);
>                 printf(_("Input ignore patterns>> "));
>                 clean_print_color(CLEAN_COLOR_RESET);
> -               if (strbuf_getline_lf(&confirm, stdin) != EOF)
> -                       strbuf_trim(&confirm);
> -               else
> +               if (strbuf_getline(&confirm, stdin) == EOF)
>                         putchar('\n');
>                 /* quit filter_by_pattern mode if press ENTER or Ctrl-D */
> @@ -750,9 +746,7 @@ static int ask_each_cmd(void)
>                         qname = quote_path_relative(item->string, NULL, &buf);
>                         /* TRANSLATORS: Make sure to keep [y/N] as is */
>                         printf(_("Remove %s [y/N]? "), qname);
> -                       if (strbuf_getline_lf(&confirm, stdin) != EOF) {
> -                               strbuf_trim(&confirm);
> -                       } else {
> +                       if (strbuf_getline(&confirm, stdin) == EOF) {
>                                 putchar('\n');
>                                 eof = 1;
>                         }
> --
> 2.7.1.345.gc14003e
Moritz Neeb· Feb 22, 2016, 07:40 UTC · re: Eric Sunshine · lore

Re: [PATCH v2 3/6] clean: read user input with strbuf_getline()

On 02/22/2016 03:27 AM, Eric Sunshine wrote:
Show 13 quoted lines
> On Sun, Feb 21, 2016 at 8:20 PM, Moritz Neeb <lists@moritzneeb.de> wrote:
>> The inputs that are read are all answers that are given by the user
>> when interacting with git on the commandline. As these answers are
>> not supposed to contain a meaningful CR it is safe to
>> replace strbuf_getline_lf() can be replaced by strbuf_getline().
>>
>> Before the user input was trimmed to remove the CR. This would be now
>> redundant. Another effect of the trimming was that some (accidentally)
>> typed spaces were filtered. But here we want to be consistent with similar UIs
>> like interactive adding, which only accepts space-less input.
> 
> I don't at all insist upon it, but this behavior change feels somewhat
> like it ought to be in its own commit.

You're right, two commits would be nicer. I was also thinking about splitting up the three codepaths, but I decided all of the clean-interaction belongs together.

Show 6 quoted lines
> I'm also not convinced that
> making this consistent with the less forgiving behavior of
> "interactive adding" is desirable (rather the reverse: that that case
> should be more flexible). However, I wasn't following the discussion
> with Junio closely, and perhaps missed you two agreeing that this is
> preferable.

To summarize the discussion with Junio: We were not directly talking about that. Two aspects from the whole discussion were that I should decide something and justify a stance (which I did) and that it's also beneficial to think aloud (which I forgot).

In fact, I was surprised, that interactive adding is that strict. I should've added that to the discussion. I am at the moment sometimes unsure whether I find things weird because git standards are different than what I'd expect or because things really should be changed. So I went for the former and decided to go for consistency with the base. I'd expect, from my own behaviour, interactive adding is used by far more than interactive cleaning, which might be an argument to adapt the latter.

But now that we're discussing this, I don't really see a benefit from the user perspective, it's more code cleanup.

Junio C Hamano· Feb 22, 2016, 19:40 UTC · re: Eric Sunshine · lore

Re: [PATCH v2 3/6] clean: read user input with strbuf_getline()

Eric Sunshine <sunshine@sunshineco.com> writes:
Show 18 quoted lines
> On Sun, Feb 21, 2016 at 8:20 PM, Moritz Neeb <lists@moritzneeb.de> wrote:
>> The inputs that are read are all answers that are given by the user
>> when interacting with git on the commandline. As these answers are
>> not supposed to contain a meaningful CR it is safe to
>> replace strbuf_getline_lf() can be replaced by strbuf_getline().
>>
>> Before the user input was trimmed to remove the CR. This would be now
>> redundant. Another effect of the trimming was that some (accidentally)
>> typed spaces were filtered. But here we want to be consistent with similar UIs
>> like interactive adding, which only accepts space-less input.
>
> I don't at all insist upon it, but this behavior change feels somewhat
> like it ought to be in its own commit. I'm also not convinced that
> making this consistent with the less forgiving behavior of
> "interactive adding" is desirable (rather the reverse: that that case
> should be more flexible). However, I wasn't following the discussion
> with Junio closely, and perhaps missed you two agreeing that this is
> preferable.
There was no such discussion ;-)

I am not 100% sure if we want to be lenient in reading "yes, please remove this one", but if we already are loose, I tend to agree that there is not much point tightening it, especially with a clean-up topic like this one.

Thanks.
Moritz Neeb· Feb 22, 2016, 01:22 UTC · re: Moritz Neeb · lore

[PATCH v2 5/6] remote: read $GIT_DIR/branches/* with strbuf_getline()

The line read from the branch file is directly trimmed after reading with strbuf_trim(). There is thus no logic expecting CR, so strbuf_getline_lf() can be replaced by its CRLF counterpart.

Signed-off-by: Moritz Neeb <lists@moritzneeb.de>
---
To be honest, I did not yet fully understand the purpose of this branches/ file.
What I'd expect is that it is some intermediary file while fetching?
Or is it edited directly by the user and thus it's necessary to strip spaces
that could be added accidentally?
 remote.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
Show changes to remote.c +1 −0
diff --git a/remote.c b/remote.c
index 02e698a..aaff6aa 100644
--- a/remote.c
+++ b/remote.c
@@ -281,7 +281,7 @@ static void read_branches_file(struct remote *remote)
 	if (!f)
 		return;
 -	strbuf_getline_lf(&buf, f);
+	strbuf_getline(&buf, f);
 	fclose(f);
 	strbuf_trim(&buf);
 	if (!buf.len) {
-- 
2.7.1.345.gc14003e
Junio C Hamano· Feb 22, 2016, 19:09 UTC · re: Moritz Neeb · lore

Re: [PATCH v2 5/6] remote: read $GIT_DIR/branches/* with strbuf_getline()

Moritz Neeb <lists@moritzneeb.de> writes:
Show 10 quoted lines
> The line read from the branch file is directly trimmed after reading with
> strbuf_trim(). There is thus no logic expecting CR, so strbuf_getline_lf()
> can be replaced by its CRLF counterpart.
>
> Signed-off-by: Moritz Neeb <lists@moritzneeb.de>
> ---
> To be honest, I did not yet fully understand the purpose of this branches/ file.
> What I'd expect is that it is some intermediary file while fetching?
> Or is it edited directly by the user and thus it's necessary to strip spaces
> that could be added accidentally?
[Documentation/gitrepository-layout.txt]
branches::
	A slightly deprecated way to store shorthands to be used
	to specify a URL to 'git fetch', 'git pull' and 'git push'.
	A file can be stored as `branches/<name>` and then
	'name' can be given to these commands in place of
	'repository' argument.  See the REMOTES section in
	linkgit:git-fetch[1] for details.  This mechanism is legacy
	and not likely to be found in modern repositories. This
	directory is ignored if $GIT_COMMON_DIR is set and
	"$GIT_COMMON_DIR/branches" will be used instead.

← back to recent threads