Author: brane
Date: Sun Feb 24 06:19:42 2013
New Revision: 1449442
URL: http://svn.apache.org/r1449442
Log:
Fix issue #4321 (backspace not working at conflict prompt).
Implement simple line editing on Unix when termios is available.
* subversion/libsvn_subr/prompt.c: Include svn_ctype.h and signal.h.
(terminal_open): With termios, always turn off terminal echo.
(echo_control_char): New; Helper function to echo control characters.
(terminal_getc): In termios mode:
- echo input characters, or asterisks when echo is disabled;
- handle deletions and job control;
- echo actual control chars we handled, even if they've been redefined.
Modified:
subversion/trunk/subversion/libsvn_subr/prompt.c
Modified: subversion/trunk/subversion/libsvn_subr/prompt.c
URL:
http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_subr/prompt.c?rev=1449442&r1=1449441&r2=1449442&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_subr/prompt.c (original)
+++ subversion/trunk/subversion/libsvn_subr/prompt.c Sun Feb 24 06:19:42 2013
@@ -32,6 +32,7 @@
#include <apr_portable.h>
#include "svn_cmdline.h"
+#include "svn_ctype.h"
#include "svn_string.h"
#include "svn_auth.h"
#include "svn_error.h"
@@ -43,6 +44,7 @@
#ifdef WIN32
#include <conio.h>
#elif defined(HAVE_TERMIOS_H)
+#include <signal.h>
#include <termios.h>
#endif
@@ -210,8 +212,7 @@ terminal_open(terminal_handle_t **termin
attr.c_lflag &= ~(ISIG | ICANON);
attr.c_cc[VMIN] = 1; /* Read one byte at a time */
attr.c_cc[VTIME] = 0; /* No timeout, wait indefinitely */
- if (noecho)
- attr.c_lflag &= ~(ECHO); /* Turn off echo */
+ attr.c_lflag &= ~(ECHO); /* Turn off echo */
if (0 == tcsetattr((*terminal)->osinfd, TCSAFLUSH, &attr))
{
(*terminal)->noecho = noecho;
@@ -269,6 +270,31 @@ terminal_puts(const char *string, termin
#define TERMINAL_EOL (TERMINAL_NONE + 2) /* end of input/end of line */
#define TERMINAL_EOF (TERMINAL_NONE + 3) /* end of file during input */
+/* Helper for terminal_getc: writes CH to OUTFD as a control char. */
+#ifndef WIN32
+static void
+echo_control_char(char ch, apr_file_t *outfd)
+{
+ if (svn_ctype_iscntrl(ch))
+ {
+ const char substitute = (ch < 32? '@' + ch : '?');
+ apr_file_putc('^', outfd);
+ apr_file_putc(substitute, outfd);
+ }
+ else if (svn_ctype_isprint(ch))
+ {
+ /* Pass printable characters unchanged. */
+ apr_file_putc(ch, outfd);
+ }
+ else
+ {
+ /* Everything else is strange. */
+ apr_file_putc('^', outfd);
+ apr_file_putc('!', outfd);
+ }
+}
+#endif /* WIN32 */
+
/* Read one character or control code from TERMINAL, returning it in CODE.
if CAN_ERASE and the input was a deletion, emit codes to erase the
last character displayed on the terminal.
@@ -277,6 +303,7 @@ static svn_error_t *
terminal_getc(int *code, terminal_handle_t *terminal,
svn_boolean_t can_erase, apr_pool_t *pool)
{
+ const svn_boolean_t echo = !terminal->noecho;
apr_status_t status = APR_SUCCESS;
char ch;
@@ -284,7 +311,6 @@ terminal_getc(int *code, terminal_handle
if (!terminal->infd)
{
/* See terminal_open; we're using Console I/O. */
- const svn_boolean_t echo = !terminal->noecho;
/* The following was hoisted from APR's getpass for Windows. */
int concode = _getch();
@@ -359,18 +385,49 @@ terminal_getc(int *code, terminal_handle
return svn_error_wrap_apr(status, _("Can't read from terminal"));
if (ch == attr->c_cc[VINTR] || ch == attr->c_cc[VQUIT])
- return svn_error_create(SVN_ERR_CANCELLED, NULL, NULL);
+ {
+ /* Break */
+ echo_control_char(ch, terminal->outfd);
+ return svn_error_create(SVN_ERR_CANCELLED, NULL, NULL);
+ }
else if (ch == '\r' || ch == '\n' || ch == attr->c_cc[VEOL])
- *code = TERMINAL_EOL;
- else if (ch == '\b'
- || ch == attr->c_cc[VERASE] || ch == attr->c_cc[VKILL])
- *code = TERMINAL_DEL;
+ {
+ /* Newline */
+ *code = TERMINAL_EOL;
+ apr_file_putc('\n', terminal->outfd);
+ }
+ else if (ch == '\b' || ch == attr->c_cc[VERASE])
+ {
+ /* Delete */
+ *code = TERMINAL_DEL;
+ if (can_erase)
+ {
+ apr_file_putc('\b', terminal->outfd);
+ apr_file_putc(' ', terminal->outfd);
+ apr_file_putc('\b', terminal->outfd);
+ }
+ }
else if (ch == attr->c_cc[VEOF])
- *code = TERMINAL_EOF;
+ {
+ /* End of input */
+ *code = TERMINAL_EOF;
+ echo_control_char(ch, terminal->outfd);
+ }
+ else if (ch == attr->c_cc[VSUSP])
+ {
+ /* Suspend */
+ *code = TERMINAL_NONE;
+ kill(0, SIGTSTP);
+ }
else if (!apr_iscntrl(ch))
- *code = (int)(unsigned char)ch;
+ {
+ /* Normal character */
+ *code = (int)(unsigned char)ch;
+ apr_file_putc((echo ? ch : '*'), terminal->outfd);
+ }
else
{
+ /* Ignored character */
*code = TERMINAL_NONE;
apr_file_putc('\a', terminal->outfd);
}