Re: git_getpass regression?

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Fri, Jun 29, 2012 at 10:30 PM, Jeff King <peff@xxxxxxxx> wrote:
> On Fri, Jun 29, 2012 at 10:14:35PM +0200, Erik Faye-Lund wrote:
>
>> > Backspace shouldn't be making it to git at all; it should be handled at
>> > the terminal input layer, because we are not putting the terminal into
>> > raw mode.
>>
>> I don't get it. How can the terminal ever interpret the backspace, when
>> we've already put the character it's supposed to erase in a strbuf? Sure,
>> the echo can be correctly dealt with (probably with the exception of the
>> border case of erasing characters from the prompt text), but once we've put
>> the characters into a buffer, the terminal cannot erase it.
>
> Because a terminal in non-raw mode is typically line-oriented, and your
> program doesn't get to see any input at all until the user hits enter.
>
> Try this on your linux box:
>
>  strace -e read cat
>
> You should be able to type characters and backspace erase them, all
> without seeing any activity from strace. When you hit enter, you should
> see the full text you typed read as a single syscall.
>

Thanks for the explanation.

> If the backspacing doesn't work, then there is a configuration mismatch
> between what linux's tty driver expects and what your terminal emulator
> is sending (the former is probably expecting ^? as the erase character,
> and the latter is sending ^H).
>

The only thing I actually tested, was that getpass behaved correctly.
I just assumed fgetc returned the input right away. I had no idea that
fgetc didn't fetch characters until a whole line was entered. I guess
you learn something every day ;)

I have now written a small test-application that confirms that the
prompt works just fine; I was making conclusions without testing
properly, my bad. So I'm sorry for the noise. But please read on ;)

>> > If that is the case, I wonder if your 'stty erase' settings
>> > do not match what your terminal emulator is sending.
>>
>> I have no idea what that even means. And having to fiddle around with
>> terminal settings just makes git feel cheap.
>
> But it's not git that is broken. It's your configuration. Fixing git is
> a band-aid, and other programs will still be broken. Of course, I may be
> mis-diagnosing your problem. Did you try this:
>
>> > If you run "stty erase ^H" and then run git, does it work?
>
> ?
>

I'm sorry, I didn't have access to a box at that time, that's why I
didn't answer that part.

But now, I see that "stty erase ^H" makes the terminal behave all
strange when it comes to the backspace character; it starts printing
"^?" instead.

>> >  I think (3) is the only sane thing,
>> > though.

Yes, you are right. But perhaps with one exception: we probably want
to piggy-back on those terminal-handling goodies on Windows. If only
to get git to behave more consistently with other applications.

So perhaps we should do something like this?

---8<---
diff --git a/compat/terminal.c b/compat/terminal.c
index 53c5166..2f44fb5 100644
--- a/compat/terminal.c
+++ b/compat/terminal.c
@@ -76,18 +76,58 @@ char *git_terminal_prompt(const char *prompt, int echo)
 char *git_terminal_prompt(const char *prompt, int echo)
 {
 	static struct strbuf buf = STRBUF_INIT;
+	int r;
+	FILE *input_fh, *output_fh;
+	DWORD cmode = 0;
+	HANDLE hconin;
+
+	input_fh = fopen("CONIN$", "r");
+	if (!input_fh)
+		return NULL;
+
+	output_fh = fopen("CONOUT$", "w");
+	if (!output_fh) {
+		fclose(input_fh);
+		return NULL;
+	}

-	fputs(prompt, stderr);
-	strbuf_reset(&buf);
-	for (;;) {
-		int c = _getch();
-		if (c == '\n' || c == '\r')
-			break;
-		if (echo)
-			putc(c, stderr);
-		strbuf_addch(&buf, c);
+	if (!echo) {
+		hconin = CreateFile("CONIN$", GENERIC_READ | GENERIC_WRITE,
+		    FILE_SHARE_READ, NULL, OPEN_EXISTING,
+		    FILE_ATTRIBUTE_NORMAL, NULL);
+		if (hconin == INVALID_HANDLE_VALUE) {
+			fclose(input_fh);
+			fclose(output_fh);
+			return NULL;
+		}
+		GetConsoleMode(hconin, &cmode);
+		if (!SetConsoleMode(hconin, cmode & (~ENABLE_ECHO_INPUT))) {
+			fclose(input_fh);
+			fclose(output_fh);
+			return NULL;
+		}
 	}
-	putc('\n', stderr);
+
+	fputs(prompt, output_fh);
+	fflush(output_fh);
+
+	r = strbuf_getline(&buf, input_fh, '\n');
+	if (!echo) {
+		putc('\n', output_fh);
+		fflush(output_fh);
+
+		SetConsoleMode(hconin, cmode);
+		CloseHandle(hconin);
+	}
+
+	if (buf.buf[buf.len - 1] == '\r')
+		strbuf_setlen(&buf, buf.len - 1);
+
+	fclose(input_fh);
+	fclose(output_fh);
+
+	if (r == EOF)
+		return NULL;
 	return buf.buf;
 }

---8<---

Notice how this looks very similar to the HAVE_TTY code-path, with the
exception of needing two file handles instead of one, and the actual
enabling/resetting of the prompt-setting. So the code-paths can
probably be refactored to share code...

>> As I already said, I don't think this is the case. Inserting 0x7f in a
>> strbuf does not delete the preceding char...
>
> Of course not. Control characters should be handled by the terminal
> driver, and shouldn't make it to git at all. Backspacing works perfectly
> well on correctly configured systems, and it should not be git's
> responsibility to care about it at all for line-oriented input (and if
> we _did_ want to handle it ourselves, we should use a real terminal
> library like readline or curses).

Yes, I absolutely agree. Sorry for getting confused and wasting your
time with unfounded accusations.

But perhaps something good came from it; the Windows-prompt doesn't
support erasing until the patch above is applied. I don't know if you
care or not, but I certainly do ;)
--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Kernel Development]     [Gcc Help]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [V4L]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Fedora Users]